summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--2d23d.html15
-rw-r--r--2d23dcustom.html18
-rw-r--r--2pi.html22
-rw-r--r--AutoAudio.html67
-rw-r--r--AutoHarmonograph.html20
-rw-r--r--AutoImages.html71
-rw-r--r--AutoVideos.html75
-rw-r--r--Harmonograph.html22
-rw-r--r--LICENSE.txt674
-rw-r--r--NameGenerator.html49
-rw-r--r--README.md2
-rw-r--r--all.html210
-rw-r--r--ant.html21
-rw-r--r--apps.html35
-rw-r--r--autoaudio.wavbin0 -> 50044 bytes
-rw-r--r--autovideo.mp4bin0 -> 6689449 bytes
-rw-r--r--ballbounce.html18
-rw-r--r--clock.html16
-rw-r--r--clock.pngbin0 -> 42204 bytes
-rw-r--r--css/bootstrap.min.css6
-rw-r--r--css/style.css24
-rw-r--r--explanation.html69
-rw-r--r--favicon.pngbin0 -> 2865 bytes
-rw-r--r--games.html50
-rw-r--r--h.html48
-rw-r--r--hfavicon.pngbin0 -> 516 bytes
-rw-r--r--index.html35
-rw-r--r--js/2d23d.js83
-rw-r--r--js/2d23dcustom.js90
-rw-r--r--js/2pi.js100
-rw-r--r--js/AutoAudio.js213
-rw-r--r--js/AutoHarmonograph.js107
-rw-r--r--js/AutoImages.js240
-rw-r--r--js/AutoVideos.js265
-rw-r--r--js/Harmonograph.js206
-rw-r--r--js/NameGenerator.js149
-rw-r--r--js/ant.js135
-rw-r--r--js/ballbounce.js54
-rw-r--r--js/clock.js116
-rw-r--r--js/h.js203
-rw-r--r--js/header_links.js5
-rw-r--r--js/latexit.js110
-rw-r--r--js/magnets.js83
-rw-r--r--js/magnets_old_version.js83
-rw-r--r--js/mandelbrot.js179
-rw-r--r--js/mazesolver.js457
-rw-r--r--js/modularcircles.js54
-rw-r--r--js/modularpascal.js55
-rw-r--r--js/p5.js29765
-rw-r--r--js/processing.js21748
-rw-r--r--js/riffwave.js130
-rw-r--r--js/shaperoller.js172
-rw-r--r--js/tree.js24
-rw-r--r--js/treegenerator.js37
-rw-r--r--magnets.html17
-rw-r--r--magnets_old_version.html15
-rw-r--r--mandelbrot.html20
-rw-r--r--mandelbrot_explanation.html72
-rw-r--r--mathematical.html120
-rw-r--r--mazesolver.html39
-rw-r--r--modularcircles.html22
-rw-r--r--modularpascal.html22
-rw-r--r--screenshots/2d3d.pngbin0 -> 94823 bytes
-rw-r--r--screenshots/2d3dcustom.pngbin0 -> 50734 bytes
-rw-r--r--screenshots/AutoHarmonograph.pngbin0 -> 97985 bytes
-rw-r--r--screenshots/AutoImages.pngbin0 -> 64574 bytes
-rw-r--r--screenshots/NameGenerator.pngbin0 -> 10428 bytes
-rw-r--r--screenshots/ant.pngbin0 -> 3195 bytes
-rw-r--r--screenshots/autoartapp.pngbin0 -> 189554 bytes
-rw-r--r--screenshots/ballbounce.pngbin0 -> 12007 bytes
-rw-r--r--screenshots/ballbounceapp.pngbin0 -> 50364 bytes
-rw-r--r--screenshots/clock.pngbin0 -> 42256 bytes
-rw-r--r--screenshots/h.pngbin0 -> 26255 bytes
-rw-r--r--screenshots/magnets.pngbin0 -> 15253 bytes
-rw-r--r--screenshots/magnetsapp.pngbin0 -> 35036 bytes
-rw-r--r--screenshots/mandelbrot.pngbin0 -> 154914 bytes
-rw-r--r--screenshots/mazesolver.pngbin0 -> 3874 bytes
-rw-r--r--screenshots/modularcircles.pngbin0 -> 461582 bytes
-rw-r--r--screenshots/modularpascal.pngbin0 -> 79002 bytes
-rw-r--r--screenshots/picalculator.pngbin0 -> 28946 bytes
-rw-r--r--screenshots/shaperoller.pngbin0 -> 27866 bytes
-rw-r--r--screenshots/tree.pngbin0 -> 34388 bytes
-rw-r--r--shaperoller.html55
-rw-r--r--tree.html15
-rw-r--r--treegenerator.html25
85 files changed, 56852 insertions, 0 deletions
diff --git a/2d23d.html b/2d23d.html
new file mode 100644
index 0000000..7f7b1b9
--- /dev/null
+++ b/2d23d.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+<title>2D becomes 3D</title>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+</head>
+<body>
+<h2>2D becomes 3D</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+<script src="js/p5.js"></script>
+<script src="js/2d23d.js"></script>
+</body>
+</html> \ No newline at end of file
diff --git a/2d23dcustom.html b/2d23dcustom.html
new file mode 100644
index 0000000..341f472
--- /dev/null
+++ b/2d23dcustom.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+<title>2D becomes 3D</title>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+</head>
+<body>
+<h2>2D becomes 3D</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+<p>Draw a pattern by dragging your mouse around. Press enter when you're done.<br>
+To draw a straight line, click the starting location, press the shift key,
+and click the ending location.</p>
+<script src="js/p5.js"></script>
+<script src="js/2d23dcustom.js"></script>
+</body>
+</html> \ No newline at end of file
diff --git a/2pi.html b/2pi.html
new file mode 100644
index 0000000..35f2307
--- /dev/null
+++ b/2pi.html
@@ -0,0 +1,22 @@
+<html>
+<head>
+<title>π calculator</title>
+
+<meta charset="utf-8">
+
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+<script src="js/p5.js"></script>
+
+</head>
+<body>
+<h2>π calculator</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+<p>Since 2π day is today (6/28), here's a π/2π calculator. Draw a circle, then find out how well your circle approximates π!<br>
+<button onclick="calculate();">Calculate π</button><br>
+<div id="info"></div>
+<script src="js/2pi.js"></script>
+
+</html> \ No newline at end of file
diff --git a/AutoAudio.html b/AutoAudio.html
new file mode 100644
index 0000000..bffb2da
--- /dev/null
+++ b/AutoAudio.html
@@ -0,0 +1,67 @@
+<html>
+
+
+<head>
+<script src="js/latexit.js"></script>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+<link rel="shortcut icon" type="image/png" href="favicon.png">
+<title>AutoAudio</title>
+</head>
+
+<body>
+
+<h2>AutoAudio</h2>
+<a class="header_link" href="index.html">Home</a>
+<a class="header_link" href="all.html">All</a>
+<a class="header_link" href="mathematical.html">Mathematical Demonstrations</a>
+<a class="header_link" href="games.html">Games</a>
+<a class="header_link" href="apps.html">Android Apps</a>
+<hr>
+
+<p>AutoAudio is a program that creates computer generated audio.<br>
+For an explanation of AutoAudio, go <a href="explanation.html">here</a> <br>
+After you click "Create", please wait until the audio finishes before listening to it.
+</p>
+
+<p id='Error'></p>
+
+
+<script src='js/riffwave.js'></script>
+
+<form id='Options'>
+<fieldset>
+<legend>Options</legend>
+Length: <input type="number" name="leninput" value="5"><br>
+<input type='checkbox'> Notify me when it finishes.
+</fieldset>
+<fieldset>
+<legend>Advanced Options</legend>
+Single operator weight: <input type='number' value='1'><br>
+Number weight: <input type='number' value='1'><br>
+Function length: <input type='number' value='60'><br>
+</fieldset>
+</form>
+
+<br>
+<button onclick="create();" type='button' class="btn btn-success">Create</button>
+<br><br>
+
+<audio id='Audio' controls></audio>
+
+<br><br>
+
+<p id='Time'></p>
+
+<div id='Function' lang='latex'></div>
+
+<script src="js/AutoAudio.js"></script>
+<noscript>
+<h1>This web application requires javascript.</h1>
+</noscript>
+
+</body>
+<footer>
+<p>AutoArt is licensed under the <a href='LICENSE.txt'>GNU General public license</a>.</p>
+</footer>
+</html>
diff --git a/AutoHarmonograph.html b/AutoHarmonograph.html
new file mode 100644
index 0000000..bebf100
--- /dev/null
+++ b/AutoHarmonograph.html
@@ -0,0 +1,20 @@
+<html>
+<head>
+<title>AutoHarmonograph</title>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+</head>
+<body>
+<h2>AutoHarmonograph</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+
+Number of pendulums: <input type="number" id="npendulums"><br>
+Maximum damping: <input type="number" id="DMAX"><br>
+<button onclick="start();">Start</button><br>
+<button onclick="saveCanvas();">Save image</button><br>
+<script src="js/p5.js"></script>
+<script src="js/AutoHarmonograph.js"></script>
+</body>
+</html> \ No newline at end of file
diff --git a/AutoImages.html b/AutoImages.html
new file mode 100644
index 0000000..dfc6325
--- /dev/null
+++ b/AutoImages.html
@@ -0,0 +1,71 @@
+<html>
+
+<head>
+<script src="js/latexit.js"></script>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+<link rel="shortcut icon" type="image/png" href="favicon.png">
+<title>AutoImages</title>
+</head>
+
+<body>
+
+<h2>AutoImages</h2>
+<a class="header_link" href="index.html">Home</a>
+<a class="header_link" href="all.html">All</a>
+<a class="header_link" href="mathematical.html">Mathematical Demonstrations</a>
+<a class="header_link" href="games.html">Games</a>
+<a class="header_link" href="apps.html">Android Apps</a>
+<hr>
+<p>AutoImages is a program that creates computer generated images.<br>
+For an explanation of AutoImages, go <a href="explanation.html">here</a><br>
+First click the create button then the download button.<br>
+If the download button doesn't work, just right click on the image then click "Save image as...".
+Change X and Y to change the resolution of the image.<br>
+After you click create, wait until the image shows, then click "Download".</p>
+
+<br>
+<button onclick="create();" type='button' class="btn btn-success">Create</button>
+<br><br>
+
+<form id='Options'>
+<fieldset>
+<legend>Options</legend>
+X: <input type="number" name="xinput" value="1000">
+<br>
+Y: <input type="number" name="yinput" value="500"><br>
+<input type='checkbox'> Notify me when it finishes.
+</fieldset>
+<fieldset>
+<legend>Advanced Options</legend>
+Single operator weight: <input type='number' value='1'><br>
+Number weight: <input type='number' value='1'><br>
+Function length: <input type='number' value='60'><br>
+</fieldset>
+</form>
+
+<p id='Error'></p>
+
+<p id='Time'></p>
+
+<a id='Download' href="" download='AutoImage.png'></a>
+
+<br>
+
+<canvas id='Canvas' width=0 height=0></canvas>
+
+<br><br>
+
+<div id='Function' lang='latex'></div>
+
+<script src="js/AutoImages.js"></script>
+
+<noscript>
+<h1>This web application requires javascript.</h1>
+</noscript>
+
+</body>
+<footer>
+<p>AutoArt is licensed under the <a href='LICENSE.txt'>GNU General public license</a>.</p>
+</footer>
+</html>
diff --git a/AutoVideos.html b/AutoVideos.html
new file mode 100644
index 0000000..0e23d65
--- /dev/null
+++ b/AutoVideos.html
@@ -0,0 +1,75 @@
+<html>
+
+
+<head>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+<script src="js/latexit.js"></script>
+<link rel="shortcut icon" type="image/png" href="favicon.png">
+<title>AutoVideos</title>
+
+</head>
+
+
+<body>
+
+<h2>AutoVideos</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+<p>AutoVideos is a program that creates computer generated videos.<br>
+For an explanation of AutoAudio, go <a href="explanation.html">here</a>
+First click the create button then the download button.<br>
+If the download button doesn't work, just right click on the image then click "Save image as...".
+Change X and Y to change the resolution of the image.<br>
+After you click create, wait until the image shows, then click "Download".</p>
+
+
+
+<form id='Options'>
+<fieldset>
+<legend>Options</legend>
+X: <input type="number" name="xinput" value="426">
+<br>
+Y: <input type="number" name="yinput" value="240">
+<br>
+Length: <input type='number' name='leninput' value='4'>
+<br>
+Frame rate: <input type='number' name='frinput' value='12'><br>
+<input type='checkbox'> Notify me when it finishes.
+</fieldset>
+<br>
+<fieldset>
+<legend>Advanced Options</legend>
+Single operation weight: <input type='number' value='1'><br>
+Number weight: <input type='number' value='1'><br>
+Function length: <input type='number' value='60'><br>
+</fieldset>
+</form>
+
+<br>
+<button onclick="create();" type='button' class="btn btn-success">Create</button>
+<br><br>
+
+<p id='Time'></p>
+
+<p id='Error'></p>
+
+<video width=0 height=0 id='Video' controls></video><br><br>
+<canvas id='Canvas' width=0 height=0></canvas>
+
+<div id='Function' lang='latex'></div>
+
+<script src="http://antimatter15.com/whammy/whammy.js"></script>
+<script src="js/AutoVideos.js"></script>
+
+<noscript>
+<h1>This web application requires javascript.</h1>
+</noscript>
+
+</body>
+<footer>
+<p>AutoArt is licensed under the <a href='LICENSE.txt'>GNU General public license</a>.</p>
+</footer>
+
+</html>
diff --git a/Harmonograph.html b/Harmonograph.html
new file mode 100644
index 0000000..ef3d398
--- /dev/null
+++ b/Harmonograph.html
@@ -0,0 +1,22 @@
+<html>
+<head>
+<title>Harmonograph</title>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+</head>
+<body>
+
+<h2>Harmonograph</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+
+<button onclick="start();">Start</button><br>
+<button onclick="saveCanvas();">Save image</button><br>
+<button onclick="addPendulum();">Add pendulum</button>
+
+<script src="js/p5.js"></script>
+<script src="js/Harmonograph.js"></script>
+
+</body>
+</html> \ No newline at end of file
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..9cecc1d
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ {one line to give the program's name and a brief idea of what it does.}
+ Copyright (C) {year} {name of author}
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ {project} Copyright (C) {year} {fullname}
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/NameGenerator.html b/NameGenerator.html
new file mode 100644
index 0000000..019a6f4
--- /dev/null
+++ b/NameGenerator.html
@@ -0,0 +1,49 @@
+<html>
+<head>
+<title>NameGenerator</title>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+<style>
+#loading
+{
+ font-size: 20px;
+}
+button:active
+{
+ background-color: #ff0000;
+ color:#ff0000;
+}
+</style>
+
+<script src="js/NameGenerator.js"></script>
+
+</head>
+
+
+<body>
+<h2>Name Generator</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+<p>Name Generator generates names using a method created by <a href="https://en.wikipedia.org/wiki/Claude_Shannon" target="_blank">Claude Shannon</a>.
+Claude Shannon created fake English text by going to a random page in a book, and writing down the first letter on the page.
+Let's say it was "h". Then, he would flip to another random page in the book, and find the first occurrence of the letter "h". Let's say
+it was in the word "had". He would then write down the letter that followed the "h", in this case "a", then flip to another page in the book,
+and find that the first occurrence of "a" was followed by "l", and so on. This program is slightly different. It takes two random (taking into
+account the frequencies of letters in English) letters. It finds what those two letters are commonly followed by. Then it takes the last two letters,
+and finds what those two letters are commonly followed by, and so on. There is also a faster offline version <a href="https://github.com/pommicket/NameGenerator" target="_blank">on GitHub</a><br><br>
+After clicking "Create Names!", the button will become red until it has finished loading. To download the names, click on the download link, then press Ctrl+S/Command+S.</p>
+
+Number of names: <input type="number" id="numNames" value=10><br>
+Display names at bottom <input id="outputNames" type="checkbox" checked=true> (Unchecking this option will mean that you will have to download the names to see them)<br><br>
+<button id="button" onclick="createNames();">Create Names!</button><br>
+<a id="download" target="_blank"></a>
+
+<div id="names">
+</div>
+
+<p id="loading">Loading...</p>
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..4b622c5
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+# AutoArt-Website
+http://autoart.neocities.org
diff --git a/all.html b/all.html
new file mode 100644
index 0000000..2e082c7
--- /dev/null
+++ b/all.html
@@ -0,0 +1,210 @@
+<html>
+<head>
+<title>All</title>
+<meta charset="utf-8">
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+<link rel="shortcut icon" type="image/png" href="favicon.png">
+</head>
+
+<body>
+<h2>All projects</h2>
+<a class="header_link" href="index.html">Home</a>
+<a class="header_link" href="mathematical.html">Mathematical Demonstrations</a>
+<a class="header_link" href="games.html">Games</a>
+<a class="header_link" href="apps.html">Android Apps</a>
+<hr>
+
+<img src="screenshots/AutoImages.png" width=200 height=200><br>
+<a href="AutoImages.html">AutoImages</a><br>
+<p>
+A computer program that creates art. It uses<br>
+random functions to generate images. An explanation<br>
+can be found <a href="explanation.html">here</a>.<br>
+There is also AutoArtEvolve, which improves its<br>
+images based on the ones you prefer:<br>
+<iframe frameborder="0" src="https://itch.io/embed/43294?linkback=true&amp;bg_color=fafafa&amp;fg_color=000000&amp;link_color=5ca4fa&amp;border_color=9f9f9f" width="552" height="167"></iframe>
+</p>
+<hr>
+
+<video width="250" height="250" controls>
+ <source src="autovideo.mp4" type="video/mp4">
+</video><br>
+<a href="AutoVideos.html">AutoVideos</a><br>
+<p>
+A computer program that creates videos. It uses<br>
+random functions to generate videos. An explanation<br>
+can be found <a href="explanation.html">here</a>.<br>
+</p>
+<hr>
+
+<audio controls>
+ <source src="autoaudio.wav" type="audio/wav">
+</audio><br>
+<a href="AutoAudio.html">AutoAudio</a><br>
+<p>
+A computer program that creates audio. It uses<br>
+random functions to generate audio. An explanation<br>
+can be found <a href="explanation.html">here</a>.<br>
+</p>
+<hr>
+
+
+<img src="screenshots/NameGenerator.png" width=200 height=200><br>
+<a href="NameGenerator.html">NameGenerator</a><br>
+<p>
+A computer program that generates names! It uses<br>
+the frequencies of trigrams.<br>
+<hr>
+
+<img src="screenshots/AutoHarmonograph.png" width=200 height=200><br>
+<a href="AutoHarmonograph.html">AutoHarmonograph</a> or <a href="Harmonograph.html">Non-randomized</a><br>
+<p>
+A randomized <a href="https://en.wikipedia.org/wiki/Harmonograph">Harmonograph</a>. You pick the
+number of pendulums.
+<hr>
+
+<img src="screenshots/2d3d.png" width=200 height=200><img src="screenshots/2d3dcustom.png" width=200 height=200><br>
+<a href="2d23d.html">2D becomes 3D</a> (and <a href="2d23dcustom.html">2D becomes 3D custom</a>
+where you pick the starting shape)<br>
+<p>
+Different 2D shapes are moved around in circles<br>
+to form 3D objects.
+</p>
+<hr>
+
+<img src="screenshots/clock.png" width=200 height=200><br>
+<a href="clock.html">Clock</a><br>
+<p>
+A clock that shows the time of day with circles<br>
+going around in circles. The colours of the circles<br>
+change depending on the time. The innermost circle<br>
+is seconds, the second is minutes, the third is<br>
+hours, and the fourth is the day of the year.</p>
+<hr>
+
+<img src="screenshots/mazesolver.png" width=200 height=200><br>
+<a href="mazesolver.html">Maze Solver</a><br>
+<p>
+Create a maze, choose a starting point and an<br>
+ending point, and your computer will solve the<br>
+maze.
+</p>
+<hr>
+
+<img src="screenshots/h.png" width=200 height=200><br>
+<a href="h.html">H</a><br>
+<p>
+Pick simple rules, and watch a cool pattern<br>
+start to appear.
+</p>
+<hr>
+<img src="screenshots/tree.png" width=200 height=200><br>
+<a href="tree.html">Tree</a> and <a href="treegenerator.html">Tree generator</a>
+<p>
+Move your mouse around to see a tree change shape.<br>
+In Tree Generator, you choose all the tree parameters<br>
+but in Tree, you move your mouse around to pick the<br> parameters.<br>
+<hr>
+
+<img src="screenshots/ballbounce.png" width=200 height=200><br>
+<a href="ballbounce.html">Ball bounce</a><br>
+<p>
+Try to bounce a ball with your mouse. The game<br>
+gets harder and harder because you can't see the<br>
+ball behind its tracks.<br>
+</p>
+<hr>
+
+<img src="screenshots/magnets.png" width=200 height=200><br>
+<a href="magnets.html">Magnets</a><br>
+<p>
+Try to avoid magnets which are attracted to your<br>
+mouse.
+</p>
+<hr>
+
+<img src="screenshots/modularpascal.png" width=256 height=256><br>
+<a href="modularpascal.html">Modular Pascal's Triangle</a><br>
+<p>
+A picture of Pascal's triangle mod x. Set x and<br>
+Click update to see the triangle. </p>
+<hr>
+
+<img src="screenshots/modularcircles.png" width=250 height=250><br>
+<a href="modularcircles.html">Modular Circles</a><br>
+<p>
+There are a circle of points, each one corresponding<br>
+to a number. Lines are drawn between points according<br>
+to rules. Create interesting shapes like cardioids<br>
+</p>
+<hr>
+
+<img src="screenshots/shaperoller.png" width=250 height=250><br>
+<a href="shaperoller.html">Shape Roller</a><br>
+<p>
+This program rolls (invisible) shapes around other <br>
+(invisible) shapes and traces a point on the shape as<br>
+it moves along. Create interesting shapes like cardioids.<br>
+</p>
+<hr>
+
+<img src="screenshots/mandelbrot.png" width=250 height=250><br>
+<a href="mandelbrot.html">The Mandelbrot Set</a><br>
+<p>
+This program draws the Mandelbrot Set. You can<br>
+zoom in and change the power.<br>
+</p>
+<hr>
+
+<img src="screenshots/ant.png" width=250 height=250><br>
+<a href="ant.html">Langton's Ant</a><br>
+<p>
+This is the cellular automaton known as Langton's<br>
+Ant. You can find out more about it <a href="https://en.wikipedia.org/wiki/Langton%27s_ant">here</a>.
+</p>
+<hr>
+
+<img src="screenshots/picalculator.png" width=250 height=250><br>
+<a href="2pi.html">π Calculator</a><br>
+<p>
+Draw a circle, then find out how well your circle<br>
+approximates π!<br>
+</p>
+<hr>
+
+<img src="screenshots/autoartapp.png" width=200><br>
+<a href="https://play.google.com/store/apps/details?id=org.neocities.autoart.autoart">AutoArt</a><br>
+The Android app for <a href="AutoImages.html">AutoImages</a>.<br>
+<hr>
+
+<img src="screenshots/magnetsapp.png" width=200><br>
+<a href="https://play.google.com/store/apps/details?id=org.neocities.autoart.magnets">Magnets</a><br>
+<p>
+The Android app for <a href="magnets.html">Magnets</a>.<br>
+</p>
+<hr>
+
+<img src="screenshots/ballbounceapp.png" width=200><br>
+<a href="https://play.google.com/store/apps/details?id=org.neocities.autoart.ballbounce">Ball Bounce</a><br>
+<p>
+The Android app for <a href="ballbounce.html">Ball Bounce</a>.<br>
+</p>
+<hr>
+
+<a href="http://github.com/pommicket/NameGeneratorEvolve">NameGeneratorEvolve</a>
+<p>
+Like NameGenerator, except it learns what names<br>
+you like.
+</p>
+<hr>
+<p>
+More coming soon!
+</p>
+
+</body>
+<footer>
+<p>This website is licensed under the <a href='LICENSE.txt'>GNU General public license</a>. All of the source code can be found <a href="http://github.com/pommicket/pommicket.github.io">here</a></p>
+</footer>
+</footer>
+</html>
diff --git a/ant.html b/ant.html
new file mode 100644
index 0000000..36c5bb5
--- /dev/null
+++ b/ant.html
@@ -0,0 +1,21 @@
+<html>
+<head>
+<title>Langton's Ant</title>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+<script src="js/p5.js"></script>
+</head>
+<body>
+<h2>Langton's Ant</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+
+<button onclick="start();">Start</button><br>
+<button onclick="stop();">Stop</button><br>
+<button onclick="clearCells();">Clear</button><br>
+<button onclick="resetPos();">Reset ant position</button><br>
+<div>Speed: <input type="number" id="speed" value=1></div>
+
+<script src="js/ant.js"></script>
+</html> \ No newline at end of file
diff --git a/apps.html b/apps.html
new file mode 100644
index 0000000..af2fe01
--- /dev/null
+++ b/apps.html
@@ -0,0 +1,35 @@
+<html>
+<head>
+<title>Android Apps</title>
+<meta charset="utf-8">
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+<link rel="shortcut icon" type="image/png" href="favicon.png">
+</head>
+
+<body>
+<h2>Android Apps</h2>
+<a class="header_link" href="index.html">Home</a>
+<a class="header_link" href="all.html">All</a>
+<a class="header_link" href="mathematical.html">Mathematical Demonstrations</a>
+<a class="header_link" href="games.html">Games</a>
+<hr>
+<img src="screenshots/autoartapp.png" width=200><br>
+<a href="https://play.google.com/store/apps/details?id=org.neocities.autoart.autoart">AutoArt</a><br>
+The Android app for <a href="AutoImages.html">AutoImages</a>.<br>
+<hr>
+
+<img src="screenshots/magnetsapp.png" width=200><br>
+<a href="https://play.google.com/store/apps/details?id=org.neocities.autoart.magnets">Magnets</a><br>
+<p>
+The Android app for <a href="magnets.html">Magnets</a>.<br>
+</p>
+<hr>
+
+<img src="screenshots/ballbounceapp.png" width=200><br>
+<a href="https://play.google.com/store/apps/details?id=org.neocities.autoart.ballbounce">Ball Bounce</a><br>
+<p>
+The Android app for <a href="ballbounce.html">Ball Bounce</a>.<br>
+</p>
+
+</body> \ No newline at end of file
diff --git a/autoaudio.wav b/autoaudio.wav
new file mode 100644
index 0000000..25ea094
--- /dev/null
+++ b/autoaudio.wav
Binary files differ
diff --git a/autovideo.mp4 b/autovideo.mp4
new file mode 100644
index 0000000..98a5a0d
--- /dev/null
+++ b/autovideo.mp4
Binary files differ
diff --git a/ballbounce.html b/ballbounce.html
new file mode 100644
index 0000000..0bbdf01
--- /dev/null
+++ b/ballbounce.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+<title>Bounce the ball</title>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+
+</head>
+<body>
+<h2>Ball Bounce</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+<p>You can get the android app for Ball Bounce
+ <a href="https://play.google.com/store/apps/details?id=org.neocities.autoart.ballbounce">here</a>.</p>
+<script src="js/p5.js"></script>
+<script src="js/ballbounce.js"></script>
+</body>
+</html> \ No newline at end of file
diff --git a/clock.html b/clock.html
new file mode 100644
index 0000000..1fe919f
--- /dev/null
+++ b/clock.html
@@ -0,0 +1,16 @@
+<html>
+<head>
+<title>Clock</title>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+<link rel="shortcut icon" type="image/png" href="clock.png">
+</head>
+<body>
+<h2>Clock</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+<script src="js/p5.js"></script>
+<script src="js/clock.js"></script>
+</body>
+</html> \ No newline at end of file
diff --git a/clock.png b/clock.png
new file mode 100644
index 0000000..896169f
--- /dev/null
+++ b/clock.png
Binary files differ
diff --git a/css/bootstrap.min.css b/css/bootstrap.min.css
new file mode 100644
index 0000000..4cf729e
--- /dev/null
+++ b/css/bootstrap.min.css
@@ -0,0 +1,6 @@
+/*!
+ * Bootstrap v3.3.6 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}
+/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file
diff --git a/css/style.css b/css/style.css
new file mode 100644
index 0000000..047f5c9
--- /dev/null
+++ b/css/style.css
@@ -0,0 +1,24 @@
+footer
+{
+ font-size:12px;
+}
+.header_link
+{
+ font-size: 16px;
+ padding-left: 10px;
+ padding-right: 10px;
+ padding-top: 5px;
+ padding-bottom: 5px;
+
+ background: #f9f9f9;
+}
+.header_link:hover
+{
+ text-decoration: none;
+ background: #eeeeee;
+}
+
+body
+{
+ margin: 10px;
+} \ No newline at end of file
diff --git a/explanation.html b/explanation.html
new file mode 100644
index 0000000..805192f
--- /dev/null
+++ b/explanation.html
@@ -0,0 +1,69 @@
+<html>
+<head>
+<script src="js/latexit.js"></script>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+
+<title>AutoArt Explanation</title>
+</head>
+
+<body>
+
+<h2>Explanation of AutoArt</h2>
+<a class="header_link" href="index.html">Home</a>
+<a class="header_link" href="all.html">All</a>
+<a class="header_link" href="mathematical.html">Mathematical Demonstrations</a>
+<a class="header_link" href="games.html">Games</a>
+<a class="header_link" href="apps.html">Android Apps</a>
+<hr>
+
+<h4><a href="AutoImages.html">AutoImages:</a></h4>
+<p>AutoImages works by creating random functions using Markov Chains. Every image on your computer is made up of many very small squares called "pixels". Each pixel has a red, green
+and blue value. If a pixel has a red of 255, a green of 0, and a blue of 0, the pixel will be red. AutoImages creates 3 functions, the red function, green function, and blue function.
+Each function takes in the x and y position of each pixel (where the pixel is), and returns the red, green, or blue value for that pixel.
+
+<div lang="latex">
+\\
+R(x, y) =$ the red value of the pixel at position $(x, y)\\
+G(x, y) =$ the green value of the pixel at position $(x, y)\\
+B(x, y) =$ the blue value of the pixel at position $(x, y)\\
+$Where $R, G,$ and $B$ are created randomly.
+</div>
+
+<p>It uses the random functions to calculate the colour of each pixel in the image.</p>
+
+<h4><a href="AutoVideos.html">AutoVideos:</a></h4>
+<p>
+AutoImages works by creating random functions using Markov Chains. Every video on your computer is made up of many images called "frames". When you play a video, you are just
+seeing a series of images played very quickly (at 24 images per second). Each image is made up of pixels, and each pixel has a red, green, and blue value. AutoVideos' functions
+given the colour of each pixel given its x and y positions, and its frame number (the first frame is frame #0, the second is frame #1 and so on).
+</p>
+<div lang="latex">
+\\
+t =$ Frame number$\\
+R(x, y, t) =$ the red value of the pixel at position $(x, y)$ in frame number $t\\
+G(x, y, t) =$ the green value of the pixel at position $(x, y)$ in frame number $t\\
+B(x, y, t) =$ the blue value of the pixel at position $(x, y)$ in frame number $t\\
+$Where $R, G,$ and $B$ are created randomly.
+
+</div>
+
+<h4><a href="AutoAudio.html">AutoAudio:</a></h4>
+<p>
+All audio is stored as a series of samples. Each sample has a y position. For example the function</p>
+<div lang="latex">
+\\
+y(t) = sin(880 \pi t)\\
+$If $t$ is the time at which the sample is played
+</div>
+<p> sounds like an A
+on a piano. AutoAudio creates a random function, then plays it.
+</p>
+<div lang="latex">
+S(t) =$ the $y$ position of sample at time $t
+</div>
+
+
+</body>
+
+</html>
diff --git a/favicon.png b/favicon.png
new file mode 100644
index 0000000..72aed42
--- /dev/null
+++ b/favicon.png
Binary files differ
diff --git a/games.html b/games.html
new file mode 100644
index 0000000..7d25126
--- /dev/null
+++ b/games.html
@@ -0,0 +1,50 @@
+<html>
+<head>
+<title>Games</title>
+<meta charset="utf-8">
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+<link rel="shortcut icon" type="image/png" href="favicon.png">
+<style>
+body
+{
+ margin-left: 10px;
+}
+</style>
+</head>
+
+<body>
+<h2>Games</h2>
+<a class="header_link" href="index.html">Home</a>
+<a class="header_link" href="all.html">All</a>
+<a class="header_link" href="mathematical.html">Mathematical Demonstrations</a>
+<a class="header_link" href="apps.html">Android Apps</a>
+<hr>
+
+
+<img src="screenshots/ballbounce.png" width=200 height=200><br>
+<a href="ballbounce.html">Ball bounce</a><br>
+<p>
+Try to bounce a ball with your mouse. The game<br>
+gets harder and harder because you can't see the<br>
+ball behind its tracks.<br>
+</p>
+<hr>
+
+<img src="screenshots/magnets.png" width=200 height=200><br>
+<a href="magnets.html">Magnets</a><br>
+<p>
+Try to avoid magnets which are attracted to your<br>
+mouse.
+</p>
+<hr>
+
+<img src="screenshots/picalculator.png" width=250 height=250><br>
+<a href="2pi.html">π Calculator</a><br>
+<p>
+Draw a circle, then find out how well your circle<br>
+approximates π!<br>
+</p>
+
+
+</body> \ No newline at end of file
diff --git a/h.html b/h.html
new file mode 100644
index 0000000..d9e0c30
--- /dev/null
+++ b/h.html
@@ -0,0 +1,48 @@
+<html>
+<head>
+<link rel='stylesheet' href='css/bootstrap.min.css'>
+<link rel='stylesheet' href='css/style.css'>
+<link rel="shortcut icon" type="image/png" href="hfavicon.png">
+<title>H</title>
+</head>
+
+<body>
+<h2 style='font-family: Times New Roman'>H</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+
+<script src='js/h.js'></script>
+
+<p>Line lengths controls the size of the Hs, width and height control
+how big the H can get, and wait between iterations changes how quickly the
+Hs are generated. Click begin to start, and once you think it has
+grown enough, click stop. You can then download the H or continue to make
+more Hs by clicking begin again.</p>
+
+Line lengths: <input type='number' id='lineLength' value=6><br>
+Width: <input type='number' id='w' value=500><br>
+Height: <input type='number' id='h' value=500><br>
+Wait between iterations (ms): <input type='number' id='delay' value=1000>
+<br>
+Chance that a new branch will form:
+<input type='number' min=0 max=1 id='chance' value=1 step=0.1>
+<br>
+
+
+<button onclick='beginH();'>Begin</button><br>
+<button onclick='stop();'>Stop</button><br>
+
+<canvas width=100 height=100 id='canvas'></canvas>
+
+<footer>
+<p class='footer'>This program is licensed under the
+<a href='http://autoart.neocities.org/LICENSE.txt'>GNU General public license</a>
+</p>
+</footer>
+
+</body>
+
+
+
+</html>
diff --git a/hfavicon.png b/hfavicon.png
new file mode 100644
index 0000000..0d89a5c
--- /dev/null
+++ b/hfavicon.png
Binary files differ
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..850561e
--- /dev/null
+++ b/index.html
@@ -0,0 +1,35 @@
+<html>
+
+ <head>
+ <title>AutoArt</title>
+ <link rel="stylesheet" href="css/bootstrap.min.css">
+ <link rel="stylesheet" href="css/style.css">
+ <link rel="shortcut icon" type="image/png" href="favicon.png">
+
+ </head>
+ <body>
+ <h2>AutoArt</h2>
+ <a class="header_link" href="all.html">All</a>
+ <a class="header_link" href="mathematical.html">Mathematical Demonstrations</a>
+ <a class="header_link" href="games.html">Games</a>
+ <a class="header_link" href="apps.html">Android Apps</a>
+
+ <hr>
+ <p>
+ AutoArt is a website with mathematical demonstrations, games, and more.<br>
+ The main projects on the website are:
+
+ <ul>
+ <li><a href="AutoImages.html">Auto Images</a></li>
+ <li><a href="AutoVideos.html">Auto Videos</a></li>
+ <li><a href="AutoAudio.html">Auto Audio</a></li>
+ </ul>
+ </p>
+ <p>Image from <a href='AutoImages.html'>AutoImages</a>:<br></p>
+ <img src="screenshots/AutoImages.png" title="Example of AutoImages" style="width:500px;height:250px">
+ </body>
+ <footer>
+<p>This website is licensed under the <a href='LICENSE.txt'>GNU General public license</a>. All of the source code can be found <a href="http://github.com/pommicket/pommicket.github.io">here</a></p>
+</footer>
+
+</html>
diff --git a/js/2d23d.js b/js/2d23d.js
new file mode 100644
index 0000000..c3ac565
--- /dev/null
+++ b/js/2d23d.js
@@ -0,0 +1,83 @@
+var angle = 0;
+var state = 0;
+var rotations = 0;
+var waiting = false;
+var iterations = 0;
+var radius = 200;
+
+function checkRotations()
+{
+ if (rotations > 0)
+ {
+ rotations = 0;
+ angle = 0;
+ waiting = true;
+ }
+}
+
+function setup()
+{
+ createCanvas(2 * radius + 100, 2 * radius + 100);
+ ellipseMode(CENTER);
+ rectMode(CENTER);
+ noFill();
+ frameRate(100);
+}
+
+function draw()
+{
+ if (waiting)
+ {
+ iterations++;
+
+ if (iterations > 150)
+ {
+ iterations = 0;
+ waiting = false;
+ background(255, 255, 255);
+ state++;
+ }
+ return;
+ }
+ var ca = cos(angle) * radius + radius + 50;
+ var sa = sin(angle) * radius + radius + 50;
+
+ angle += 0.1/6;
+
+ if (angle > TWO_PI)
+ rotations++;
+
+ checkRotations();
+
+ angle %= TWO_PI;
+
+ if (state == 0)
+ ellipse(ca, sa, 50, 50);
+
+ else if (state == 1)
+ rect(ca, sa, 50, 50);
+
+ else if (state == 2)
+ triangle(ca, sa, ca+50, sa, ca, sa+50);
+
+
+ else if (state == 3)
+ triangle(ca, sa, ca+25, sa+25, ca, sa+50);
+
+
+ else if (state == 4)
+ ellipse(ca, sa, 100, 50);
+
+
+ else if (state == 5)
+ line(ca, sa, ca+50, sa);
+
+
+ else if (state == 6)
+ line(ca, sa, ca, sa+50);
+
+ else if (state >= 7)
+ arc(ca, sa, 50, 50, 0, (HALF_PI + state) % TWO_PI);
+
+
+} \ No newline at end of file
diff --git a/js/2d23dcustom.js b/js/2d23dcustom.js
new file mode 100644
index 0000000..3b55fd9
--- /dev/null
+++ b/js/2d23dcustom.js
@@ -0,0 +1,90 @@
+var RETURN = 13;
+var SHIFT = 16;
+var ret_pressed = false;
+var drawing;
+var radius = 200;
+var angle = 0;
+var done = false;
+var straightLine = false;
+var slFirstPos = [];
+
+function setup()
+{
+ createCanvas(100, 100);
+ drawing = createGraphics(100, 100);
+ stroke(255, 0, 0);
+ line(0, 0, 99, 0);
+ line(0, 0, 0, 99);
+ line(0, 99, 99, 99);
+ line(99, 0, 99, 99);
+ stroke(0);
+}
+
+function outOfBounds(x)
+{
+ return constrain(x, 0, 100) != x;
+}
+
+function mouseDragged()
+{
+ if (outOfBounds(pmouseX) || outOfBounds(mouseX) ||
+ outOfBounds(pmouseY) || outOfBounds(mouseY))
+ return;
+ stroke(0, 0, 0);
+ drawing.line(pmouseX, pmouseY, mouseX, mouseY);
+ line(pmouseX, pmouseY, mouseX, mouseY);
+}
+
+function mouseClicked()
+{
+
+ if (straightLine)
+ {
+ line(slFirstPos[0], slFirstPos[1], mouseX, mouseY);
+ drawing.line(slFirstPos[0], slFirstPos[1], mouseX, mouseY);
+ straightLine = false;
+ return;
+ }
+ slFirstPos = [mouseX, mouseY];
+}
+
+function keyPressed()
+{
+ if (keyCode == RETURN)
+ {
+ if (straightLine)
+ return;
+ if (ret_pressed)
+ return;
+ ret_pressed = true;
+ createCanvas(600, 600);
+ }
+ else if (keyCode == SHIFT)
+ {
+ straightLine = true;
+ }
+}
+
+function draw()
+{
+ cursor(ARROW);
+ if (!ret_pressed)
+ return;
+
+ if (done)
+ return;
+
+ if (angle > TWO_PI)
+ return;
+
+ stroke(255, 0, 0);
+ tint(255, 0, 0);
+ var ca = cos(angle) * radius + radius + 50;
+ var sa = sin(angle) * radius + radius + 50;
+
+
+ image(drawing, ca, sa);
+
+ angle += 0.1/6;
+
+} \ No newline at end of file
diff --git a/js/2pi.js b/js/2pi.js
new file mode 100644
index 0000000..54d59d7
--- /dev/null
+++ b/js/2pi.js
@@ -0,0 +1,100 @@
+var vertices = [];
+
+function mouseDragged()
+{
+ if (document.getElementById("info").innerHTML != "")
+ return;
+ if (mouseX < 0 || mouseX > width || mouseY < 0 || mouseY > width)
+ return;
+ stroke(0);
+ vertices.push([mouseX, mouseY]);
+ if (vertices.length > 1)
+ line(mouseX, mouseY, vertices[vertices.length-2][0], vertices[vertices.length-2][1]);
+}
+
+function circumference()
+{
+ var total = 0;
+ for (var i = 1; i < vertices.length; i++)
+ {
+ total += dist(vertices[i-1][0], vertices[i-1][1], vertices[i][0], vertices[i][1]);
+ }
+ return total;
+}
+
+function center()
+{
+ var centerX = 0;
+ var centerY = 0;
+ for (var i = 0; i < vertices.length; i++)
+ {
+ centerX += vertices[i][0] / vertices.length;
+ centerY += vertices[i][1] / vertices.length;
+ }
+ return [centerX, centerY];
+}
+
+function twoPiApprox()
+{
+ var o = center();
+ var c = circumference();
+
+ ellipseMode(CENTER);
+ fill(0, 0, 200);
+ noStroke();
+ ellipse(o[0], o[1], 5, 5);
+
+ var avgR = 0;
+ var minLocation;
+ var maxLocation;
+ for (var i = 0; i < vertices.length; i++)
+ {
+ var d = dist(vertices[i][0], vertices[i][1], o[0], o[1]);
+ avgR += d / vertices.length;
+
+ }
+
+ stroke(0, 255, 0);
+ noFill();
+ line(o[0], o[1], o[0]+avgR, o[1]);
+ stroke(255, 0, 0);
+ ellipse(o[0], o[1], 2*avgR, 2*avgR);
+ stroke(255, 200, 0);
+ ellipse(o[0], o[1], c/PI, c/PI);
+
+
+ return c/(2*avgR);
+
+}
+
+function setup()
+{
+ createCanvas(500, 500);
+ stroke(255, 0, 0);
+ line(0, 0, width-1, 0);
+ line(width-1, 0, width-1, height-1);
+ line(0, height-1, width-1, height-1);
+ line(0, 0, 0, height-1);
+}
+
+function calculate()
+{
+ var approx = twoPiApprox();
+ var avgPi = approx;
+ var percentError;
+ if (avgPi > PI)
+ {
+ percentError = 100*(avgPi/PI - 1);
+ }
+ else
+ {
+ percentError = 100*(PI/avgPi - 1);
+ }
+ var div = document.getElementById("info");
+ div.innerHTML = "Your approximation&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;True value<br> 2π = " + 2 * avgPi + " | " + TWO_PI + "<br> π = " +
+ + avgPi + " | " + PI + ".<br>" + percentError + "% error.<br>"
+ + "The blue dot is the center of your shape. The green line is the average radius of the shape.<br>"
+ + "The red circle is a circle with the same radius as your shape, and the orange circle is a<br>"
+ + "circle with the same circumference as your shape. The closer they are, the better your<br>"
+ + "approximation of π";
+} \ No newline at end of file
diff --git a/js/AutoAudio.js b/js/AutoAudio.js
new file mode 100644
index 0000000..cc6b554
--- /dev/null
+++ b/js/AutoAudio.js
@@ -0,0 +1,213 @@
+var length;
+var single = ["Math.sqrt", "Math.cos", "Math.sin"]; //Operations on a single number
+var singleweights = {};
+var binary = ["*", "+", "-", "/"]; //Operations for 2 numbers
+var binaryweights = {};
+var varlist = ["x"];
+var numlist = ["Constant"];
+numlist = numlist.concat(varlist);
+var numberweights = {"Constant":1};
+var numberweight = 1;
+var singleweight = 1;
+var eqlength;
+var notify;
+var functionp = document.getElementById('Function');
+
+for(var i = 0; i < single.length; i++)
+{
+ singleweights[single[i]] = 1;
+}
+
+for(var i = 0; i < binary.length; i++)
+{
+ binaryweights[binary[i]] = 1;
+}
+
+for(var i = 0; i < varlist.length; i++)
+{
+ numberweights[varlist[i]] = 1;
+}
+
+function randItem(l)
+{
+ return l[Math.floor(Math.random() * l.length)];
+}
+
+function countChar(string, letter)
+{
+ var amount = 0;
+ for (var i = 0; i < string.length; i++)
+ {
+ if (string[i] == letter)
+ {
+ amount++;
+ }
+ }
+ return amount;
+}
+
+function rmvmath(str)
+{
+ //A function that removes all the Math.'s in a string
+ var newstr = '';
+ for(var i = 0; i < str.length - 5; i++)
+ {
+ if(str[i] + str[i+1] + str[i+2] + str[i+3] + str[i+4] !== 'Math.')
+ {
+ newstr += str[i]
+ }
+ else
+ {
+ i += 4;
+ }
+ }
+ return newstr;
+};
+
+function randEquation()
+{
+ var hasx = false;
+ var equation;
+ var lasttype;
+ var thistype;
+ var chanceend;
+ var length;
+ var what;
+ var number;
+
+
+ while (!hasx)
+ {
+ //Types: b for binary, s for single, f for first, n for number
+ equation = '';
+ lasttype = 'f';
+ thistype = 0;
+ hasx = false;
+ chanceend = 0;
+ length = 1; //Number of operations done so far
+
+ while (true)
+ {
+ chanceend = Math.pow((1.0 - (1.0 / length)), eqlength);
+ if (lasttype == 'n')
+ {
+ number = Math.random();
+ if (number < chanceend)
+ {
+ break;
+ }
+ equation = '(' + equation + ')' + randItem(binary);
+ lasttype = 'b';
+ }
+ else if (lasttype == 's' || lasttype == 'b' || lasttype == 'f')
+ {
+ equation += '(';
+ thistype = Math.random();
+ if (thistype < singleweight / (singleweight + numberweight))
+ {
+ equation += randItem(single);
+ lasttype = 's';
+ }
+ else
+ {
+ what = randItem(numlist);
+ if (what == 'Constant')
+ {
+ equation += (Math.random()*100+100).toString();
+ }
+ else
+ {
+ equation += what;
+ if (what == 'x')
+ {
+ hasx = true;
+ }
+ }
+ lasttype = 'n';
+ equation += ')';
+ }
+ }
+ length++;
+ }
+ }
+ while (countChar(equation, '(') > countChar(equation, ')'))
+ {
+ equation += ')';
+ }
+ return equation;
+}
+
+function evalEquation(eq, x)
+{
+ try
+ {
+ eval('var result = ' + eq);
+ return result;
+ }
+ catch(err)
+ {
+ return 0;
+ }
+}
+
+function create()
+{
+ var date = new Date();
+ var start = date.getTime();
+ var form = document.getElementById('Options');
+ var errtag = document.getElementById('Error');
+ errtag.innerHTML = '';
+ for (var i = 0; i < form.elements.length; i++)
+ {
+ if (form.elements[i].value == '')
+ {
+ errtag.innerHTML = 'Please enter a valid number.';
+ return;
+ }
+ }
+
+ length = parseInt(form.elements[1].value);
+ notify = form.elements[2].checked;
+
+ singleweight = parseFloat(form.elements[4].value);
+ numberweight = parseFloat(form.elements[5].value);
+ eqlength = parseFloat(form.elements[6].value);
+
+ var data = [];
+ var equation = randEquation();
+
+ for (var i=0; i<length * 10000; i++)
+ {
+ var value = evalEquation(equation, i);
+ value %= 1;
+ data[i] = Math.abs(255 * value);
+ }
+ var wave = new RIFFWAVE(data);
+ audio = document.getElementById('Audio');
+ audio.src = wave.dataURI;
+ date = new Date();
+ end = date.getTime();
+ var timeparagraph = document.getElementById('Time');
+ var timetaken = Math.round((end-start)/1000);
+ if (timetaken == 1)
+ {
+ timeparagraph.innerHTML = 'The time it took was 1 second.';
+ }
+ else
+ {
+ timeparagraph.innerHTML = 'The time it took was ' + timetaken + ' seconds.';
+ }
+
+ functionp.innerHTML = '$Function: $' + rmvmath(equation);
+ LatexIT.render('*',false);
+
+ if(notify)
+ {
+ alert('Your audio has finished.');
+ }
+
+ audio.play();
+
+
+
+}
diff --git a/js/AutoHarmonograph.js b/js/AutoHarmonograph.js
new file mode 100644
index 0000000..b4158db
--- /dev/null
+++ b/js/AutoHarmonograph.js
@@ -0,0 +1,107 @@
+var t = 0;
+var pp;
+var Ax, Ay, fx, fy, dx, dy, basepx, basepy;
+var AMAX = 1000;
+var DMIN = 0.0001;
+var DMAX = 0.0003;
+var FMAX = 0.1;
+var PMAX = 6.28;
+var ADIF = AMAX / 10;
+var DDIF = DMAX / 10;
+var FDIF = FMAX / 10;
+var PDIF = PMAX / 10;
+
+
+function sinExp(A, t, f, p, d)
+{
+ return A*sin(t*f + p) * exp(-d*t);
+}
+
+var Pendulum = function()
+{
+ this.xy = floor(random(2));
+ if (this.xy === 0)
+ this.p = random(-PDIF, PDIF)+basepx;
+ else
+ this.p = random(-PDIF, PDIF)+basepy;
+
+ pendulums.push(this);
+}
+
+Pendulum.prototype.swing = function()
+{
+ if (this.xy === 0)
+ {
+ var x = sinExp(Ay, t, fx, this.p, dx);
+ var y = 0;
+ }
+ else
+ {
+ var x = 0;
+ var y = sinExp(Ax, t, fy, this.p, dy);
+ }
+ return [x, y];
+}
+
+var pendulums = [];
+
+
+function calculate()
+{
+ var xsum = 0;
+ var ysum = 0;
+ var sw;
+ for (var i = 0; i < pendulums.length; i++)
+ {
+ sw = pendulums[i].swing();
+ xsum += sw[0];
+ ysum += sw[1];
+ }
+ return [xsum/pendulums.length, ysum/pendulums.length];
+}
+
+
+function setup()
+{
+ document.getElementById("npendulums").value = 10;
+ document.getElementById("DMAX").value = DMAX;
+}
+
+function start()
+{
+
+ createCanvas(800, 800);
+ frameRate(1000);
+ DMAX = document.getElementById("DMAX").value;
+ basepx = random(PMAX);
+ basepy = random(PMAX);
+ Ax = random(AMAX);
+ Ay = random(AMAX);
+ fx = random(FMAX);
+ fy = random(FMAX);
+ dx = random(DMIN, DMAX);
+ dy = random(DMIN, DMAX);
+
+ pendulums = [];
+
+ for (var i = 0; i < document.getElementById("npendulums").value; i++)
+ new Pendulum();
+
+}
+
+function saveCanvas()
+{
+ save("AutoHarmonograph.png");
+}
+
+function draw()
+{
+ translate(width/2, height/2);
+ curr = calculate();
+ if (t !== 0)
+ line(pp[0], pp[1], curr[0], curr[1]);
+
+
+ t++;
+ pp = curr;
+}
diff --git a/js/AutoImages.js b/js/AutoImages.js
new file mode 100644
index 0000000..cd3ff66
--- /dev/null
+++ b/js/AutoImages.js
@@ -0,0 +1,240 @@
+
+var canvas = document.getElementById('Canvas');
+var ctx = canvas.getContext('2d');
+
+var single = ["Math.sqrt", "Math.cos", "Math.sin"]; //Operations on a single number
+var singleweights = {};
+var binary = ["*", "+", "-", "/"]; //Operations for 2 numbers
+var binaryweights = {};
+var varlist = ["x", "y"];
+var numlist = ["Constant"];
+numlist = numlist.concat(varlist);
+var numberweights = {"Constant":1}
+var numberweight;
+var singleweight;
+var eqlength;
+var xsize;
+var ysize;
+var functionp = document.getElementById('Function');
+var notify;
+var latex;
+var mathjax;
+
+for(var i = 0; i < single.length; i++)
+{
+ singleweights[single[i]] = 1;
+}
+
+for(var i = 0; i < binary.length; i++)
+{
+ binaryweights[binary[i]] = 1;
+}
+
+for(var i = 0; i < varlist.length; i++)
+{
+ numberweights[varlist[i]] = 1;
+}
+
+function rmvmath(str)
+{
+ //A function that removes all the Math.'s in a string
+ var newstr = '';
+ for(var i = 0; i < str.length - 5; i++)
+ {
+ if(str[i] + str[i+1] + str[i+2] + str[i+3] + str[i+4] !== 'Math.')
+ {
+ newstr += str[i]
+ }
+ else
+ {
+ i += 4;
+ }
+ }
+ return newstr;
+};
+
+function randItem(l)
+{
+ return l[Math.floor(Math.random() * l.length)];
+};
+
+function countChar(string, letter)
+{
+ var amount = 0;
+ for (var i = 0; i < string.length; i++)
+ {
+ if (string[i] === letter)
+ {
+ amount++;
+ }
+ }
+ return amount;
+};
+
+
+
+function randEquation()
+{
+ var hasx = false;
+ var hasy = false;
+ var equation;
+ var lasttype;
+ var thistype;
+ var chanceend;
+ var length;
+ var what;
+ var number;
+
+ while (!(hasx && hasy))
+ {
+ //Types: b for binary, s for single, f for first, n for number
+ equation = '';
+ lasttype = 'f';
+ thistype = 0;
+ hasx = false;
+ hasy = false;
+ chanceend = 0;
+ length = 1; //Number of operations done so far
+
+ while (true)
+ {
+ chanceend = Math.pow((1.0 - (1.0 / length)), eqlength);
+ if (lasttype === 'n')
+ {
+ number = Math.random();
+ if (number < chanceend)
+ {
+ break;
+ }
+ equation = '(' + equation + ')' + randItem(binary);
+ lasttype = 'b';
+ }
+ else if (lasttype === 's' || lasttype === 'b' || lasttype === 'f')
+ {
+ equation += '(';
+ thistype = Math.random();
+ if (thistype < singleweight / (singleweight + numberweight))
+ {
+ equation += randItem(single);
+ lasttype = 's';
+ }
+ else
+ {
+ what = randItem(numlist);
+ if (what === 'Constant')
+ {
+ equation += (Math.random(100, 200)).toString();
+ }
+ else
+ {
+ equation += what;
+ if (what === 'x')
+ {
+ hasx = true;
+ }
+ else if (what === 'y')
+ {
+ hasy = true;
+ }
+ }
+ lasttype = 'n';
+ equation += ')';
+ }
+ }
+ length++;
+ }
+ }
+ while (countChar(equation, '(') > countChar(equation, ')'))
+ {
+ equation += ')';
+ }
+ return equation;
+};
+
+
+function create()
+{
+ var date = new Date();
+ var start = date.getTime();
+
+ form = document.getElementById('Options');
+
+ var errtag = document.getElementById('Error');
+ errtag.innerHTML = '';
+
+ for (var i = 0; i < form.elements.length; i++)
+ {
+ if (form.elements[i].value === '')
+ {
+ errtag.innerHTML = 'Please enter a valid number.';
+ return;
+ }
+ }
+
+ xsize = parseInt(form.elements[1].value);
+ ysize = parseInt(form.elements[2].value);
+ notify = form.elements[3].checked;
+
+ singleweight = parseFloat(form.elements[5].value);
+ numberweight = parseFloat(form.elements[6].value);
+ eqlength = parseFloat(form.elements[7].value);
+
+ canvas.width = xsize;
+ canvas.height = ysize;
+ var imgData = ctx.createImageData(xsize, ysize);
+ var requation = randEquation();
+ var gequation = randEquation();
+ var bequation = randEquation();
+ var x;
+ var y;
+ var r;
+ var g;
+ var b;
+ eval(
+ 'for (var i=0;i<imgData.data.length;i+=4)'+
+ '{'+
+ ' x = (i/4) % xsize;'+
+ ' y = Math.floor((i/4) / xsize);'+
+ ' r = (' + requation + ') % 255;'+
+ ' g = (' + gequation + ') % 255;'+
+ ' b = (' + bequation + ') % 255;'+
+ ' r = Math.abs(Math.round(r));' +
+ ' g = Math.abs(Math.round(g));' +
+ ' b = Math.abs(Math.round(b));' +
+
+ ' imgData.data[i] = r;' +
+ ' imgData.data[i+1] = g;' +
+ ' imgData.data[i+2] = b;' +
+ ' imgData.data[i+3] = 255;' +
+ '}');
+ ctx.putImageData(imgData,0,0);
+ var date = new Date();
+ var end = date.getTime();
+ var timeparagraph = document.getElementById('Time');
+ var timetaken = Math.round((end-start)/1000);
+ if (timetaken === 1)
+ {
+ timeparagraph.innerHTML = 'The time it took was 1 second.';
+ }
+ else
+ {
+ timeparagraph.innerHTML = 'The time it took was ' + timetaken + ' seconds.';
+ }
+
+
+ functionp.innerHTML = '$Functions: \\newline\\newline Red: $' + rmvmath(requation) + '$\\newline\\newline Green: $' + rmvmath(gequation) + '$\\newline\\newline Blue: $' + rmvmath(bequation) + '$';
+ LatexIT.render('*',false);
+ if(notify)
+ {
+ alert('Your image has finished.');
+ }
+ document.getElementById('Download').innerHTML = "Download";
+};
+
+
+function download() {
+ var dt = canvas.toDataURL('image/png');
+ this.href = dt;
+};
+var downloadLink = document.getElementById('Download');
+downloadLink.addEventListener('click', download, false);
diff --git a/js/AutoVideos.js b/js/AutoVideos.js
new file mode 100644
index 0000000..bd3a744
--- /dev/null
+++ b/js/AutoVideos.js
@@ -0,0 +1,265 @@
+
+var single = ["Math.sqrt", "Math.cos", "Math.sin"]; //Operations on a single number
+var singleweights = {};
+var binary = ["*", "+", "-", "/"]; //Operations for 2 numbers
+var binaryweights = {};
+var varlist = ["x", "y", "t"];
+var numlist = ["Constant"];
+numlist = numlist.concat(varlist);
+var numberweights = {"Constant":1};
+var numberweight;
+var singleweight;
+var eqlength;
+var functionp = document.getElementById('Function');
+var notify;
+
+for(var i = 0; i < single.length; i++)
+{
+ singleweights[single[i]] = 1;
+}
+
+for(var i = 0; i < binary.length; i++)
+{
+ binaryweights[binary[i]] = 1;
+}
+
+for(var i = 0; i < varlist.length; i++)
+{
+ numberweights[varlist[i]] = 1;
+}
+
+function rmvmath(str)
+{
+ //A function that removes all the Math.'s in a string
+ var newstr = '';
+ for(var i = 0; i < str.length - 5; i++)
+ {
+ if(str[i] + str[i+1] + str[i+2] + str[i+3] + str[i+4] !== 'Math.')
+ {
+ newstr += str[i]
+ }
+ else
+ {
+ i += 4;
+ }
+ }
+ return newstr;
+};
+
+function randItem(l)
+{
+ return l[Math.floor(Math.random() * l.length)];
+};
+
+function countChar(string, letter)
+{
+ var amount = 0;
+ for (var i = 0; i < string.length; i++)
+ {
+ if (string[i] == letter)
+ {
+ amount++;
+ }
+ }
+ return amount;
+};
+
+
+function randEquation()
+{
+ var hasx = false;
+ var hasy = false;
+ var hast = false;
+ var equation;
+ var lasttype;
+ var thistype;
+ var chanceend;
+ var length;
+ var what;
+ var number;
+
+
+ while (!(hasx && hasy && hast))
+ {
+ //Types: b for binary, s for single, f for first, n for number
+ equation = '';
+ lasttype = 'f';
+ thistype = 0;
+ hasx = false;
+ hasy = false;
+ hast = false;
+ chanceend = 0;
+ length = 1; //Number of operations done so far
+
+ while (true)
+ {
+ chanceend = Math.pow((1.0 - (1.0 / length)), eqlength);
+ if (lasttype == 'n')
+ {
+ number = Math.random();
+ if (number < chanceend)
+ {
+ break;
+ }
+ equation = '(' + equation + ')' + randItem(binary);
+ lasttype = 'b';
+ }
+ else if (lasttype == 's' || lasttype == 'b' || lasttype == 'f')
+ {
+ equation += '(';
+ thistype = Math.random();
+ if (thistype < singleweight / (singleweight + numberweight))
+ {
+ equation += randItem(single);
+ lasttype = 's';
+ }
+ else
+ {
+ what = randItem(numlist);
+ if (what == 'Constant')
+ {
+ equation += (Math.random(100, 200)).toString();
+ }
+ else
+ {
+ equation += what;
+ if (what == 'x')
+ {
+ hasx = true;
+ }
+ else if (what == 'y')
+ {
+ hasy = true;
+ }
+ else if (what == 't')
+ {
+ hast = true;
+ }
+ }
+ lasttype = 'n';
+ equation += ')';
+ }
+ }
+ length++;
+ }
+ }
+ while (countChar(equation, '(') > countChar(equation, ')'))
+ {
+ equation += ')';
+ }
+ return equation;
+};
+
+function evalEquation(eq, x, y, t)
+{
+ try
+ {
+ eval('var result = ' + eq);
+ return result;
+ }
+ catch(err)
+ {
+ return 0;
+ }
+};
+function create()
+{
+ var d = new Date();
+ var start = d.getTime();
+ var xsize;
+ var ysize;
+ var length;
+ var framerate;
+
+ var form = document.getElementById('Options');
+
+ var errtag = document.getElementById('Error');
+ errtag.innerHTML = '';
+
+ for (var i = 0; i < form.elements.length; i++)
+ {
+ if (form.elements[i].value == '')
+ {
+ errtag.innerHTML = 'Please enter a valid number.';
+ return;
+ }
+ }
+
+ xsize = parseInt(form.elements[1].value);
+ ysize = parseInt(form.elements[2].value);
+ length = parseFloat(form.elements[3].value);
+ framerate = parseInt(form.elements[4].value);
+ notify = form.elements[5].checked;
+
+ singleweight = parseFloat(form.elements[7].value);
+ numberweight = parseFloat(form.elements[8].value);
+ eqlength = parseFloat(form.elements[9].value);
+
+ var requation = randEquation();
+ var gequation = randEquation();
+ var bequation = randEquation();
+ var x;
+ var y;
+ var r;
+ var g;
+ var b;
+ var encoder = new Whammy.Video(framerate);
+ var video = document.getElementById('Video');
+ var canvas = document.getElementById('Canvas');
+ video.width = xsize;
+ video.height = ysize;
+ canvas.width = xsize;
+ canvas.height = ysize;
+ var ctx = canvas.getContext('2d');
+ var apx = ctx.getImageData(0, 0, xsize, ysize);
+ var data = apx.data;
+
+
+ eval(
+ 'for(var t = 0; t < Math.round(framerate * length); t++)'+
+ '{'+
+ ' for(var i = 0; i < data.length; i+=4)'+
+ ' {'+
+ ' x = (i/4) % xsize;'+
+ ' y = Math.floor((i/4) / xsize);'+
+ ' r = Math.abs(Math.round((' + requation + ') % 255));'+
+ ' g = Math.abs(Math.round((' + gequation + ') % 255));'+
+ ' b = Math.abs(Math.round((' + bequation + ') % 255));'+
+ ' data[i] = r;'+
+ ' data[i+1] = g;'+
+ ' data[i+2] = b;'+
+ ' data[i+3] = 255;'+
+ ' }'+
+ ' apx.data = data;'+
+ ' ctx.putImageData(apx, 0, 0);'+
+ ' encoder.add(ctx);'+
+ '}'
+ );
+ var output = encoder.compile();
+ var url = webkitURL.createObjectURL(output);
+ video.src = url;
+
+ ctx.drawImage(video, 0, 0);
+ d = new Date();
+ var end = d.getTime();
+ var timeparagraph = document.getElementById('Time');
+ var timetaken = Math.round((end-start)/1000);
+ if (timetaken == 1)
+ {
+ timeparagraph.innerHTML = 'The time it took was 1 second.';
+ }
+ else
+ {
+ timeparagraph.innerHTML = 'The time it took was ' + timetaken + ' seconds.';
+ }
+
+ functionp.innerHTML = '$Functions: \\newline\\newline Red: $' + rmvmath(requation) + '$\\newline\\newline Green: $' + rmvmath(gequation) + '$\\newline\\newline Blue: $' + rmvmath(bequation) + '$';
+ LatexIT.render('*',false);
+
+ if(notify)
+ {
+ alert('Your video has finished.');
+ }
+
+
+};
diff --git a/js/Harmonograph.js b/js/Harmonograph.js
new file mode 100644
index 0000000..a2e12a6
--- /dev/null
+++ b/js/Harmonograph.js
@@ -0,0 +1,206 @@
+var t = 0;
+var pp;
+var AMAX = 1000;
+var DMIN = 0.0001;
+var DMAX = 0.0003;
+var FMAX = 0.1;
+var PMAX = 6.28;
+var ADIF = AMAX / 10;
+var DDIF = DMAX / 10;
+var FDIF = FMAX / 10;
+var PDIF = PMAX / 10;
+var npendulums = 0;
+var started = false;
+
+function sinExp(A, t, f, p, d)
+{
+
+
+ return A*sin(t*f + p) * exp(-d*t);
+}
+
+var Pendulum = function(a, d, f, p, xy)
+{
+ this.a = a;
+ this.d = d;
+ this.f = f;
+ this.p = p;
+ this.xy = xy;
+ pendulums.push(this);
+}
+
+Pendulum.prototype.swing = function()
+{
+ //document.write(this.a + " " + t + " " + this.f + " " + this.p + " " + this.d + " " + this.xy + "<br>");
+ if (this.xy == 0)
+ {
+ var x = sinExp(this.a, t, this.f, this.p, this.d);
+ var y = 0;
+ }
+ else
+ {
+ var x = 0;
+ var y = sinExp(this.a, t, this.f, this.p, this.d);
+ }
+
+ return [x, y];
+}
+
+var pendulums = [];
+
+
+function calculate()
+{
+ var xsum = 0;
+ var ysum = 0;
+ var sw;
+ for (var i = 0; i < pendulums.length; i++)
+ {
+ sw = pendulums[i].swing();
+ //document.write(sw + "<br>");
+ xsum += sw[0];
+ ysum += sw[1];
+ }
+ return [xsum/pendulums.length, ysum/pendulums.length];
+}
+
+function addPendulum()
+{
+ var txt = document.createElement("p");
+ txt.innerHTML = "Phase:";
+ var p = document.createElement("input");
+ p.type = "number";
+ p.value = PI;
+ p.id = "p" + npendulums;
+ txt.id = "pt" + npendulums;
+ document.body.appendChild(txt);
+ document.body.appendChild(p);
+
+ txt = document.createElement("p");
+ txt.innerHTML = "Amplitude:";
+ txt.id = "at" + npendulums;
+ var a = document.createElement("input");
+ a.type = "number";
+ a.value = 500;
+ a.id = "A" + npendulums;
+ document.body.appendChild(txt);
+ document.body.appendChild(a);
+
+ txt = document.createElement("p");
+ txt.innerHTML = "Damping:";
+ txt.id = "dt" + npendulums;
+ var d = document.createElement("input");
+ d.type = "number";
+ d.value = 0.0003;
+ d.id = "d" + npendulums;
+ document.body.appendChild(txt);
+ document.body.appendChild(d);
+
+
+ txt = document.createElement("p");
+ txt.innerHTML = "Frequency:";
+ var f = document.createElement("input");
+ f.type = "number";
+ f.value = 0.1;
+ f.id = "f" + npendulums;
+ txt.id = "ft" + npendulums;
+ document.body.appendChild(txt);
+ document.body.appendChild(f);
+
+ txt = document.createElement("p");
+ txt.innerHTML = "Is it a Y pendulum? (you should have at least one X and Y pendulum) ";
+ var xy = document.createElement("input");
+ xy.type = "checkbox";
+ xy.value = 1;
+ xy.id = "xy" + npendulums;
+ txt.id = "xyt" + npendulums;
+ document.body.appendChild(txt);
+ document.body.appendChild(xy);
+ var br = document.createElement("br");
+ br.id = "br" + npendulums;
+ document.body.appendChild(br);
+
+
+ txt = document.createTextNode("Delete this pendulum");
+ var delBtn = document.createElement("button");
+ delBtn.setAttribute("onclick", "deletePendulum(" + npendulums + ");");
+ delBtn.appendChild(txt);
+ delBtn.id = "db" + npendulums;
+ document.body.appendChild(delBtn);
+ npendulums++;
+}
+
+function deletePendulum(x)
+{
+ pendulums.splice(x, 1);
+ document.body.removeChild(document.getElementById("p" +x));
+ document.body.removeChild(document.getElementById("d" +x));
+ document.body.removeChild(document.getElementById("f" +x));
+ document.body.removeChild(document.getElementById("A" +x));
+ document.body.removeChild(document.getElementById("xy" +x));
+
+ document.body.removeChild(document.getElementById("pt" +x));
+ document.body.removeChild(document.getElementById("dt" +x));
+ document.body.removeChild(document.getElementById("ft" +x));
+ document.body.removeChild(document.getElementById("at" +x));
+ document.body.removeChild(document.getElementById("xyt" +x));
+
+ document.body.removeChild(document.getElementById("db" +x));
+ document.body.removeChild(document.getElementById("br" +x));
+
+ for (var i = x+1; i < npendulums; i++)
+ {
+ document.getElementById("p"+i).id = "p"+(i-1);
+ document.getElementById("d"+i).id = "d"+(i-1);
+ document.getElementById("f"+i).id = "f"+(i-1);
+ document.getElementById("A"+i).id = "A"+(i-1);
+ document.getElementById("pt"+i).id = "pt"+(i-1);
+ document.getElementById("dt"+i).id = "dt"+(i-1);
+ document.getElementById("ft"+i).id = "ft"+(i-1);
+ document.getElementById("at"+i).id = "at"+(i-1);
+ document.getElementById("xy"+i).id = "xy"+(i-1);
+ document.getElementById("xyt"+i).id = "xyt"+(i-1);
+ document.getElementById("db"+i).id = "db"+(i-1);
+ document.getElementById("db"+(i-1)).setAttribute("onclick", "deletePendulum(" + (i-1) + ");");
+ document.getElementById("br"+i).id = "br"+(i-1);
+ }
+ npendulums--;
+}
+
+
+function start()
+{
+ started = true;
+ createCanvas(800, 800);
+ frameRate(1000);
+ for (var i = 0; i < npendulums; i++)
+ {
+ //document.write("Hello");
+ var a = document.getElementById("A" + i).value;
+ var f = document.getElementById("f" + i).value;
+ var p = document.getElementById("p" + i).value;
+ var d = document.getElementById("d" + i).value;
+ var xy = document.getElementById("xy"+i).checked;
+ new Pendulum(parseFloat(a), parseFloat(d), parseFloat(f), parseFloat(p), xy);
+ }
+
+}
+
+function saveCanvas()
+{
+ save("AutoHarmonograph.png");
+}
+
+function draw()
+{
+ if(!started)
+ return;
+ translate(width/2, height/2);
+ curr = calculate();
+ if (t !== 0)
+ line(pp[0], pp[1], curr[0], curr[1]);
+
+
+ t++;
+ pp = curr;
+} \ No newline at end of file
diff --git a/js/NameGenerator.js b/js/NameGenerator.js
new file mode 100644
index 0000000..44e3eaa
--- /dev/null
+++ b/js/NameGenerator.js
@@ -0,0 +1,149 @@
+
+var trigrams = {};
+var trigramKeyList;
+var sumStartsWith = {};
+
+function loadTrigrams(responseText)
+{
+ var lines = responseText.split('\n');
+ for (var i = 0; i < lines.length; i++)
+ {
+ var trigram = lines[i].substring(0, 3);
+ var value = parseInt(lines[i].substring(4, lines[i].length));
+ if (trigram == '' || isNaN(value))
+ continue;
+ trigrams[trigram] = value;
+ }
+
+ trigramKeyList = Object.keys(trigrams);
+
+ document.getElementById("loading").innerHTML = "";
+}
+
+function start()
+{
+ var xhttp = new XMLHttpRequest();
+
+ xhttp.onreadystatechange = function()
+ {
+ if (xhttp.readyState == 4 && xhttp.status == 200)
+ {
+ loadTrigrams(xhttp.responseText);
+ }
+ };
+ xhttp.open("GET", "https://raw.githubusercontent.com/pommicket/NameGenerator/master/trigrams.txt", true);
+ xhttp.send();
+}
+
+function pickFirst2()
+{
+
+ var count = 0;
+ var sum = 0;
+
+ for (var i = 0; i < trigramKeyList.length; i++)
+ {
+ if (trigramKeyList[i][0] == ' ')
+ {
+ sum += trigrams[trigramKeyList[i]];
+ }
+ }
+
+ var selected = Math.random() * sum;
+
+
+ for (var i = 0; i < trigramKeyList.length; i++)
+ {
+ if (trigramKeyList[i][0] == ' ')
+ {
+ count += trigrams[trigramKeyList[i]];
+ if (selected < count)
+ return trigramKeyList[i].substring(1);
+ }
+ }
+
+ return "ERROR";
+}
+
+function nextChar(name)
+{
+ var last2 = name.substring(name.length-2);
+ var total = 0;
+ if (sumStartsWith[last2] == NaN || sumStartsWith[last2] == undefined)
+ {
+ for (var i = 0; i < trigramKeyList.length; i++)
+ {
+ if (trigramKeyList[i].substring(0, 2) == last2)
+ total += trigrams[trigramKeyList[i]];
+ }
+ sumStartsWith[last2] = total;
+ }
+ else
+ {
+ total = sumStartsWith[last2];
+ }
+ var selected = Math.random() * total;
+ var count = 0;
+
+ for (var i = 0; i < trigramKeyList.length; i++)
+ {
+ if (trigramKeyList[i].substring(0, 2) == last2)
+ {
+ count += trigrams[trigramKeyList[i]];
+ if (selected < count)
+ return trigramKeyList[i][2];
+ }
+ }
+
+ return "ERROR";
+}
+
+
+function generateName()
+{
+ var first = pickFirst2();
+ var name = first;
+ var next = '';
+ var length = 0;
+ do
+ {
+ name += next;
+ next = nextChar(name);
+ }
+ while (next != ' ');
+
+ name = name[0].toUpperCase() + name.substring(1);
+
+
+ return name;
+}
+
+
+function createNames()
+{
+ document.getElementById("button").disabled = true;
+ var nameStr = '';
+ var numNames = document.getElementById("numNames").value;
+ var nameDiv = document.getElementById("names");
+
+ window.setTimeout(50, function() {document.getElementById("loading").innerHTML = "Loading...";});
+
+ nameDiv.innerHTML = "";
+
+ for (var i = 0; i < numNames; i++)
+ nameStr += generateName() + "<br>";
+
+ if (document.getElementById("outputNames").checked)
+ nameDiv.innerHTML = nameStr;
+
+ document.getElementById("loading").innerHTML = "";
+
+ var dload;
+ dload = document.getElementById("download");
+ dload.innerHTML = "Download names (.txt)";
+ var txt = nameStr.replace(/<br>/g, "\n");
+ dload.href = "data:text/plain;charset=utf-8," + encodeURI(txt);
+ document.getElementById("button").disabled = false;
+}
+
+start(); \ No newline at end of file
diff --git a/js/ant.js b/js/ant.js
new file mode 100644
index 0000000..f6ef4c6
--- /dev/null
+++ b/js/ant.js
@@ -0,0 +1,135 @@
+var cells = [];
+var direction = 'u';
+var antPos;
+var running = false;
+
+function turnright(d)
+{
+ switch(d)
+ {
+ case 'u':
+ return 'r';
+ case 'd':
+ return 'l';
+ case 'r':
+ return 'd';
+ case 'l':
+ return 'u';
+ }
+}
+
+function turnleft(d)
+{
+ return turnright(turnright(turnright(d)));
+}
+
+
+function setup()
+{
+ frameRate(100);
+ createCanvas(500, 500);
+ antPos = [width/2, height/2];
+
+ for (var i = 0; i < height; i++)
+ {
+ cells.push([]);
+ for (var j = 0; j < width; j++)
+ {
+ cells[i].push(1);
+ }
+ }
+ stroke(255, 0, 0);
+ point(antPos[0], antPos[1]);
+}
+
+
+
+function start()
+{
+ running = true;
+}
+
+function stop()
+{
+ running = false;
+}
+
+
+
+function resetPos()
+{
+ drawCell(antPos[0], antPos[1]);
+ antPos[0] = width/2;
+ antPos[1] = height/2;
+ stroke(255, 0, 0);
+ point(antPos[0], antPos[1]);
+}
+
+function drawCell(x, y)
+{
+ stroke(cells[y][x]*255);
+ point(x, y);
+}
+
+function draw()
+{
+ if (!running)
+ return;
+
+ for (var i = 0; i < parseInt(document.getElementById("speed").value); i++)
+ {
+
+ if (antPos[0] < 0 || antPos[0] >= width || antPos[1] < 0 || antPos[1] >= height)
+ {
+ running = false;
+ }
+
+ if (!running)
+ return;
+
+ if (cells[antPos[1]][antPos[0]] == 1)
+ direction = turnright(direction);
+ else
+ direction = turnleft(direction);
+
+
+ cells[antPos[1]][antPos[0]] = 1-cells[antPos[1]][antPos[0]];
+ drawCell(antPos[0], antPos[1]);
+
+ if (direction == 'u')
+ antPos[1]--;
+ else if (direction == 'l')
+ antPos[0]--;
+ else if (direction == 'd')
+ antPos[1]++;
+ else if (direction == 'r')
+ antPos[0]++;
+
+ stroke(255, 0, 0);
+ point(antPos[0], antPos[1]);
+ }
+}
+
+function mouseDragged()
+{
+ if (mouseX < 0 || mouseX >= width || mouseY < 0 || mouseY >= height)
+ return;
+ cells[mouseY][mouseX] = 1-cells[mouseY][mouseX];
+ drawCell(mouseX, mouseY);
+}
+
+function clearCells()
+{
+ cells = [];
+ for (var i = 0; i < height; i++)
+ {
+ cells.push([]);
+ for (var j = 0; j < width; j++)
+ {
+ cells[i].push(1);
+ }
+ }
+ background(255);
+ stroke(255, 0, 0);
+ point(antPos[0], antPos[1]);
+} \ No newline at end of file
diff --git a/js/ballbounce.js b/js/ballbounce.js
new file mode 100644
index 0000000..15c3a53
--- /dev/null
+++ b/js/ballbounce.js
@@ -0,0 +1,54 @@
+var WIDTH = 500;
+var HEIGHT = 500;
+var ball_radius = 25;
+var ball_pos = [WIDTH/2, 0];
+var ball_vel = [0, 0];
+var score = 0;
+var started = false;
+function setup()
+{
+ createCanvas(WIDTH, HEIGHT);
+ reset();
+}
+
+function reset()
+{
+ background(255, 255, 255);
+ stroke(255, 0, 0);
+ noFill();
+ rect(0, 0, WIDTH-1, HEIGHT-1);
+ noStroke();
+}
+
+function draw()
+{
+
+ fill(0,0,0);
+ ellipse(ball_pos[0], ball_pos[1], ball_radius*2, ball_radius*2);
+ fill(255, 0, 0);
+ if (abs(ball_pos[0] - mouseX) < ball_radius && mouseY > ball_pos[1])
+ {
+ if (dist(ball_pos[0], ball_pos[1], mouseX, mouseY) < ball_radius)
+ {
+ ball_vel = [(ball_pos[0] - mouseX) / 10, -4];
+ ball_pos[1] -= 10;
+ score++;
+ started = true;
+ }
+ }
+ if (started)
+ ball_vel[1] += 0.1;
+
+ ball_pos[0] += ball_vel[0];
+ ball_pos[1] += ball_vel[1];
+
+ if (ball_pos[1] > HEIGHT)
+ {
+ alert("You lost. Score: " + score);
+ reset();
+ ball_pos = [WIDTH/2, 0];
+ ball_vel = [0, 0];
+ score = 0;
+ started = false;
+ }
+} \ No newline at end of file
diff --git a/js/clock.js b/js/clock.js
new file mode 100644
index 0000000..314d002
--- /dev/null
+++ b/js/clock.js
@@ -0,0 +1,116 @@
+var WIDTH = 600;
+var HEIGHT = 600;
+var CIRCLE_SIZE = 20;
+var SECOND_CIRCLE = 100;
+var MINUTE_CIRCLE = 200;
+var HOUR_CIRCLE = 300;
+var DAY_CIRCLE = 500;
+function setup()
+{
+createCanvas(600, 600);
+ ellipseMode(CENTER);
+}
+
+function leapYear()
+{
+ return year() % 4 == 0 && (!(year() % 100 == 0) || year() % 400 == 0) ? 1 : 0;
+}
+
+
+function day365()
+{
+ var m = month();
+ var currday = 0;
+ if (m == 0)
+ return day();
+ currday += 31;
+ if (m == 1)
+ return day()+currday;
+ currday += 28 + leapYear();
+ if (m == 2)
+ return day()+currday;
+ currday += 31;
+ if (m == 3)
+ return day()+currday;
+ currday += 30;
+ if (m == 4)
+ return day()+currday;
+ currday += 31;
+ if (m == 5)
+ return day()+currday;
+ currday += 30;
+ if (m == 6)
+ return day()+currday;
+ currday += 31;
+ if (m == 7)
+ return day()+currday;
+ currday += 31;
+ if (m == 8)
+ return day()+currday;
+ currday += 30;
+ if (m == 9)
+ return day()+currday;
+ currday += 31;
+ if (m == 10)
+ return day()+currday;
+ currday += 30;
+ if (m == 11)
+ return day()+currday;
+ currday += 31;
+ if (m == 12)
+ return day()+currday;
+ return -1;
+}
+
+function draw()
+{
+ background(255, 255, 255);
+ var date = new Date();
+ var ms = date.getTime() % 1000;
+ var s = second();
+ var h = hour();
+ var m = minute();
+ var secs = s + ms/1000;
+ var mins = m + secs / 60;
+ var hrs = h + mins / 60;
+ var dy = day365() + hrs / 24;
+
+ hours = TWO_PI * (hrs / 24) - HALF_PI;
+ minutes = TWO_PI * (mins / 60) - HALF_PI;
+ seconds = TWO_PI * (secs / 60) - HALF_PI;
+ date = TWO_PI * (dy / (365 + leapYear())) - HALF_PI;
+ noFill();
+
+ stroke(secs*4, 0, 0);
+ ellipse(WIDTH / 2, HEIGHT / 2, SECOND_CIRCLE, SECOND_CIRCLE);
+ stroke(0, mins*4, 0);
+ ellipse(WIDTH / 2, HEIGHT / 2, MINUTE_CIRCLE, MINUTE_CIRCLE);
+ stroke(0, 0, hrs*4);
+ ellipse(WIDTH / 2, HEIGHT / 2, HOUR_CIRCLE, HOUR_CIRCLE);
+ stroke(0, 0, 0);
+ ellipse(WIDTH / 2, HEIGHT / 2, DAY_CIRCLE, DAY_CIRCLE);
+
+ noStroke();
+
+ var dayX = (cos(date) * DAY_CIRCLE / 2) + WIDTH / 2;
+ var dayY = (sin(date) * DAY_CIRCLE / 2) + HEIGHT / 2;
+
+ var hourX = (cos(hours) * HOUR_CIRCLE / 2) + WIDTH / 2;
+ var hourY = (sin(hours) * HOUR_CIRCLE / 2) + HEIGHT / 2;
+
+ var minuteX = (cos(minutes) * MINUTE_CIRCLE / 2) + WIDTH / 2;
+ var minuteY = (sin(minutes) * MINUTE_CIRCLE / 2) + HEIGHT / 2;
+
+ var secondX = (cos(seconds) * SECOND_CIRCLE / 2) + WIDTH / 2;
+ var secondY = (sin(seconds) * SECOND_CIRCLE / 2) + HEIGHT / 2;
+
+ fill(secs*4, 0, 0);
+ ellipse(secondX, secondY, CIRCLE_SIZE, CIRCLE_SIZE);
+ fill(0, mins*4, 0);
+ ellipse(minuteX, minuteY, CIRCLE_SIZE, CIRCLE_SIZE);
+ fill(0, 0, hrs*4);
+ ellipse(hourX, hourY, CIRCLE_SIZE, CIRCLE_SIZE);
+ fill(0, 0, 0);
+ ellipse(dayX, dayY, CIRCLE_SIZE, CIRCLE_SIZE);
+ textSize(30);
+} \ No newline at end of file
diff --git a/js/h.js b/js/h.js
new file mode 100644
index 0000000..d23530f
--- /dev/null
+++ b/js/h.js
@@ -0,0 +1,203 @@
+try
+{
+var lineLength = 5;
+var canvas;
+var ctx;
+var w;
+var h;
+var endpoints;
+var allendpoints;
+var delay;
+var iterateTimeout;
+var stopped = false;
+var running = false;
+var chance;
+
+function line(x1, y1, x2, y2)
+{
+ ctx.beginPath();
+ ctx.moveTo(x1+0.5, y1);
+ ctx.lineTo(x2+0.5, y2);
+ ctx.stroke();
+}
+
+function Endpoint(x, y, direction)
+{
+ this.x = x;
+ this.y = y;
+ this.direction = direction;
+}
+
+function equals(other)
+{
+ return this.x == other.x && this.y == other.y;
+}
+
+function nequals(other)
+{
+ return !this.equals(other);
+}
+
+function multiple(e)
+{
+ var count = 0;
+ for (var i = 0; i < allendpoints.length; i++)
+ {
+ if (allendpoints[i].equals(e))
+ {
+ count++;
+
+ if (count > 1)
+ return true;
+ }
+ }
+
+ return false;
+
+}
+
+function rmvAll(e)
+{
+ var newArray = [];
+ for (var i = 0; i < endpoints.length; i++)
+ if (endpoints[i].nequals(e))
+ newArray.push(endpoints[i]);
+ endpoints = newArray;
+}
+
+
+function iterate()
+{
+ var indexOfThis = endpoints.indexOf(this);
+ endpoints.splice(indexOfThis, 1);
+
+ if (this.direction == 'h')
+ {
+ line(this.x-lineLength, this.y, this.x+lineLength, this.y);
+ var e1 = new Endpoint(this.x-lineLength, this.y, 'v');
+ var e2 = new Endpoint(this.x+lineLength, this.y, 'v');
+
+
+ }
+ else
+ {
+ line(this.x, this.y-lineLength, this.x, this.y+lineLength);
+ var e1 = new Endpoint(this.x, this.y-lineLength, 'h');
+ var e2 = new Endpoint(this.x, this.y+lineLength, 'h');
+
+ }
+
+ if (Math.random() < chance)
+ endpoints.push(e1);
+ if (Math.random() < chance)
+ endpoints.push(e2);
+ allendpoints.push(e1);
+ allendpoints.push(e2);
+
+}
+
+Endpoint.prototype.equals=equals;
+Endpoint.prototype.nequals=nequals;
+Endpoint.prototype.iterate=iterate;
+
+function iterateAll()
+{
+
+ var cpy = endpoints.slice();
+
+ for (var i = 0; i < cpy.length; i++)
+ if (multiple(cpy[i]))
+ rmvAll(cpy[i]);
+
+ cpy = endpoints.slice();
+ for (var i = 0; i < cpy.length; i++)
+ cpy[i].iterate();
+
+ iterateTimeout = setTimeout(iterateAll, delay);
+}
+
+function stop()
+{
+ running = false;
+ if (!stopped)
+ {
+ clearTimeout(iterateTimeout);
+ var download = document.createElement('a');
+ download.innerHTML = 'Download H';
+ download.href = canvas.toDataURL();
+ download.download = 'h.png';
+ download.id = 'download';
+ document.body.appendChild(download);
+ stopped = true;
+ }
+}
+
+function start()
+{
+ if (!running)
+ {
+ running = true;
+
+
+ w = canvas.width;
+ h = canvas.height;
+ endpoints = [];
+ allendpoints = [];
+ var a = new Endpoint(w/2, h/2, 'h');
+ endpoints.push(a);
+ a.iterate();
+ iterateAll();
+ }
+}
+
+
+function beginH()
+{
+try{
+ if (!stopped)
+ {
+ canvas = document.getElementById('canvas');
+
+ var w = parseInt(document.getElementById('w').value);
+ var h = parseInt(document.getElementById('h').value);
+ canvas.width = w;
+ canvas.height = h;
+
+ ctx = canvas.getContext('2d');
+ ctx.strokeStyle = "#000";
+ ctx.fillStyle = '#fff';
+
+ ctx.fillRect(0, 0, w, h);
+
+ lineLength= document.getElementById('lineLength').value;
+ lineLength = parseInt(lineLength);
+
+ delay = parseInt(document.getElementById('delay').value);
+
+ document.body.appendChild(canvas);
+ document.body.appendChild(document.createElement('br'));
+
+ chance = parseFloat(document.getElementById('chance').value);
+
+
+ start();
+ }
+ else
+ {
+ document.body.removeChild(document.getElementById('download'));
+ iterateAll();
+ stopped = false;
+ }
+ }
+catch(e)
+{
+ document.write(e);
+}
+}
+
+
+}
+catch(e)
+{
+ document.write(e);
+} \ No newline at end of file
diff --git a/js/header_links.js b/js/header_links.js
new file mode 100644
index 0000000..3b5b3dd
--- /dev/null
+++ b/js/header_links.js
@@ -0,0 +1,5 @@
+document.getElementById("header_links_div").innerHTML = '<a class="header_link" href="index.html">Home</a> ' +
+'<a class="header_link" href="all.html">All</a> ' +
+'<a class="header_link" href="mathematical.html">Mathematical Demonstrations</a> ' +
+'<a class="header_link" href="games.html">Games</a> ' +
+'<a class="header_link" href="apps.html">Android Apps</a> '; \ No newline at end of file
diff --git a/js/latexit.js b/js/latexit.js
new file mode 100644
index 0000000..f6d76b9
--- /dev/null
+++ b/js/latexit.js
@@ -0,0 +1,110 @@
+/*
+* LaTeX IT - JavaScript to Convert Latex within an HTML page into Equations
+* Copyright (C) 2009 William Bateman, 2008 Waipot Ngamsaad
+
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+var LatexIT = {
+ mode : 'gif',
+ imgnum : 0,
+ isFirefox:false,
+ init : function() {
+ // We need to review the support for SVG. Latest released versions are not supporting this as they should
+ // if(document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1"))
+ // this.mode='svg';
+
+ // browser name
+ // svg support in FireFox is not allowing two images to occur currently on the same line.
+
+ var ua = navigator.userAgent.toLowerCase();
+ if(ua.indexOf("firefox")!=-1)
+ {
+ // this.isFirefox = true;
+ }
+ },
+
+ pre : function(txt) {
+ if ( !txt.match(/<img.*?>/i) )
+ {
+ //Clean code
+ txt=txt.replace(/<br>/mgi,"");
+ txt=txt.replace(/<br \/>/mgi,"");
+ //Create img tag
+ // txt = " <img src=\"http://latex.codecogs.com/"+this.mode+".latex?"+ txt +"\" /> ";
+ // txt = " <object type=\"image/svg+xml\" width=\"20\" data=\"http://latex.codecogs.com/"+this.mode+".latex?"+ txt +"\" /> ";
+
+ if(this.mode=='svg')
+ {
+ // Best for Firefox
+ if(this.isFirefox)
+ txt = " <object type=\"image/svg+xml\" data=\"http://latex.codecogs.com/"+this.mode+".latex?"+ txt +"\" class=\"latex\" style=\"margin:0; padding:0; border:0\" /> ";
+ else // Best for Chrome
+ // txt = " <object type=\"image/svg+xml\" data=\"http://latex.codecogs.com/"+this.mode+".latex?"+ txt +"\" class=\"latex\" /> ";
+ txt = " <img src=\"http://latex.codecogs.com/"+this.mode+".latex?"+ txt +"\" alt=\""+ txt +"\" title=\""+ txt +"\" border=\"0\" class=\"latex\" /> ";
+ }
+ else
+ txt = " <img src=\"http://latex.codecogs.com/"+this.mode+".latex?"+ txt +"\" alt=\""+txt+"\" border=\"0\" class=\"latex\" /> ";
+ }
+ return txt;
+ },
+
+ latex : function(txt) {
+ var html, htmlinline;
+ if(this.isFirefox) {
+ html=" <object type=\"image/svg+xml\" data=\"http://latex.codecogs.com/"+this.mode+".latex?$2\" class=\"latex\" /> ";
+ htmlinline=" <object type=\"image/svg+xml\" data=\"http://latex.codecogs.com/"+this.mode+".latex?\\inline $2\" class=\"latex\" /> ";
+ }
+ else {
+ html=" <img src=\"http://latex.codecogs.com/"+this.mode+".latex?$2\" border=\"0\" class=\"latex\" /> ";
+ htmlinline=" <img src=\"http://latex.codecogs.com/"+this.mode+".latex?\\inline $2\" border=\"0\" class=\"latex\" /> ";
+ }
+
+
+ txt=txt.replace(/(^\$|[^\\]\$)(.*?[^\\])\$/gm, htmlinline);
+ txt=txt.replace(/(^\\|[^\\]\\)\[(.*?[^\\])\\\]/mg," <br/>"+html+"<br/> ");
+ txt=txt.replace(/\\\$/mg,"\$");
+ txt=txt.replace(/\\\\(\[|\])/mg,"$1");
+
+ return txt;
+ },
+
+ render : function(tag, latexmode) {
+ var eqn = window.document.getElementsByTagName(tag);
+ for (var i=0; i<eqn.length; i++) {
+ if(latexmode)
+ eqn[i].innerHTML = LatexIT.latex(eqn[i].innerHTML);
+ else if (eqn[i].getAttribute("lang") == "latex" || eqn[i].getAttribute("xml:lang") == "latex")
+ eqn[i].innerHTML = LatexIT.pre(eqn[i].innerHTML);
+ }
+ },
+
+ add : function(tag, latexmode)
+ {
+ if(typeof(latexmode)=='undefined') latexmode=false;
+ if(window.addEventListener)
+ window.addEventListener('load', new Function('LatexIT.render("'+tag+'", '+latexmode+')'),false);
+ else
+ window.attachEvent('onload', new Function('LatexIT.render("'+tag+'", '+latexmode+')') );
+ },
+
+ scale : function(e,scale)
+ {
+ e.width =(e.width*scale);
+ e.height=(e.height*scale);
+ }
+};
+
+LatexIT.init();
+LatexIT.add('*'); \ No newline at end of file
diff --git a/js/magnets.js b/js/magnets.js
new file mode 100644
index 0000000..7d82507
--- /dev/null
+++ b/js/magnets.js
@@ -0,0 +1,83 @@
+var magnets = [];
+var MAGNET_SIZE = 10;
+var gameOver = false;
+var safe_zone_radius = 300;
+var Magnet = function()
+{
+ do
+ {
+ this.x = Math.floor(Math.random() * width);
+ this.y = Math.floor(Math.random() * height);
+ }
+ while(dist(this.x, this.y, mouseX, mouseY) < safe_zone_radius);
+ magnets.push(this);
+}
+
+Magnet.prototype.drawIt = function()
+{
+ noStroke();
+ fill(150, 0, 0);
+ ellipse(this.x, this.y, MAGNET_SIZE, MAGNET_SIZE);
+
+
+ if (abs(this.x - mouseX) < MAGNET_SIZE / 2 && abs(this.y - mouseY) < MAGNET_SIZE / 2)
+ {
+ gameOver = true;
+ alert("Score: " + magnets.length);
+ magnets = [];
+ gameOver = false;
+ safe_zone_radius = 300;
+ }
+
+
+
+ this.x -= 50 * cos(atan2(this.y-mouseY, this.x-mouseX)) / dist(this.x, this.y, mouseX, mouseY);
+ this.y -= 50 * sin(atan2(this.y-mouseY, this.x-mouseX)) / dist(this.x, this.y, mouseX, mouseY);
+
+
+
+}
+
+function setup()
+{
+ createCanvas(500, 500);
+ ellipseMode(CENTER);
+}
+
+function draw()
+{
+ if (gameOver)
+ return;
+
+ background(255);
+
+ stroke(255, 0, 0);
+ noFill();
+ rect(0, 0, width-1, height-1);
+ fill(0);
+ noStroke();
+ textSize(20);
+ text("Score: " + magnets.length, 5, 20);
+ fill(0, 0, 150);
+ ellipse(mouseX, mouseY, MAGNET_SIZE, MAGNET_SIZE);
+ noFill();
+ stroke(0, 255, 0);
+ safe_zone_radius *= 0.9995;
+ ellipse(mouseX, mouseY, safe_zone_radius, safe_zone_radius);
+
+ if ((mouseX > width || mouseY > height) && magnets.length > 0)
+ {
+ gameOver = true;
+ alert("You went out of bounds. Score: " + magnets.length);
+ magnets = [];
+ gameOver = false;
+ safe_zone_radius = 300;
+ }
+
+ if (frameCount % 100 === 0 && mouseX < width && mouseY < height)
+ new Magnet();
+ for (var i = 0; i < magnets.length; i++)
+ {
+ magnets[i].drawIt();
+ }
+}
diff --git a/js/magnets_old_version.js b/js/magnets_old_version.js
new file mode 100644
index 0000000..6169725
--- /dev/null
+++ b/js/magnets_old_version.js
@@ -0,0 +1,83 @@
+var magnets = [];
+var MAGNET_SIZE = 10;
+var gameOver = false;
+var safe_zone_radius = 300;
+var Magnet = function()
+{
+ do
+ {
+ this.x = Math.floor(Math.random() * width);
+ this.y = Math.floor(Math.random() * height);
+ }
+ while(dist(this.x, this.y, width/2, height/2) < safe_zone_radius);
+ magnets.push(this);
+}
+
+Magnet.prototype.drawIt = function()
+{
+ noStroke();
+ fill(150, 0, 0);
+ ellipse(this.x, this.y, MAGNET_SIZE, MAGNET_SIZE);
+
+
+ if (abs(this.x - mouseX) < MAGNET_SIZE / 2 && abs(this.y - mouseY) < MAGNET_SIZE / 2)
+ {
+ gameOver = true;
+ alert("Score: " + magnets.length);
+ magnets = [];
+ gameOver = false;
+ safe_zone_radius = 300;
+ }
+
+
+
+ this.x -= 50 * cos(atan2(this.y-mouseY, this.x-mouseX)) / dist(this.x, this.y, mouseX, mouseY);
+ this.y -= 50 * sin(atan2(this.y-mouseY, this.x-mouseX)) / dist(this.x, this.y, mouseX, mouseY);
+
+
+
+}
+
+function setup()
+{
+ createCanvas(500, 500);
+ ellipseMode(CENTER);
+}
+
+function draw()
+{
+ if (gameOver)
+ return;
+
+ background(255);
+
+ stroke(255, 0, 0);
+ noFill();
+ rect(0, 0, width-1, height-1);
+ fill(0);
+ noStroke();
+ textSize(20);
+ text("Score: " + magnets.length, 5, 20);
+ fill(0, 0, 150);
+ ellipse(mouseX, mouseY, MAGNET_SIZE, MAGNET_SIZE);
+ noFill();
+ stroke(0, 255, 0);
+ safe_zone_radius *= 0.9995;
+ ellipse(width/2, height/2, safe_zone_radius, safe_zone_radius);
+
+ if ((mouseX > width || mouseY > height) && magnets.length > 0)
+ {
+ gameOver = true;
+ alert("You went out of bounds. Score: " + magnets.length);
+ magnets = [];
+ gameOver = false;
+ safe_zone_radius = 300;
+ }
+
+ if (frameCount % 100 === 0 && mouseX < width && mouseY < height)
+ new Magnet();
+ for (var i = 0; i < magnets.length; i++)
+ {
+ magnets[i].drawIt();
+ }
+}
diff --git a/js/mandelbrot.js b/js/mandelbrot.js
new file mode 100644
index 0000000..cdfc467
--- /dev/null
+++ b/js/mandelbrot.js
@@ -0,0 +1,179 @@
+var increment = 0.01;
+var iterations = 30;
+var startI = -2.5;
+var startJ = -2.5;
+var power = 2;
+var canvas = document.getElementById("canvas");
+var ctx = canvas.getContext("2d");
+var width = canvas.width;
+var height = canvas.height;
+function reciprocal(re, im)
+{
+ return [re/(re*re+im*im), -im/(im*im+re*re)];
+}
+
+function add(re1, im1, re2, im2)
+{
+ return [re1+re2, im1+im2];
+}
+
+function multiply(re1, im1, re2, im2)
+{
+ return [re1*re2-im1*im2, re1*im2+re2*im1];
+}
+
+function cpower(re, im, power)
+{
+ if (power < 0)
+ {
+ var r = reciprocal(re, im);
+ return cpower(r[0], r[1], -power);
+ }
+ var x = [re, im];
+ var i = 1;
+ while (i < power)
+ {
+ x = multiply(x[0], x[1], re, im);
+ i++;
+ }
+ return x;
+
+}
+
+function iterate(z, power, c)
+{
+ var x = cpower(z[0], z[1], power);
+ return add(x[0], x[1], c[0], c[1]);
+}
+
+function cabs(z)
+{
+ return Math.sqrt(z[0]*z[0]+z[1]*z[1]);
+}
+
+function num_iterations(power, c, max_iterations)
+{
+ var iterations = 0;
+ var z = [0, 0];
+ while (iterations < max_iterations && cabs(z) < 2)
+ {
+ z = iterate(z, power, c);
+ iterations++;
+ }
+ return iterations;
+}
+
+function draw_mandelbrot()
+{
+ var size = width*increment;
+
+
+
+ var endI = startI+size;
+ var endJ = startJ+size;
+
+ var imgData = ctx.createImageData(width, height);
+
+ for (var i = startI; i < endI; i+=increment)
+ {
+ for (var j = startJ; j < endJ; j+=increment)
+ {
+ var ipos = i*(1.0/increment)-startI*(1.0/increment);
+ var jpos = j*(1.0/increment)-startJ*(1.0/increment);
+ jpos = Math.floor(Math.round(jpos));
+ ipos = Math.floor(Math.round(ipos));
+
+ var x = num_iterations(power, [i, j], iterations)/iterations * 255
+ imgData.data[4*(jpos*width+ipos)] = x;
+ imgData.data[1+4*(jpos*width+ipos)] = x;
+ imgData.data[2+4*(jpos*width+ipos)] = x;
+ imgData.data[3+4*(jpos*width+ipos)] = 255;
+
+
+ }
+ }
+ ctx.putImageData(imgData, 0, 0);
+}
+
+function draw()
+{
+
+}
+
+function map(val, startA, endA, startB, endB)
+{
+ return (endB-startB)*((val-startA)/(endA-startA))+startB;
+}
+
+function mousePressed(e)
+{
+ var x;
+ var y;
+ if (e.pageX || e.pageY) {
+ x = e.pageX;
+ y = e.pageY;
+ }
+ else {
+ x = e.clientX;
+ y = e.clientY;
+ }
+ x -= canvas.offsetLeft;
+ y -= canvas.offsetTop;
+ if (x < 0 || x > width || y < 0 || y > height)
+ return;
+
+ if (e.button == 0)
+ {
+ increment /= 2;
+ startI = map(x, 0, width, startI, startI+width*increment);
+ startJ = map(y, 0, height, startJ, startJ+width*increment);
+
+ draw_mandelbrot();
+ }
+}
+
+function keyPressed(e)
+{
+
+ var key = String.fromCharCode(e.keyCode);
+ if (key == "A")
+ {
+ increment /= 2;
+ startI = startI+width*increment/2;
+ startJ = startJ+height*increment/2;
+
+ draw_mandelbrot();
+ }
+ if (key == "Q")
+ {
+ increment *= 2;
+ startI = startI-width*increment/2;
+ startJ = startJ-height*increment/2;
+ draw_mandelbrot();
+ }
+ if (key == "P")
+ {
+ power++;
+ draw_mandelbrot();
+ }
+ if (key == "L")
+ {
+ power--;
+ draw_mandelbrot();
+ }
+ if (key == "I")
+ {
+ iterations = Math.floor(iterations*1.5);
+ draw_mandelbrot();
+ }
+ if (key == "K")
+ {
+ iterations = Math.floor(iterations/1.5);
+ draw_mandelbrot();
+ }
+}
+canvas.addEventListener("mousedown", mousePressed);
+document.body.addEventListener("keydown", keyPressed);
+
+draw_mandelbrot();
+ \ No newline at end of file
diff --git a/js/mazesolver.js b/js/mazesolver.js
new file mode 100644
index 0000000..aa43ed3
--- /dev/null
+++ b/js/mazesolver.js
@@ -0,0 +1,457 @@
+
+var canvas = document.getElementById('Canvas');
+var form = document.getElementById('Form');
+
+canvas.onmousedown = function(e) { if (e.button === 1) return false; }
+
+var X;
+var Y;
+var TILEWIDTH;
+
+var started = false;
+
+var tiles = [];
+for (var i = 0; i < Y; i++)
+{
+ tiles.push([]);
+ for (var j = 0; j < X; j++)
+ tiles[i].push(false);
+}
+
+var tilesToGoal = [];
+for (var i = 0; i < Y; i++)
+{
+ tilesToGoal.push([]);
+ for (var j = 0; j < X; j++)
+ tilesToGoal[i].push(false);
+}
+
+
+var ctx = canvas.getContext('2d');
+
+var mouseDown = false;
+
+var startPlaced = false;
+var endPlaced = false;
+var begun = false;
+
+var start; //(location)
+var end; //(location)
+
+var doesItWorkParagraph = document.getElementById('DoesItWork');
+
+function reset()
+{
+ TILEWIDTH = Math.ceil(canvas.width / X);
+ if (canvas.width !== TILEWIDTH * X)
+ canvas.width = TILEWIDTH * X;
+ if (canvas.height !== TILEWIDTH * X)
+ canvas.height = TILEWIDTH * X;
+ startPlaced = false;
+ endPlaced = false;
+ begun = false;
+ start = [];
+ end = [];
+ tiles = [];
+ for (var i = 0; i < Y; i++)
+ {
+ tiles.push([]);
+ for (var j = 0; j < X; j++)
+ tiles[i].push(false);
+ }
+
+ tilesToGoal = [];
+ for (var i = 0; i < Y; i++)
+ {
+ tilesToGoal.push([]);
+ for (var j = 0; j < X; j++)
+ tilesToGoal[i].push(false);
+ }
+}
+
+function maxIndex(l)
+{
+ if (l === [])
+ return -1;
+
+ var highest = l[0];
+ var highestindex = 0;
+
+ for (var i = 1; i < l.length; i++)
+ if (l[i] > highest)
+ {
+ highest = l[i];
+ highestindex = i;
+ }
+ return highestindex;
+}
+
+function to1d(l)
+{
+ //Turns a 2d array into a 1d array
+ var newl = [];
+ for (var y = 0; y < l.length; y++)
+ for (var x = 0; x < l[y].length; x++)
+ newl.push(l[y][x]);
+ return newl;
+}
+
+function equals2d(array1, array2)
+{
+ return equals(to1d(array1), to1d(array2));
+}
+function maxValue(l)
+{
+ if (l === [])
+ return -1;
+
+ var highest = l[0];
+
+ for (var i = 1; i < l.length; i++)
+ if (l[i] > highest)
+ highest = l[i];
+
+ return highest;
+}
+
+
+
+function copy2d(array)
+{
+ var newarray = [];
+ for (var i = 0; i < array.length; i++)
+ newarray.push(array[i].slice())
+
+ return newarray;
+}
+
+function equals(array1, array2)
+{
+ for (var i = 0; i < array1.length; i++)
+ if (array1[i] !== array2[i])
+ return false;
+ return true;
+}
+
+function index2d(array, x)
+{
+ for (var i = 0; i < array.length; i++)
+ if (equals(array[i], x))
+ return i;
+ return -1;
+}
+
+function rmvArray(array2d, array)
+{
+ var newarray = [];
+ for (var i = 0; i < array2d.length; i++)
+ if (!(equals(array2d[i], array)))
+ newarray.push(array2d[i]);
+
+ return newarray;
+}
+
+function getTilesToGoal()
+{
+
+ for (var y = 0; y < tiles.length; y++)
+ for (var x = 0; x < tiles[y].length; x++)
+ tilesToGoal[y][x] = tiles[y][x] ? -1 : -2
+
+
+
+ var oldTTG;
+ var highestValue;
+ var surroundingTiles;
+
+ tilesToGoal[end[1]][end[0]] = 0;
+ var list = [[1, 2], [0, 0], [4, 5]];
+ var n = 0;
+
+ do
+ {
+ n++;
+ oldTTG = copy2d(tilesToGoal);
+ highestValue = maxValue(to1d(tilesToGoal));
+
+ for (var y = 0; y < tilesToGoal.length; y++)
+
+ for (var x = 0; x < tilesToGoal[y].length; x++)
+
+ if (tilesToGoal[y][x] === highestValue)
+ {
+
+ surroundingTiles = [[x+1, y], [x, y+1], [x-1, y], [x, y-1]];
+ if (y === tilesToGoal.length - 1) surroundingTiles = rmvArray(surroundingTiles, [x, y+1]);
+ if (y === 0) surroundingTiles = rmvArray(surroundingTiles, [x, y-1]);
+ if (x === tilesToGoal[y].length - 1) surroundingTiles = rmvArray(surroundingTiles, [x+1, y]);
+ if (x === 0) surroundingTiles = rmvArray(surroundingTiles, [x-1, y]);
+
+ for (var i = 0; i < surroundingTiles.length; i++)
+ {
+ if ((!tiles[surroundingTiles[i][1]][surroundingTiles[i][0]]) && (tilesToGoal[surroundingTiles[i][1]][surroundingTiles[i][0]] < 0))
+ tilesToGoal[surroundingTiles[i][1]][surroundingTiles[i][0]] = highestValue + 1
+ }
+ }
+ if (tilesToGoal[start[1]][start[0]] > 0)
+ break;
+ }
+ while (!(equals2d(tilesToGoal, oldTTG)));
+
+
+
+ /*for (var y = 0; y < tilesToGoal.length; y++)
+ {
+ for (var x = 0; x < tilesToGoal[y].length; x++)
+ document.write(tilesToGoal[y][x] + ' ');
+
+ document.write('<br>');
+ }*/
+
+
+ if (tilesToGoal[start[1]][start[0]] < 0)
+ return false;
+
+ return true;
+}
+
+function remove(element)
+{
+ element.parentNode.removeChild(element);
+}
+
+function startCreation()
+{
+ var size = form.elements[0].value;
+ X = size;
+ Y = size;
+ var button = document.getElementById('StartButton');
+ button.innerHTML = 'Solve Maze';
+ button.onclick = function(){try{begin();}catch(err){document.write(err)}};
+ remove(form);
+ canvas.width = 500;
+ canvas.height = 500;
+ reset();
+ clear();
+
+}
+
+function begin()
+{
+
+ if (!(startPlaced && endPlaced))
+ {
+ doesItWorkParagraph.innerHTML = 'You must choose a start and end location (right-click).';
+ reset();
+ clear();
+ return;
+ }
+
+
+ var mazeWorks = getTilesToGoal();
+ if (mazeWorks === false)
+ {
+ doesItWorkParagraph.innerHTML = 'Maze cannot be solved.';
+ reset();
+ clear();
+ return;
+ }
+
+ started = true;
+
+ var location = start;
+ var x;
+ var y;
+ var surroundingTiles;
+
+ while (!(equals(location, end)))
+ {
+ x = location[0];
+ y = location[1];
+
+ circle(x * TILEWIDTH + TILEWIDTH / 2, y * TILEWIDTH + TILEWIDTH / 2, parseInt(TILEWIDTH / 2.5), '#ffaaaa');
+ surroundingTiles = [[x+1, y], [x, y+1], [x-1, y], [x, y-1]];
+ if (y === tilesToGoal.length - 1) surroundingTiles = rmvArray(surroundingTiles, [x, y+1]);
+ if (y === 0) surroundingTiles = rmvArray(surroundingTiles, [x, y-1]);
+ if (x === tilesToGoal[y].length - 1) surroundingTiles = rmvArray(surroundingTiles, [x+1, y]);
+ if (x === 0) surroundingTiles = rmvArray(surroundingTiles, [x-1, y]);
+
+ for (var i = 0; i < surroundingTiles.length; i++)
+ if ((tilesToGoal[surroundingTiles[i][1]][surroundingTiles[i][0]] < tilesToGoal[y][x]) && tilesToGoal[surroundingTiles[i][1]][surroundingTiles[i][0]] >= 0)
+ {
+ location = [surroundingTiles[i][0], surroundingTiles[i][1]];
+ break; //(inner for loop)
+ }
+
+ }
+
+ x = location[0];
+ y = location[1];
+
+ circle(x * TILEWIDTH + TILEWIDTH / 2, y * TILEWIDTH + TILEWIDTH / 2, parseInt(TILEWIDTH / 2.5), '#ffaaaa');
+
+ reset();
+
+}
+
+
+
+
+function mouseMoved(event)
+{
+
+ if ((!mouseDown) || started)
+ return;
+
+ var button = -1;
+
+ if ("which" in event)
+ if (event.which == 2) button = 1; else if (event.which == 3) button = 2; else button = 0;
+ else if ("button" in event)
+ button = event.button;
+ else
+ button = 0;
+
+
+ var x = event.offsetX;
+ var y = event.offsetY;
+
+ var tilex = Math.floor(x / TILEWIDTH);
+ var tiley = Math.floor(y / TILEWIDTH);
+
+ if (button === 1)
+ {
+ rect(tilex * TILEWIDTH, tiley * TILEWIDTH, TILEWIDTH, TILEWIDTH, '#dddddd');
+ tiles[tiley][tilex] = false;
+ if (equals([tilex, tiley], start))
+ {
+ start = [];
+ startPlaced = false;
+ }
+ if (equals([tilex, tiley], end))
+ {
+ end = [];
+ endPlaced = false;
+ }
+ return;
+ }
+ else if (button === 2)
+ return;
+
+ var notClickedOnStart = (!startPlaced) || (!((tilex === start[0]) && (tiley === start[1])));
+
+ var notClickedOnEnd = (!endPlaced) || (!((tilex === end[0]) && (tiley === end[1])));
+
+ if (notClickedOnStart && notClickedOnEnd)
+ {
+ rect(tilex * TILEWIDTH, tiley * TILEWIDTH, TILEWIDTH, TILEWIDTH, '#aaaaaa');
+ tiles[tiley][tilex] = true;
+ }
+
+}
+
+function clear()
+{
+ rect(0, 0, canvas.width, canvas.height, '#dddddd');
+ started = false;
+}
+
+function mousePressed(event)
+{
+
+ if (started)
+ {
+ clear();
+ return;
+ }
+
+ var button = -1;
+
+ if ("which" in event)
+ if (event.which == 2) button = 1; else if (event.which == 3) button = 2; else button = 0;
+ else if ("button" in event)
+ button = event.button;
+ else
+ button = 0;
+
+ if (button !== 2)
+ {
+ mouseDown = true;
+ mouseMoved(event);
+ }
+ else
+ {
+ var x = event.offsetX;
+ var y = event.offsetY;
+
+ var tilex = Math.floor(x / TILEWIDTH);
+ var tiley = Math.floor(y / TILEWIDTH);
+ if ((!startPlaced) && (!tiles[tiley][tilex]))
+ {
+ start = [tilex, tiley];
+ rect(tilex * TILEWIDTH, tiley * TILEWIDTH, TILEWIDTH, TILEWIDTH, '#00ff00');
+ startPlaced = true;
+ }
+ else if ((!endPlaced) && (!tiles[tiley][tilex]))
+ {
+ end = [tilex, tiley];
+ rect(tilex * TILEWIDTH, tiley * TILEWIDTH, TILEWIDTH, TILEWIDTH, '#ffff00');
+ endPlaced = true;
+ return;
+ }
+ }
+}
+
+function mouseReleased(event)
+{
+ mouseDown = false;
+}
+
+
+
+canvas.addEventListener('mousemove', mouseMoved, false);
+canvas.addEventListener('mousedown', mousePressed, false);
+canvas.addEventListener('mouseup', mouseReleased, false);
+
+function circle(x, y, r, colour)
+{
+ ctx.fillStyle = colour;
+ ctx.beginPath();
+ ctx.arc(x, y, r, 0, 2*Math.PI);
+ ctx.fill();
+};
+
+
+function rect(x, y, w, h, colour)
+{
+ ctx.fillStyle = colour;
+ ctx.fillRect(x, y, w, h);
+};
+
+
+function text(str, x, y)
+{
+ ctx.font = "20px Helvetica";
+ ctx.fillStyle = '#000000';
+ ctx.fillText(str, x, y);
+}
+
+function line(x1, y1, x2, y2)
+{
+ ctx.strokeStyle = '#000000';
+ ctx.beginPath();
+ ctx.moveTo(x1, y1);
+ ctx.lineTo(x2, y2);
+ ctx.stroke();
+};
+
+function line(x1, y1, x2, y2, color)
+{
+ ctx.strokeStyle = color
+ ctx.beginPath();
+ ctx.moveTo(x1, y1);
+ ctx.lineTo(x2, y2);
+ ctx.stroke();
+};
+
+rect(0, 0, canvas.width, canvas.height, '#dddddd');
diff --git a/js/modularcircles.js b/js/modularcircles.js
new file mode 100644
index 0000000..8b588f9
--- /dev/null
+++ b/js/modularcircles.js
@@ -0,0 +1,54 @@
+function setup()
+{
+ createCanvas(600, 600);
+}
+
+function nPoints()
+{
+ return document.getElementById("npoints").value;
+}
+
+function shouldMul()
+{
+ return document.getElementById("should_mul").checked;
+}
+
+function amount()
+{
+ return parseFloat(document.getElementById("amount").value);
+}
+
+function getPos(number)
+{
+ angle = 2*PI * number/(nPoints());
+ return [cos(angle)*250+300, sin(angle)*250+300];
+}
+
+function draw()
+{
+ if (shouldMul())
+ document.getElementById("amount").step = 0.1;
+ else
+ document.getElementById("amount").step = 1;
+ background(255);
+ ellipseMode(CENTER);
+ noStroke();
+ fill(0);
+ for (var i = 0; i < nPoints(); i++)
+ ellipse(getPos(i)[0], getPos(i)[1], 3, 3);
+
+ stroke(0);
+ for (var i = 0; i < nPoints(); i++)
+ {
+ if (shouldMul())
+ {
+ stroke(map(map((amount()*i)%nPoints(), 0, nPoints(), 0, 256) - map(i, 0, nPoints(), 0, 256), -256, 256, 0, 256), map(i, 0, nPoints(), 0, 256), map((amount()*i)%nPoints(), 0, nPoints(), 0, 256));
+ line(getPos(i)[0], getPos(i)[1], getPos((amount()*i)%nPoints())[0], getPos((amount()*i)%nPoints())[1]);
+ }
+ else
+ {
+ stroke(0, map(i, 0, nPoints(), 0, 256), map((amount()+i)%nPoints(), 0, nPoints(), 0, 256));
+ line(getPos(i)[0], getPos(i)[1], getPos((amount()+i)%nPoints())[0], getPos((amount()+i)%nPoints())[1]);
+ }
+ }
+} \ No newline at end of file
diff --git a/js/modularpascal.js b/js/modularpascal.js
new file mode 100644
index 0000000..80f65d3
--- /dev/null
+++ b/js/modularpascal.js
@@ -0,0 +1,55 @@
+var triangle = [];
+var colors = [];
+var currentX;
+function getX()
+{
+ return document.getElementById("mod").value;
+}
+
+
+
+function updateTriangle()
+{
+ var x = getX();
+ triangle = [];
+ currentX = x;
+ for (var i = 0; i < height/2; i++)
+ {
+ triangle.push([]);
+ triangle[i].push(1);
+ for (var j = 1; j < i; j++)
+ triangle[i].push((triangle[i-1][j-1]+triangle[i-1][j])%x);
+ triangle[i].push(1);
+ }
+ colors = [];
+ for (var i = 0; i < x; i++)
+ colors.push([random(255), random(255), random(255)]);
+ background(255);
+ noStroke();
+ for (var i = 0; i < height/2; i++)
+ {
+ for (var j = 0; j <= i; j++)
+ {
+ fill(colors[triangle[i][j]][0], colors[triangle[i][j]][1], colors[triangle[i][j]][2]);
+ rect(getPos(i, j)[0], getPos(i, j)[1], 2, 2);
+
+ }
+ }
+
+}
+
+function setup()
+{
+ createCanvas(512, 512);
+ updateTriangle();
+}
+
+function getPos(row, column)
+{
+ return [width/2 - row + 2 * column, row*2];
+}
+
+function draw()
+{
+
+}
diff --git a/js/p5.js b/js/p5.js
new file mode 100644
index 0000000..43d7136
--- /dev/null
+++ b/js/p5.js
@@ -0,0 +1,29765 @@
+/*! p5.js v0.4.23 March 04, 2016 */
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.p5 = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
+
+},{}],2:[function(_dereq_,module,exports){
+// Run-time checking of preconditions.
+
+'use strict';
+
+// Precondition function that checks if the given predicate is true.
+// If not, it will throw an error.
+exports.argument = function(predicate, message) {
+ if (!predicate) {
+ throw new Error(message);
+ }
+};
+
+// Precondition function that checks if the given assertion is true.
+// If not, it will throw an error.
+exports.assert = exports.argument;
+
+},{}],3:[function(_dereq_,module,exports){
+// Drawing utility functions.
+
+'use strict';
+
+// Draw a line on the given context from point `x1,y1` to point `x2,y2`.
+function line(ctx, x1, y1, x2, y2) {
+ ctx.beginPath();
+ ctx.moveTo(x1, y1);
+ ctx.lineTo(x2, y2);
+ ctx.stroke();
+}
+
+exports.line = line;
+
+},{}],4:[function(_dereq_,module,exports){
+// Glyph encoding
+
+'use strict';
+
+var cffStandardStrings = [
+ '.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright',
+ 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two',
+ 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater',
+ 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
+ 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore',
+ 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+ 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', 'exclamdown', 'cent', 'sterling',
+ 'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
+ 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger', 'daggerdbl', 'periodcentered', 'paragraph',
+ 'bullet', 'quotesinglbase', 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
+ 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', 'ring',
+ 'cedilla', 'hungarumlaut', 'ogonek', 'caron', 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE',
+ 'ordmasculine', 'ae', 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior', 'logicalnot', 'mu',
+ 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn', 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn',
+ 'threequarters', 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior', 'copyright',
+ 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring', 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex',
+ 'Edieresis', 'Egrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute', 'Ocircumflex',
+ 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute', 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute',
+ 'Ydieresis', 'Zcaron', 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla', 'eacute',
+ 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex', 'idieresis', 'igrave', 'ntilde', 'oacute',
+ 'ocircumflex', 'odieresis', 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
+ 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall', 'dollaroldstyle', 'dollarsuperior',
+ 'ampersandsmall', 'Acutesmall', 'parenleftsuperior', 'parenrightsuperior', '266 ff', 'onedotenleader',
+ 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle',
+ 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
+ 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior',
+ 'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl',
+ 'parenleftinferior', 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 'Asmall',
+ 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall',
+ 'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall',
+ 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', 'exclamdownsmall',
+ 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall', 'Brevesmall', 'Caronsmall',
+ 'Dotaccentsmall', 'Macronsmall', 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
+ 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds',
+ 'zerosuperior', 'foursuperior', 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior',
+ 'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior',
+ 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior',
+ 'commainferior', 'Agravesmall', 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall',
+ 'Aringsmall', 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 'Edieresissmall',
+ 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall',
+ 'Oacutesmall', 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall',
+ 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000',
+ '001.001', '001.002', '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman', 'Semibold'];
+
+var cffStandardEncoding = [
+ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
+ '', '', '', '', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright',
+ 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two',
+ 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater',
+ 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
+ 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore',
+ 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+ 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', '', '', '', '', '', '', '', '',
+ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
+ 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle',
+ 'quotedblleft', 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', '', 'endash', 'dagger',
+ 'daggerdbl', 'periodcentered', '', 'paragraph', 'bullet', 'quotesinglbase', 'quotedblbase', 'quotedblright',
+ 'guillemotright', 'ellipsis', 'perthousand', '', 'questiondown', '', 'grave', 'acute', 'circumflex', 'tilde',
+ 'macron', 'breve', 'dotaccent', 'dieresis', '', 'ring', 'cedilla', '', 'hungarumlaut', 'ogonek', 'caron',
+ 'emdash', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'AE', '', 'ordfeminine', '', '', '',
+ '', 'Lslash', 'Oslash', 'OE', 'ordmasculine', '', '', '', '', '', 'ae', '', '', '', 'dotlessi', '', '',
+ 'lslash', 'oslash', 'oe', 'germandbls'];
+
+var cffExpertEncoding = [
+ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
+ '', '', '', '', 'space', 'exclamsmall', 'Hungarumlautsmall', '', 'dollaroldstyle', 'dollarsuperior',
+ 'ampersandsmall', 'Acutesmall', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
+ 'comma', 'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle',
+ 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'colon',
+ 'semicolon', 'commasuperior', 'threequartersemdash', 'periodsuperior', 'questionsmall', '', 'asuperior',
+ 'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', '', '', 'isuperior', '', '', 'lsuperior', 'msuperior',
+ 'nsuperior', 'osuperior', '', '', 'rsuperior', 'ssuperior', 'tsuperior', '', 'ff', 'fi', 'fl', 'ffi', 'ffl',
+ 'parenleftinferior', '', 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 'Asmall',
+ 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall',
+ 'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall',
+ 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', '', '', '', '', '', '', '',
+ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
+ 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', '', '', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall',
+ 'Brevesmall', 'Caronsmall', '', 'Dotaccentsmall', '', '', 'Macronsmall', '', '', 'figuredash', 'hypheninferior',
+ '', '', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall', '', '', '', 'onequarter', 'onehalf', 'threequarters',
+ 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds', '',
+ '', 'zerosuperior', 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior',
+ 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior',
+ 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior', 'eightinferior',
+ 'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
+ 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall', 'AEsmall', 'Ccedillasmall',
+ 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall',
+ 'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
+ 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall', 'Uacutesmall',
+ 'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall', 'Ydieresissmall'];
+
+var standardNames = [
+ '.notdef', '.null', 'nonmarkingreturn', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent',
+ 'ampersand', 'quotesingle', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash',
+ 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less',
+ 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright',
+ 'asciicircum', 'underscore', 'grave', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde',
+ 'Adieresis', 'Aring', 'Ccedilla', 'Eacute', 'Ntilde', 'Odieresis', 'Udieresis', 'aacute', 'agrave',
+ 'acircumflex', 'adieresis', 'atilde', 'aring', 'ccedilla', 'eacute', 'egrave', 'ecircumflex', 'edieresis',
+ 'iacute', 'igrave', 'icircumflex', 'idieresis', 'ntilde', 'oacute', 'ograve', 'ocircumflex', 'odieresis',
+ 'otilde', 'uacute', 'ugrave', 'ucircumflex', 'udieresis', 'dagger', 'degree', 'cent', 'sterling', 'section',
+ 'bullet', 'paragraph', 'germandbls', 'registered', 'copyright', 'trademark', 'acute', 'dieresis', 'notequal',
+ 'AE', 'Oslash', 'infinity', 'plusminus', 'lessequal', 'greaterequal', 'yen', 'mu', 'partialdiff', 'summation',
+ 'product', 'pi', 'integral', 'ordfeminine', 'ordmasculine', 'Omega', 'ae', 'oslash', 'questiondown',
+ 'exclamdown', 'logicalnot', 'radical', 'florin', 'approxequal', 'Delta', 'guillemotleft', 'guillemotright',
+ 'ellipsis', 'nonbreakingspace', 'Agrave', 'Atilde', 'Otilde', 'OE', 'oe', 'endash', 'emdash', 'quotedblleft',
+ 'quotedblright', 'quoteleft', 'quoteright', 'divide', 'lozenge', 'ydieresis', 'Ydieresis', 'fraction',
+ 'currency', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'daggerdbl', 'periodcentered', 'quotesinglbase',
+ 'quotedblbase', 'perthousand', 'Acircumflex', 'Ecircumflex', 'Aacute', 'Edieresis', 'Egrave', 'Iacute',
+ 'Icircumflex', 'Idieresis', 'Igrave', 'Oacute', 'Ocircumflex', 'apple', 'Ograve', 'Uacute', 'Ucircumflex',
+ 'Ugrave', 'dotlessi', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'ring', 'cedilla', 'hungarumlaut',
+ 'ogonek', 'caron', 'Lslash', 'lslash', 'Scaron', 'scaron', 'Zcaron', 'zcaron', 'brokenbar', 'Eth', 'eth',
+ 'Yacute', 'yacute', 'Thorn', 'thorn', 'minus', 'multiply', 'onesuperior', 'twosuperior', 'threesuperior',
+ 'onehalf', 'onequarter', 'threequarters', 'franc', 'Gbreve', 'gbreve', 'Idotaccent', 'Scedilla', 'scedilla',
+ 'Cacute', 'cacute', 'Ccaron', 'ccaron', 'dcroat'];
+
+// This is the encoding used for fonts created from scratch.
+// It loops through all glyphs and finds the appropriate unicode value.
+// Since it's linear time, other encodings will be faster.
+function DefaultEncoding(font) {
+ this.font = font;
+}
+
+DefaultEncoding.prototype.charToGlyphIndex = function(c) {
+ var code = c.charCodeAt(0);
+ var glyphs = this.font.glyphs;
+ if (glyphs) {
+ for (var i = 0; i < glyphs.length; i += 1) {
+ var glyph = glyphs.get(i);
+ for (var j = 0; j < glyph.unicodes.length; j += 1) {
+ if (glyph.unicodes[j] === code) {
+ return i;
+ }
+ }
+ }
+ } else {
+ return null;
+ }
+};
+
+function CmapEncoding(cmap) {
+ this.cmap = cmap;
+}
+
+CmapEncoding.prototype.charToGlyphIndex = function(c) {
+ return this.cmap.glyphIndexMap[c.charCodeAt(0)] || 0;
+};
+
+function CffEncoding(encoding, charset) {
+ this.encoding = encoding;
+ this.charset = charset;
+}
+
+CffEncoding.prototype.charToGlyphIndex = function(s) {
+ var code = s.charCodeAt(0);
+ var charName = this.encoding[code];
+ return this.charset.indexOf(charName);
+};
+
+function GlyphNames(post) {
+ var i;
+ switch (post.version) {
+ case 1:
+ this.names = exports.standardNames.slice();
+ break;
+ case 2:
+ this.names = new Array(post.numberOfGlyphs);
+ for (i = 0; i < post.numberOfGlyphs; i++) {
+ if (post.glyphNameIndex[i] < exports.standardNames.length) {
+ this.names[i] = exports.standardNames[post.glyphNameIndex[i]];
+ } else {
+ this.names[i] = post.names[post.glyphNameIndex[i] - exports.standardNames.length];
+ }
+ }
+
+ break;
+ case 2.5:
+ this.names = new Array(post.numberOfGlyphs);
+ for (i = 0; i < post.numberOfGlyphs; i++) {
+ this.names[i] = exports.standardNames[i + post.glyphNameIndex[i]];
+ }
+
+ break;
+ case 3:
+ this.names = [];
+ break;
+ }
+}
+
+GlyphNames.prototype.nameToGlyphIndex = function(name) {
+ return this.names.indexOf(name);
+};
+
+GlyphNames.prototype.glyphIndexToName = function(gid) {
+ return this.names[gid];
+};
+
+function addGlyphNames(font) {
+ var glyph;
+ var glyphIndexMap = font.tables.cmap.glyphIndexMap;
+ var charCodes = Object.keys(glyphIndexMap);
+
+ for (var i = 0; i < charCodes.length; i += 1) {
+ var c = charCodes[i];
+ var glyphIndex = glyphIndexMap[c];
+ glyph = font.glyphs.get(glyphIndex);
+ glyph.addUnicode(parseInt(c));
+ }
+
+ for (i = 0; i < font.glyphs.length; i += 1) {
+ glyph = font.glyphs.get(i);
+ if (font.cffEncoding) {
+ glyph.name = font.cffEncoding.charset[i];
+ } else {
+ glyph.name = font.glyphNames.glyphIndexToName(i);
+ }
+ }
+}
+
+exports.cffStandardStrings = cffStandardStrings;
+exports.cffStandardEncoding = cffStandardEncoding;
+exports.cffExpertEncoding = cffExpertEncoding;
+exports.standardNames = standardNames;
+exports.DefaultEncoding = DefaultEncoding;
+exports.CmapEncoding = CmapEncoding;
+exports.CffEncoding = CffEncoding;
+exports.GlyphNames = GlyphNames;
+exports.addGlyphNames = addGlyphNames;
+
+},{}],5:[function(_dereq_,module,exports){
+// The Font object
+
+'use strict';
+
+var path = _dereq_('./path');
+var sfnt = _dereq_('./tables/sfnt');
+var encoding = _dereq_('./encoding');
+var glyphset = _dereq_('./glyphset');
+
+// A Font represents a loaded OpenType font file.
+// It contains a set of glyphs and methods to draw text on a drawing context,
+// or to get a path representing the text.
+function Font(options) {
+ options = options || {};
+
+ // OS X will complain if the names are empty, so we put a single space everywhere by default.
+ this.familyName = options.familyName || ' ';
+ this.styleName = options.styleName || ' ';
+ this.designer = options.designer || ' ';
+ this.designerURL = options.designerURL || ' ';
+ this.manufacturer = options.manufacturer || ' ';
+ this.manufacturerURL = options.manufacturerURL || ' ';
+ this.license = options.license || ' ';
+ this.licenseURL = options.licenseURL || ' ';
+ this.version = options.version || 'Version 0.1';
+ this.description = options.description || ' ';
+ this.copyright = options.copyright || ' ';
+ this.trademark = options.trademark || ' ';
+ this.unitsPerEm = options.unitsPerEm || 1000;
+ this.ascender = options.ascender;
+ this.descender = options.descender;
+ this.supported = true;
+ this.glyphs = new glyphset.GlyphSet(this, options.glyphs || []);
+ this.encoding = new encoding.DefaultEncoding(this);
+ this.tables = {};
+}
+
+// Check if the font has a glyph for the given character.
+Font.prototype.hasChar = function(c) {
+ return this.encoding.charToGlyphIndex(c) !== null;
+};
+
+// Convert the given character to a single glyph index.
+// Note that this function assumes that there is a one-to-one mapping between
+// the given character and a glyph; for complex scripts this might not be the case.
+Font.prototype.charToGlyphIndex = function(s) {
+ return this.encoding.charToGlyphIndex(s);
+};
+
+// Convert the given character to a single Glyph object.
+// Note that this function assumes that there is a one-to-one mapping between
+// the given character and a glyph; for complex scripts this might not be the case.
+Font.prototype.charToGlyph = function(c) {
+ var glyphIndex = this.charToGlyphIndex(c);
+ var glyph = this.glyphs.get(glyphIndex);
+ if (!glyph) {
+ // .notdef
+ glyph = this.glyphs.get(0);
+ }
+
+ return glyph;
+};
+
+// Convert the given text to a list of Glyph objects.
+// Note that there is no strict one-to-one mapping between characters and
+// glyphs, so the list of returned glyphs can be larger or smaller than the
+// length of the given string.
+Font.prototype.stringToGlyphs = function(s) {
+ var glyphs = [];
+ for (var i = 0; i < s.length; i += 1) {
+ var c = s[i];
+ glyphs.push(this.charToGlyph(c));
+ }
+
+ return glyphs;
+};
+
+Font.prototype.nameToGlyphIndex = function(name) {
+ return this.glyphNames.nameToGlyphIndex(name);
+};
+
+Font.prototype.nameToGlyph = function(name) {
+ var glyphIndex = this.nametoGlyphIndex(name);
+ var glyph = this.glyphs.get(glyphIndex);
+ if (!glyph) {
+ // .notdef
+ glyph = this.glyphs.get(0);
+ }
+
+ return glyph;
+};
+
+Font.prototype.glyphIndexToName = function(gid) {
+ if (!this.glyphNames.glyphIndexToName) {
+ return '';
+ }
+
+ return this.glyphNames.glyphIndexToName(gid);
+};
+
+// Retrieve the value of the kerning pair between the left glyph (or its index)
+// and the right glyph (or its index). If no kerning pair is found, return 0.
+// The kerning value gets added to the advance width when calculating the spacing
+// between glyphs.
+Font.prototype.getKerningValue = function(leftGlyph, rightGlyph) {
+ leftGlyph = leftGlyph.index || leftGlyph;
+ rightGlyph = rightGlyph.index || rightGlyph;
+ var gposKerning = this.getGposKerningValue;
+ return gposKerning ? gposKerning(leftGlyph, rightGlyph) :
+ (this.kerningPairs[leftGlyph + ',' + rightGlyph] || 0);
+};
+
+// Helper function that invokes the given callback for each glyph in the given text.
+// The callback gets `(glyph, x, y, fontSize, options)`.
+Font.prototype.forEachGlyph = function(text, x, y, fontSize, options, callback) {
+ if (!this.supported) {
+ return;
+ }
+
+ x = x !== undefined ? x : 0;
+ y = y !== undefined ? y : 0;
+ fontSize = fontSize !== undefined ? fontSize : 72;
+ options = options || {};
+ var kerning = options.kerning === undefined ? true : options.kerning;
+ var fontScale = 1 / this.unitsPerEm * fontSize;
+ var glyphs = this.stringToGlyphs(text);
+ for (var i = 0; i < glyphs.length; i += 1) {
+ var glyph = glyphs[i];
+ callback(glyph, x, y, fontSize, options);
+ if (glyph.advanceWidth) {
+ x += glyph.advanceWidth * fontScale;
+ }
+
+ if (kerning && i < glyphs.length - 1) {
+ var kerningValue = this.getKerningValue(glyph, glyphs[i + 1]);
+ x += kerningValue * fontScale;
+ }
+ }
+};
+
+// Create a Path object that represents the given text.
+//
+// text - The text to create.
+// x - Horizontal position of the beginning of the text. (default: 0)
+// y - Vertical position of the *baseline* of the text. (default: 0)
+// fontSize - Font size in pixels. We scale the glyph units by `1 / unitsPerEm * fontSize`. (default: 72)
+// Options is an optional object that contains:
+// - kerning - Whether to take kerning information into account. (default: true)
+//
+// Returns a Path object.
+Font.prototype.getPath = function(text, x, y, fontSize, options) {
+ var fullPath = new path.Path();
+ this.forEachGlyph(text, x, y, fontSize, options, function(glyph, gX, gY, gFontSize) {
+ var glyphPath = glyph.getPath(gX, gY, gFontSize);
+ fullPath.extend(glyphPath);
+ });
+
+ return fullPath;
+};
+
+// Draw the text on the given drawing context.
+//
+// ctx - A 2D drawing context, like Canvas.
+// text - The text to create.
+// x - Horizontal position of the beginning of the text. (default: 0)
+// y - Vertical position of the *baseline* of the text. (default: 0)
+// fontSize - Font size in pixels. We scale the glyph units by `1 / unitsPerEm * fontSize`. (default: 72)
+// Options is an optional object that contains:
+// - kerning - Whether to take kerning information into account. (default: true)
+Font.prototype.draw = function(ctx, text, x, y, fontSize, options) {
+ this.getPath(text, x, y, fontSize, options).draw(ctx);
+};
+
+// Draw the points of all glyphs in the text.
+// On-curve points will be drawn in blue, off-curve points will be drawn in red.
+//
+// ctx - A 2D drawing context, like Canvas.
+// text - The text to create.
+// x - Horizontal position of the beginning of the text. (default: 0)
+// y - Vertical position of the *baseline* of the text. (default: 0)
+// fontSize - Font size in pixels. We scale the glyph units by `1 / unitsPerEm * fontSize`. (default: 72)
+// Options is an optional object that contains:
+// - kerning - Whether to take kerning information into account. (default: true)
+Font.prototype.drawPoints = function(ctx, text, x, y, fontSize, options) {
+ this.forEachGlyph(text, x, y, fontSize, options, function(glyph, gX, gY, gFontSize) {
+ glyph.drawPoints(ctx, gX, gY, gFontSize);
+ });
+};
+
+// Draw lines indicating important font measurements for all glyphs in the text.
+// Black lines indicate the origin of the coordinate system (point 0,0).
+// Blue lines indicate the glyph bounding box.
+// Green line indicates the advance width of the glyph.
+//
+// ctx - A 2D drawing context, like Canvas.
+// text - The text to create.
+// x - Horizontal position of the beginning of the text. (default: 0)
+// y - Vertical position of the *baseline* of the text. (default: 0)
+// fontSize - Font size in pixels. We scale the glyph units by `1 / unitsPerEm * fontSize`. (default: 72)
+// Options is an optional object that contains:
+// - kerning - Whether to take kerning information into account. (default: true)
+Font.prototype.drawMetrics = function(ctx, text, x, y, fontSize, options) {
+ this.forEachGlyph(text, x, y, fontSize, options, function(glyph, gX, gY, gFontSize) {
+ glyph.drawMetrics(ctx, gX, gY, gFontSize);
+ });
+};
+
+// Validate
+Font.prototype.validate = function() {
+ var warnings = [];
+ var _this = this;
+
+ function assert(predicate, message) {
+ if (!predicate) {
+ warnings.push(message);
+ }
+ }
+
+ function assertStringAttribute(attrName) {
+ assert(_this[attrName] && _this[attrName].trim().length > 0, 'No ' + attrName + ' specified.');
+ }
+
+ // Identification information
+ assertStringAttribute('familyName');
+ assertStringAttribute('weightName');
+ assertStringAttribute('manufacturer');
+ assertStringAttribute('copyright');
+ assertStringAttribute('version');
+
+ // Dimension information
+ assert(this.unitsPerEm > 0, 'No unitsPerEm specified.');
+};
+
+// Convert the font object to a SFNT data structure.
+// This structure contains all the necessary tables and metadata to create a binary OTF file.
+Font.prototype.toTables = function() {
+ return sfnt.fontToTable(this);
+};
+
+Font.prototype.toBuffer = function() {
+ var sfntTable = this.toTables();
+ var bytes = sfntTable.encode();
+ var buffer = new ArrayBuffer(bytes.length);
+ var intArray = new Uint8Array(buffer);
+ for (var i = 0; i < bytes.length; i++) {
+ intArray[i] = bytes[i];
+ }
+
+ return buffer;
+};
+
+// Initiate a download of the OpenType font.
+Font.prototype.download = function() {
+ var fileName = this.familyName.replace(/\s/g, '') + '-' + this.styleName + '.otf';
+ var buffer = this.toBuffer();
+
+ window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
+ window.requestFileSystem(window.TEMPORARY, buffer.byteLength, function(fs) {
+ fs.root.getFile(fileName, {create: true}, function(fileEntry) {
+ fileEntry.createWriter(function(writer) {
+ var dataView = new DataView(buffer);
+ var blob = new Blob([dataView], {type: 'font/opentype'});
+ writer.write(blob);
+
+ writer.addEventListener('writeend', function() {
+ // Navigating to the file will download it.
+ location.href = fileEntry.toURL();
+ }, false);
+ });
+ });
+ },
+
+ function(err) {
+ throw err;
+ });
+};
+
+exports.Font = Font;
+
+},{"./encoding":4,"./glyphset":7,"./path":10,"./tables/sfnt":25}],6:[function(_dereq_,module,exports){
+// The Glyph object
+
+'use strict';
+
+var check = _dereq_('./check');
+var draw = _dereq_('./draw');
+var path = _dereq_('./path');
+
+function getPathDefinition(glyph, path) {
+ var _path = path || { commands: [] };
+ return {
+ configurable: true,
+
+ get: function() {
+ if (typeof _path === 'function') {
+ _path = _path();
+ }
+
+ return _path;
+ },
+
+ set: function(p) {
+ _path = p;
+ }
+ };
+}
+
+// A Glyph is an individual mark that often corresponds to a character.
+// Some glyphs, such as ligatures, are a combination of many characters.
+// Glyphs are the basic building blocks of a font.
+//
+// The `Glyph` class contains utility methods for drawing the path and its points.
+function Glyph(options) {
+ // By putting all the code on a prototype function (which is only declared once)
+ // we reduce the memory requirements for larger fonts by some 2%
+ this.bindConstructorValues(options);
+}
+
+Glyph.prototype.bindConstructorValues = function(options) {
+ this.index = options.index || 0;
+
+ // These three values cannnot be deferred for memory optimization:
+ this.name = options.name || null;
+ this.unicode = options.unicode || undefined;
+ this.unicodes = options.unicodes || options.unicode !== undefined ? [options.unicode] : [];
+
+ // But by binding these values only when necessary, we reduce can
+ // the memory requirements by almost 3% for larger fonts.
+ if (options.xMin) {
+ this.xMin = options.xMin;
+ }
+
+ if (options.yMin) {
+ this.yMin = options.yMin;
+ }
+
+ if (options.xMax) {
+ this.xMax = options.xMax;
+ }
+
+ if (options.yMax) {
+ this.yMax = options.yMax;
+ }
+
+ if (options.advanceWidth) {
+ this.advanceWidth = options.advanceWidth;
+ }
+
+ // The path for a glyph is the most memory intensive, and is bound as a value
+ // with a getter/setter to ensure we actually do path parsing only once the
+ // path is actually needed by anything.
+ Object.defineProperty(this, 'path', getPathDefinition(this, options.path));
+};
+
+Glyph.prototype.addUnicode = function(unicode) {
+ if (this.unicodes.length === 0) {
+ this.unicode = unicode;
+ }
+
+ this.unicodes.push(unicode);
+};
+
+// Convert the glyph to a Path we can draw on a drawing context.
+//
+// x - Horizontal position of the glyph. (default: 0)
+// y - Vertical position of the *baseline* of the glyph. (default: 0)
+// fontSize - Font size, in pixels (default: 72).
+Glyph.prototype.getPath = function(x, y, fontSize) {
+ x = x !== undefined ? x : 0;
+ y = y !== undefined ? y : 0;
+ fontSize = fontSize !== undefined ? fontSize : 72;
+ var scale = 1 / this.path.unitsPerEm * fontSize;
+ var p = new path.Path();
+ var commands = this.path.commands;
+ for (var i = 0; i < commands.length; i += 1) {
+ var cmd = commands[i];
+ if (cmd.type === 'M') {
+ p.moveTo(x + (cmd.x * scale), y + (-cmd.y * scale));
+ } else if (cmd.type === 'L') {
+ p.lineTo(x + (cmd.x * scale), y + (-cmd.y * scale));
+ } else if (cmd.type === 'Q') {
+ p.quadraticCurveTo(x + (cmd.x1 * scale), y + (-cmd.y1 * scale),
+ x + (cmd.x * scale), y + (-cmd.y * scale));
+ } else if (cmd.type === 'C') {
+ p.curveTo(x + (cmd.x1 * scale), y + (-cmd.y1 * scale),
+ x + (cmd.x2 * scale), y + (-cmd.y2 * scale),
+ x + (cmd.x * scale), y + (-cmd.y * scale));
+ } else if (cmd.type === 'Z') {
+ p.closePath();
+ }
+ }
+
+ return p;
+};
+
+// Split the glyph into contours.
+// This function is here for backwards compatibility, and to
+// provide raw access to the TrueType glyph outlines.
+Glyph.prototype.getContours = function() {
+ if (this.points === undefined) {
+ return [];
+ }
+
+ var contours = [];
+ var currentContour = [];
+ for (var i = 0; i < this.points.length; i += 1) {
+ var pt = this.points[i];
+ currentContour.push(pt);
+ if (pt.lastPointOfContour) {
+ contours.push(currentContour);
+ currentContour = [];
+ }
+ }
+
+ check.argument(currentContour.length === 0, 'There are still points left in the current contour.');
+ return contours;
+};
+
+// Calculate the xMin/yMin/xMax/yMax/lsb/rsb for a Glyph.
+Glyph.prototype.getMetrics = function() {
+ var commands = this.path.commands;
+ var xCoords = [];
+ var yCoords = [];
+ for (var i = 0; i < commands.length; i += 1) {
+ var cmd = commands[i];
+ if (cmd.type !== 'Z') {
+ xCoords.push(cmd.x);
+ yCoords.push(cmd.y);
+ }
+
+ if (cmd.type === 'Q' || cmd.type === 'C') {
+ xCoords.push(cmd.x1);
+ yCoords.push(cmd.y1);
+ }
+
+ if (cmd.type === 'C') {
+ xCoords.push(cmd.x2);
+ yCoords.push(cmd.y2);
+ }
+ }
+
+ var metrics = {
+ xMin: Math.min.apply(null, xCoords),
+ yMin: Math.min.apply(null, yCoords),
+ xMax: Math.max.apply(null, xCoords),
+ yMax: Math.max.apply(null, yCoords),
+ leftSideBearing: 0
+ };
+ metrics.rightSideBearing = this.advanceWidth - metrics.leftSideBearing - (metrics.xMax - metrics.xMin);
+ return metrics;
+};
+
+// Draw the glyph on the given context.
+//
+// ctx - The drawing context.
+// x - Horizontal position of the glyph. (default: 0)
+// y - Vertical position of the *baseline* of the glyph. (default: 0)
+// fontSize - Font size, in pixels (default: 72).
+Glyph.prototype.draw = function(ctx, x, y, fontSize) {
+ this.getPath(x, y, fontSize).draw(ctx);
+};
+
+// Draw the points of the glyph.
+// On-curve points will be drawn in blue, off-curve points will be drawn in red.
+//
+// ctx - The drawing context.
+// x - Horizontal position of the glyph. (default: 0)
+// y - Vertical position of the *baseline* of the glyph. (default: 0)
+// fontSize - Font size, in pixels (default: 72).
+Glyph.prototype.drawPoints = function(ctx, x, y, fontSize) {
+
+ function drawCircles(l, x, y, scale) {
+ var PI_SQ = Math.PI * 2;
+ ctx.beginPath();
+ for (var j = 0; j < l.length; j += 1) {
+ ctx.moveTo(x + (l[j].x * scale), y + (l[j].y * scale));
+ ctx.arc(x + (l[j].x * scale), y + (l[j].y * scale), 2, 0, PI_SQ, false);
+ }
+
+ ctx.closePath();
+ ctx.fill();
+ }
+
+ x = x !== undefined ? x : 0;
+ y = y !== undefined ? y : 0;
+ fontSize = fontSize !== undefined ? fontSize : 24;
+ var scale = 1 / this.path.unitsPerEm * fontSize;
+
+ var blueCircles = [];
+ var redCircles = [];
+ var path = this.path;
+ for (var i = 0; i < path.commands.length; i += 1) {
+ var cmd = path.commands[i];
+ if (cmd.x !== undefined) {
+ blueCircles.push({x: cmd.x, y: -cmd.y});
+ }
+
+ if (cmd.x1 !== undefined) {
+ redCircles.push({x: cmd.x1, y: -cmd.y1});
+ }
+
+ if (cmd.x2 !== undefined) {
+ redCircles.push({x: cmd.x2, y: -cmd.y2});
+ }
+ }
+
+ ctx.fillStyle = 'blue';
+ drawCircles(blueCircles, x, y, scale);
+ ctx.fillStyle = 'red';
+ drawCircles(redCircles, x, y, scale);
+};
+
+// Draw lines indicating important font measurements.
+// Black lines indicate the origin of the coordinate system (point 0,0).
+// Blue lines indicate the glyph bounding box.
+// Green line indicates the advance width of the glyph.
+//
+// ctx - The drawing context.
+// x - Horizontal position of the glyph. (default: 0)
+// y - Vertical position of the *baseline* of the glyph. (default: 0)
+// fontSize - Font size, in pixels (default: 72).
+Glyph.prototype.drawMetrics = function(ctx, x, y, fontSize) {
+ var scale;
+ x = x !== undefined ? x : 0;
+ y = y !== undefined ? y : 0;
+ fontSize = fontSize !== undefined ? fontSize : 24;
+ scale = 1 / this.path.unitsPerEm * fontSize;
+ ctx.lineWidth = 1;
+
+ // Draw the origin
+ ctx.strokeStyle = 'black';
+ draw.line(ctx, x, -10000, x, 10000);
+ draw.line(ctx, -10000, y, 10000, y);
+
+ // This code is here due to memory optimization: by not using
+ // defaults in the constructor, we save a notable amount of memory.
+ var xMin = this.xMin || 0;
+ var yMin = this.yMin || 0;
+ var xMax = this.xMax || 0;
+ var yMax = this.yMax || 0;
+ var advanceWidth = this.advanceWidth || 0;
+
+ // Draw the glyph box
+ ctx.strokeStyle = 'blue';
+ draw.line(ctx, x + (xMin * scale), -10000, x + (xMin * scale), 10000);
+ draw.line(ctx, x + (xMax * scale), -10000, x + (xMax * scale), 10000);
+ draw.line(ctx, -10000, y + (-yMin * scale), 10000, y + (-yMin * scale));
+ draw.line(ctx, -10000, y + (-yMax * scale), 10000, y + (-yMax * scale));
+
+ // Draw the advance width
+ ctx.strokeStyle = 'green';
+ draw.line(ctx, x + (advanceWidth * scale), -10000, x + (advanceWidth * scale), 10000);
+};
+
+exports.Glyph = Glyph;
+
+},{"./check":2,"./draw":3,"./path":10}],7:[function(_dereq_,module,exports){
+// The GlyphSet object
+
+'use strict';
+
+var _glyph = _dereq_('./glyph');
+
+// A GlyphSet represents all glyphs available in the font, but modelled using
+// a deferred glyph loader, for retrieving glyphs only once they are absolutely
+// necessary, to keep the memory footprint down.
+function GlyphSet(font, glyphs) {
+ this.font = font;
+ this.glyphs = {};
+ if (Array.isArray(glyphs)) {
+ for (var i = 0; i < glyphs.length; i++) {
+ this.glyphs[i] = glyphs[i];
+ }
+ }
+
+ this.length = (glyphs && glyphs.length) || 0;
+}
+
+GlyphSet.prototype.get = function(index) {
+ if (typeof this.glyphs[index] === 'function') {
+ this.glyphs[index] = this.glyphs[index]();
+ }
+
+ return this.glyphs[index];
+};
+
+GlyphSet.prototype.push = function(index, loader) {
+ this.glyphs[index] = loader;
+ this.length++;
+};
+
+function glyphLoader(font, index) {
+ return new _glyph.Glyph({index: index, font: font});
+}
+
+/**
+ * Generate a stub glyph that can be filled with all metadata *except*
+ * the "points" and "path" properties, which must be loaded only once
+ * the glyph's path is actually requested for text shaping.
+ */
+
+function ttfGlyphLoader(font, index, parseGlyph, data, position, buildPath) {
+ return function() {
+ var glyph = new _glyph.Glyph({index: index, font: font});
+
+ glyph.path = function() {
+ parseGlyph(glyph, data, position);
+ var path = buildPath(font.glyphs, glyph);
+ path.unitsPerEm = font.unitsPerEm;
+ return path;
+ };
+
+ return glyph;
+ };
+}
+
+function cffGlyphLoader(font, index, parseCFFCharstring, charstring) {
+ return function() {
+ var glyph = new _glyph.Glyph({index: index, font: font});
+
+ glyph.path = function() {
+ var path = parseCFFCharstring(font, glyph, charstring);
+ path.unitsPerEm = font.unitsPerEm;
+ return path;
+ };
+
+ return glyph;
+ };
+}
+
+exports.GlyphSet = GlyphSet;
+exports.glyphLoader = glyphLoader;
+exports.ttfGlyphLoader = ttfGlyphLoader;
+exports.cffGlyphLoader = cffGlyphLoader;
+
+},{"./glyph":6}],8:[function(_dereq_,module,exports){
+// opentype.js
+// https://github.com/nodebox/opentype.js
+// (c) 2015 Frederik De Bleser
+// opentype.js may be freely distributed under the MIT license.
+
+/* global ArrayBuffer, DataView, Uint8Array, XMLHttpRequest */
+
+'use strict';
+
+var encoding = _dereq_('./encoding');
+var _font = _dereq_('./font');
+var glyph = _dereq_('./glyph');
+var parse = _dereq_('./parse');
+var path = _dereq_('./path');
+
+var cmap = _dereq_('./tables/cmap');
+var cff = _dereq_('./tables/cff');
+var glyf = _dereq_('./tables/glyf');
+var gpos = _dereq_('./tables/gpos');
+var head = _dereq_('./tables/head');
+var hhea = _dereq_('./tables/hhea');
+var hmtx = _dereq_('./tables/hmtx');
+var kern = _dereq_('./tables/kern');
+var loca = _dereq_('./tables/loca');
+var maxp = _dereq_('./tables/maxp');
+var _name = _dereq_('./tables/name');
+var os2 = _dereq_('./tables/os2');
+var post = _dereq_('./tables/post');
+
+// File loaders /////////////////////////////////////////////////////////
+
+// Convert a Node.js Buffer to an ArrayBuffer
+function toArrayBuffer(buffer) {
+ var arrayBuffer = new ArrayBuffer(buffer.length);
+ var data = new Uint8Array(arrayBuffer);
+ for (var i = 0; i < buffer.length; i += 1) {
+ data[i] = buffer[i];
+ }
+
+ return arrayBuffer;
+}
+
+function loadFromFile(path, callback) {
+ var fs = _dereq_('fs');
+ fs.readFile(path, function(err, buffer) {
+ if (err) {
+ return callback(err.message);
+ }
+
+ callback(null, toArrayBuffer(buffer));
+ });
+}
+
+function loadFromUrl(url, callback) {
+ var request = new XMLHttpRequest();
+ request.open('get', url, true);
+ request.responseType = 'arraybuffer';
+ request.onload = function() {
+ if (request.status !== 200) {
+ return callback('Font could not be loaded: ' + request.statusText);
+ }
+
+ return callback(null, request.response);
+ };
+
+ request.send();
+}
+
+// Public API ///////////////////////////////////////////////////////////
+
+// Parse the OpenType file data (as an ArrayBuffer) and return a Font object.
+// If the file could not be parsed (most likely because it contains Postscript outlines)
+// we return an empty Font object with the `supported` flag set to `false`.
+function parseBuffer(buffer) {
+ var indexToLocFormat;
+ var hmtxOffset;
+ var glyfOffset;
+ var locaOffset;
+ var cffOffset;
+ var kernOffset;
+ var gposOffset;
+
+ // OpenType fonts use big endian byte ordering.
+ // We can't rely on typed array view types, because they operate with the endianness of the host computer.
+ // Instead we use DataViews where we can specify endianness.
+
+ var font = new _font.Font();
+ var data = new DataView(buffer, 0);
+
+ var version = parse.getFixed(data, 0);
+ if (version === 1.0) {
+ font.outlinesFormat = 'truetype';
+ } else {
+ version = parse.getTag(data, 0);
+ if (version === 'OTTO') {
+ font.outlinesFormat = 'cff';
+ } else {
+ throw new Error('Unsupported OpenType version ' + version);
+ }
+ }
+
+ var numTables = parse.getUShort(data, 4);
+
+ // Offset into the table records.
+ var p = 12;
+ for (var i = 0; i < numTables; i += 1) {
+ var tag = parse.getTag(data, p);
+ var offset = parse.getULong(data, p + 8);
+ switch (tag) {
+ case 'cmap':
+ font.tables.cmap = cmap.parse(data, offset);
+ font.encoding = new encoding.CmapEncoding(font.tables.cmap);
+ if (!font.encoding) {
+ font.supported = false;
+ }
+
+ break;
+ case 'head':
+ font.tables.head = head.parse(data, offset);
+ font.unitsPerEm = font.tables.head.unitsPerEm;
+ indexToLocFormat = font.tables.head.indexToLocFormat;
+ break;
+ case 'hhea':
+ font.tables.hhea = hhea.parse(data, offset);
+ font.ascender = font.tables.hhea.ascender;
+ font.descender = font.tables.hhea.descender;
+ font.numberOfHMetrics = font.tables.hhea.numberOfHMetrics;
+ break;
+ case 'hmtx':
+ hmtxOffset = offset;
+ break;
+ case 'maxp':
+ font.tables.maxp = maxp.parse(data, offset);
+ font.numGlyphs = font.tables.maxp.numGlyphs;
+ break;
+ case 'name':
+ font.tables.name = _name.parse(data, offset);
+ font.familyName = font.tables.name.fontFamily;
+ font.styleName = font.tables.name.fontSubfamily;
+ break;
+ case 'OS/2':
+ font.tables.os2 = os2.parse(data, offset);
+ break;
+ case 'post':
+ font.tables.post = post.parse(data, offset);
+ font.glyphNames = new encoding.GlyphNames(font.tables.post);
+ break;
+ case 'glyf':
+ glyfOffset = offset;
+ break;
+ case 'loca':
+ locaOffset = offset;
+ break;
+ case 'CFF ':
+ cffOffset = offset;
+ break;
+ case 'kern':
+ kernOffset = offset;
+ break;
+ case 'GPOS':
+ gposOffset = offset;
+ break;
+ }
+ p += 16;
+ }
+
+ if (glyfOffset && locaOffset) {
+ var shortVersion = indexToLocFormat === 0;
+ var locaTable = loca.parse(data, locaOffset, font.numGlyphs, shortVersion);
+ font.glyphs = glyf.parse(data, glyfOffset, locaTable, font);
+ hmtx.parse(data, hmtxOffset, font.numberOfHMetrics, font.numGlyphs, font.glyphs);
+ encoding.addGlyphNames(font);
+ } else if (cffOffset) {
+ cff.parse(data, cffOffset, font);
+ encoding.addGlyphNames(font);
+ } else {
+ font.supported = false;
+ }
+
+ if (font.supported) {
+ if (kernOffset) {
+ font.kerningPairs = kern.parse(data, kernOffset);
+ } else {
+ font.kerningPairs = {};
+ }
+
+ if (gposOffset) {
+ gpos.parse(data, gposOffset, font);
+ }
+ }
+
+ return font;
+}
+
+// Asynchronously load the font from a URL or a filesystem. When done, call the callback
+// with two arguments `(err, font)`. The `err` will be null on success,
+// the `font` is a Font object.
+//
+// We use the node.js callback convention so that
+// opentype.js can integrate with frameworks like async.js.
+function load(url, callback) {
+ var isNode = typeof window === 'undefined';
+ var loadFn = isNode ? loadFromFile : loadFromUrl;
+ loadFn(url, function(err, arrayBuffer) {
+ if (err) {
+ return callback(err);
+ }
+
+ var font = parseBuffer(arrayBuffer);
+ if (!font.supported) {
+ return callback('Font is not supported (is this a Postscript font?)');
+ }
+
+ return callback(null, font);
+ });
+}
+
+exports._parse = parse;
+exports.Font = _font.Font;
+exports.Glyph = glyph.Glyph;
+exports.Path = path.Path;
+exports.parse = parseBuffer;
+exports.load = load;
+
+},{"./encoding":4,"./font":5,"./glyph":6,"./parse":9,"./path":10,"./tables/cff":12,"./tables/cmap":13,"./tables/glyf":14,"./tables/gpos":15,"./tables/head":16,"./tables/hhea":17,"./tables/hmtx":18,"./tables/kern":19,"./tables/loca":20,"./tables/maxp":21,"./tables/name":22,"./tables/os2":23,"./tables/post":24,"fs":1}],9:[function(_dereq_,module,exports){
+// Parsing utility functions
+
+'use strict';
+
+// Retrieve an unsigned byte from the DataView.
+exports.getByte = function getByte(dataView, offset) {
+ return dataView.getUint8(offset);
+};
+
+exports.getCard8 = exports.getByte;
+
+// Retrieve an unsigned 16-bit short from the DataView.
+// The value is stored in big endian.
+exports.getUShort = function(dataView, offset) {
+ return dataView.getUint16(offset, false);
+};
+
+exports.getCard16 = exports.getUShort;
+
+// Retrieve a signed 16-bit short from the DataView.
+// The value is stored in big endian.
+exports.getShort = function(dataView, offset) {
+ return dataView.getInt16(offset, false);
+};
+
+// Retrieve an unsigned 32-bit long from the DataView.
+// The value is stored in big endian.
+exports.getULong = function(dataView, offset) {
+ return dataView.getUint32(offset, false);
+};
+
+// Retrieve a 32-bit signed fixed-point number (16.16) from the DataView.
+// The value is stored in big endian.
+exports.getFixed = function(dataView, offset) {
+ var decimal = dataView.getInt16(offset, false);
+ var fraction = dataView.getUint16(offset + 2, false);
+ return decimal + fraction / 65535;
+};
+
+// Retrieve a 4-character tag from the DataView.
+// Tags are used to identify tables.
+exports.getTag = function(dataView, offset) {
+ var tag = '';
+ for (var i = offset; i < offset + 4; i += 1) {
+ tag += String.fromCharCode(dataView.getInt8(i));
+ }
+
+ return tag;
+};
+
+// Retrieve an offset from the DataView.
+// Offsets are 1 to 4 bytes in length, depending on the offSize argument.
+exports.getOffset = function(dataView, offset, offSize) {
+ var v = 0;
+ for (var i = 0; i < offSize; i += 1) {
+ v <<= 8;
+ v += dataView.getUint8(offset + i);
+ }
+
+ return v;
+};
+
+// Retrieve a number of bytes from start offset to the end offset from the DataView.
+exports.getBytes = function(dataView, startOffset, endOffset) {
+ var bytes = [];
+ for (var i = startOffset; i < endOffset; i += 1) {
+ bytes.push(dataView.getUint8(i));
+ }
+
+ return bytes;
+};
+
+// Convert the list of bytes to a string.
+exports.bytesToString = function(bytes) {
+ var s = '';
+ for (var i = 0; i < bytes.length; i += 1) {
+ s += String.fromCharCode(bytes[i]);
+ }
+
+ return s;
+};
+
+var typeOffsets = {
+ byte: 1,
+ uShort: 2,
+ short: 2,
+ uLong: 4,
+ fixed: 4,
+ longDateTime: 8,
+ tag: 4
+};
+
+// A stateful parser that changes the offset whenever a value is retrieved.
+// The data is a DataView.
+function Parser(data, offset) {
+ this.data = data;
+ this.offset = offset;
+ this.relativeOffset = 0;
+}
+
+Parser.prototype.parseByte = function() {
+ var v = this.data.getUint8(this.offset + this.relativeOffset);
+ this.relativeOffset += 1;
+ return v;
+};
+
+Parser.prototype.parseChar = function() {
+ var v = this.data.getInt8(this.offset + this.relativeOffset);
+ this.relativeOffset += 1;
+ return v;
+};
+
+Parser.prototype.parseCard8 = Parser.prototype.parseByte;
+
+Parser.prototype.parseUShort = function() {
+ var v = this.data.getUint16(this.offset + this.relativeOffset);
+ this.relativeOffset += 2;
+ return v;
+};
+
+Parser.prototype.parseCard16 = Parser.prototype.parseUShort;
+Parser.prototype.parseSID = Parser.prototype.parseUShort;
+Parser.prototype.parseOffset16 = Parser.prototype.parseUShort;
+
+Parser.prototype.parseShort = function() {
+ var v = this.data.getInt16(this.offset + this.relativeOffset);
+ this.relativeOffset += 2;
+ return v;
+};
+
+Parser.prototype.parseF2Dot14 = function() {
+ var v = this.data.getInt16(this.offset + this.relativeOffset) / 16384;
+ this.relativeOffset += 2;
+ return v;
+};
+
+Parser.prototype.parseULong = function() {
+ var v = exports.getULong(this.data, this.offset + this.relativeOffset);
+ this.relativeOffset += 4;
+ return v;
+};
+
+Parser.prototype.parseFixed = function() {
+ var v = exports.getFixed(this.data, this.offset + this.relativeOffset);
+ this.relativeOffset += 4;
+ return v;
+};
+
+Parser.prototype.parseOffset16List =
+Parser.prototype.parseUShortList = function(count) {
+ var offsets = new Array(count);
+ var dataView = this.data;
+ var offset = this.offset + this.relativeOffset;
+ for (var i = 0; i < count; i++) {
+ offsets[i] = exports.getUShort(dataView, offset);
+ offset += 2;
+ }
+
+ this.relativeOffset += count * 2;
+ return offsets;
+};
+
+Parser.prototype.parseString = function(length) {
+ var dataView = this.data;
+ var offset = this.offset + this.relativeOffset;
+ var string = '';
+ this.relativeOffset += length;
+ for (var i = 0; i < length; i++) {
+ string += String.fromCharCode(dataView.getUint8(offset + i));
+ }
+
+ return string;
+};
+
+Parser.prototype.parseTag = function() {
+ return this.parseString(4);
+};
+
+// LONGDATETIME is a 64-bit integer.
+// JavaScript and unix timestamps traditionally use 32 bits, so we
+// only take the last 32 bits.
+Parser.prototype.parseLongDateTime = function() {
+ var v = exports.getULong(this.data, this.offset + this.relativeOffset + 4);
+ this.relativeOffset += 8;
+ return v;
+};
+
+Parser.prototype.parseFixed = function() {
+ var v = exports.getULong(this.data, this.offset + this.relativeOffset);
+ this.relativeOffset += 4;
+ return v / 65536;
+};
+
+Parser.prototype.parseVersion = function() {
+ var major = exports.getUShort(this.data, this.offset + this.relativeOffset);
+
+ // How to interpret the minor version is very vague in the spec. 0x5000 is 5, 0x1000 is 1
+ // This returns the correct number if minor = 0xN000 where N is 0-9
+ var minor = exports.getUShort(this.data, this.offset + this.relativeOffset + 2);
+ this.relativeOffset += 4;
+ return major + minor / 0x1000 / 10;
+};
+
+Parser.prototype.skip = function(type, amount) {
+ if (amount === undefined) {
+ amount = 1;
+ }
+
+ this.relativeOffset += typeOffsets[type] * amount;
+};
+
+exports.Parser = Parser;
+
+},{}],10:[function(_dereq_,module,exports){
+// Geometric objects
+
+'use strict';
+
+// A bézier path containing a set of path commands similar to a SVG path.
+// Paths can be drawn on a context using `draw`.
+function Path() {
+ this.commands = [];
+ this.fill = 'black';
+ this.stroke = null;
+ this.strokeWidth = 1;
+}
+
+Path.prototype.moveTo = function(x, y) {
+ this.commands.push({
+ type: 'M',
+ x: x,
+ y: y
+ });
+};
+
+Path.prototype.lineTo = function(x, y) {
+ this.commands.push({
+ type: 'L',
+ x: x,
+ y: y
+ });
+};
+
+Path.prototype.curveTo = Path.prototype.bezierCurveTo = function(x1, y1, x2, y2, x, y) {
+ this.commands.push({
+ type: 'C',
+ x1: x1,
+ y1: y1,
+ x2: x2,
+ y2: y2,
+ x: x,
+ y: y
+ });
+};
+
+Path.prototype.quadTo = Path.prototype.quadraticCurveTo = function(x1, y1, x, y) {
+ this.commands.push({
+ type: 'Q',
+ x1: x1,
+ y1: y1,
+ x: x,
+ y: y
+ });
+};
+
+Path.prototype.close = Path.prototype.closePath = function() {
+ this.commands.push({
+ type: 'Z'
+ });
+};
+
+// Add the given path or list of commands to the commands of this path.
+Path.prototype.extend = function(pathOrCommands) {
+ if (pathOrCommands.commands) {
+ pathOrCommands = pathOrCommands.commands;
+ }
+
+ Array.prototype.push.apply(this.commands, pathOrCommands);
+};
+
+// Draw the path to a 2D context.
+Path.prototype.draw = function(ctx) {
+ ctx.beginPath();
+ for (var i = 0; i < this.commands.length; i += 1) {
+ var cmd = this.commands[i];
+ if (cmd.type === 'M') {
+ ctx.moveTo(cmd.x, cmd.y);
+ } else if (cmd.type === 'L') {
+ ctx.lineTo(cmd.x, cmd.y);
+ } else if (cmd.type === 'C') {
+ ctx.bezierCurveTo(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.x, cmd.y);
+ } else if (cmd.type === 'Q') {
+ ctx.quadraticCurveTo(cmd.x1, cmd.y1, cmd.x, cmd.y);
+ } else if (cmd.type === 'Z') {
+ ctx.closePath();
+ }
+ }
+
+ if (this.fill) {
+ ctx.fillStyle = this.fill;
+ ctx.fill();
+ }
+
+ if (this.stroke) {
+ ctx.strokeStyle = this.stroke;
+ ctx.lineWidth = this.strokeWidth;
+ ctx.stroke();
+ }
+};
+
+// Convert the Path to a string of path data instructions
+// See http://www.w3.org/TR/SVG/paths.html#PathData
+// Parameters:
+// - decimalPlaces: The amount of decimal places for floating-point values (default: 2)
+Path.prototype.toPathData = function(decimalPlaces) {
+ decimalPlaces = decimalPlaces !== undefined ? decimalPlaces : 2;
+
+ function floatToString(v) {
+ if (Math.round(v) === v) {
+ return '' + Math.round(v);
+ } else {
+ return v.toFixed(decimalPlaces);
+ }
+ }
+
+ function packValues() {
+ var s = '';
+ for (var i = 0; i < arguments.length; i += 1) {
+ var v = arguments[i];
+ if (v >= 0 && i > 0) {
+ s += ' ';
+ }
+
+ s += floatToString(v);
+ }
+
+ return s;
+ }
+
+ var d = '';
+ for (var i = 0; i < this.commands.length; i += 1) {
+ var cmd = this.commands[i];
+ if (cmd.type === 'M') {
+ d += 'M' + packValues(cmd.x, cmd.y);
+ } else if (cmd.type === 'L') {
+ d += 'L' + packValues(cmd.x, cmd.y);
+ } else if (cmd.type === 'C') {
+ d += 'C' + packValues(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.x, cmd.y);
+ } else if (cmd.type === 'Q') {
+ d += 'Q' + packValues(cmd.x1, cmd.y1, cmd.x, cmd.y);
+ } else if (cmd.type === 'Z') {
+ d += 'Z';
+ }
+ }
+
+ return d;
+};
+
+// Convert the path to a SVG <path> element, as a string.
+// Parameters:
+// - decimalPlaces: The amount of decimal places for floating-point values (default: 2)
+Path.prototype.toSVG = function(decimalPlaces) {
+ var svg = '<path d="';
+ svg += this.toPathData(decimalPlaces);
+ svg += '"';
+ if (this.fill & this.fill !== 'black') {
+ if (this.fill === null) {
+ svg += ' fill="none"';
+ } else {
+ svg += ' fill="' + this.fill + '"';
+ }
+ }
+
+ if (this.stroke) {
+ svg += ' stroke="' + this.stroke + '" stroke-width="' + this.strokeWidth + '"';
+ }
+
+ svg += '/>';
+ return svg;
+};
+
+exports.Path = Path;
+
+},{}],11:[function(_dereq_,module,exports){
+// Table metadata
+
+'use strict';
+
+var check = _dereq_('./check');
+var encode = _dereq_('./types').encode;
+var sizeOf = _dereq_('./types').sizeOf;
+
+function Table(tableName, fields, options) {
+ var i;
+ for (i = 0; i < fields.length; i += 1) {
+ var field = fields[i];
+ this[field.name] = field.value;
+ }
+
+ this.tableName = tableName;
+ this.fields = fields;
+ if (options) {
+ var optionKeys = Object.keys(options);
+ for (i = 0; i < optionKeys.length; i += 1) {
+ var k = optionKeys[i];
+ var v = options[k];
+ if (this[k] !== undefined) {
+ this[k] = v;
+ }
+ }
+ }
+}
+
+Table.prototype.sizeOf = function() {
+ var v = 0;
+ for (var i = 0; i < this.fields.length; i += 1) {
+ var field = this.fields[i];
+ var value = this[field.name];
+ if (value === undefined) {
+ value = field.value;
+ }
+
+ if (typeof value.sizeOf === 'function') {
+ v += value.sizeOf();
+ } else {
+ var sizeOfFunction = sizeOf[field.type];
+ check.assert(typeof sizeOfFunction === 'function', 'Could not find sizeOf function for field' + field.name);
+ v += sizeOfFunction(value);
+ }
+ }
+
+ return v;
+};
+
+Table.prototype.encode = function() {
+ return encode.TABLE(this);
+};
+
+exports.Table = Table;
+
+},{"./check":2,"./types":26}],12:[function(_dereq_,module,exports){
+// The `CFF` table contains the glyph outlines in PostScript format.
+// https://www.microsoft.com/typography/OTSPEC/cff.htm
+// http://download.microsoft.com/download/8/0/1/801a191c-029d-4af3-9642-555f6fe514ee/cff.pdf
+// http://download.microsoft.com/download/8/0/1/801a191c-029d-4af3-9642-555f6fe514ee/type2.pdf
+
+'use strict';
+
+var encoding = _dereq_('../encoding');
+var glyphset = _dereq_('../glyphset');
+var parse = _dereq_('../parse');
+var path = _dereq_('../path');
+var table = _dereq_('../table');
+
+// Custom equals function that can also check lists.
+function equals(a, b) {
+ if (a === b) {
+ return true;
+ } else if (Array.isArray(a) && Array.isArray(b)) {
+ if (a.length !== b.length) {
+ return false;
+ }
+
+ for (var i = 0; i < a.length; i += 1) {
+ if (!equals(a[i], b[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// Parse a `CFF` INDEX array.
+// An index array consists of a list of offsets, then a list of objects at those offsets.
+function parseCFFIndex(data, start, conversionFn) {
+ //var i, objectOffset, endOffset;
+ var offsets = [];
+ var objects = [];
+ var count = parse.getCard16(data, start);
+ var i;
+ var objectOffset;
+ var endOffset;
+ if (count !== 0) {
+ var offsetSize = parse.getByte(data, start + 2);
+ objectOffset = start + ((count + 1) * offsetSize) + 2;
+ var pos = start + 3;
+ for (i = 0; i < count + 1; i += 1) {
+ offsets.push(parse.getOffset(data, pos, offsetSize));
+ pos += offsetSize;
+ }
+
+ // The total size of the index array is 4 header bytes + the value of the last offset.
+ endOffset = objectOffset + offsets[count];
+ } else {
+ endOffset = start + 2;
+ }
+
+ for (i = 0; i < offsets.length - 1; i += 1) {
+ var value = parse.getBytes(data, objectOffset + offsets[i], objectOffset + offsets[i + 1]);
+ if (conversionFn) {
+ value = conversionFn(value);
+ }
+
+ objects.push(value);
+ }
+
+ return {objects: objects, startOffset: start, endOffset: endOffset};
+}
+
+// Parse a `CFF` DICT real value.
+function parseFloatOperand(parser) {
+ var s = '';
+ var eof = 15;
+ var lookup = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'E', 'E-', null, '-'];
+ while (true) {
+ var b = parser.parseByte();
+ var n1 = b >> 4;
+ var n2 = b & 15;
+
+ if (n1 === eof) {
+ break;
+ }
+
+ s += lookup[n1];
+
+ if (n2 === eof) {
+ break;
+ }
+
+ s += lookup[n2];
+ }
+
+ return parseFloat(s);
+}
+
+// Parse a `CFF` DICT operand.
+function parseOperand(parser, b0) {
+ var b1;
+ var b2;
+ var b3;
+ var b4;
+ if (b0 === 28) {
+ b1 = parser.parseByte();
+ b2 = parser.parseByte();
+ return b1 << 8 | b2;
+ }
+
+ if (b0 === 29) {
+ b1 = parser.parseByte();
+ b2 = parser.parseByte();
+ b3 = parser.parseByte();
+ b4 = parser.parseByte();
+ return b1 << 24 | b2 << 16 | b3 << 8 | b4;
+ }
+
+ if (b0 === 30) {
+ return parseFloatOperand(parser);
+ }
+
+ if (b0 >= 32 && b0 <= 246) {
+ return b0 - 139;
+ }
+
+ if (b0 >= 247 && b0 <= 250) {
+ b1 = parser.parseByte();
+ return (b0 - 247) * 256 + b1 + 108;
+ }
+
+ if (b0 >= 251 && b0 <= 254) {
+ b1 = parser.parseByte();
+ return -(b0 - 251) * 256 - b1 - 108;
+ }
+
+ throw new Error('Invalid b0 ' + b0);
+}
+
+// Convert the entries returned by `parseDict` to a proper dictionary.
+// If a value is a list of one, it is unpacked.
+function entriesToObject(entries) {
+ var o = {};
+ for (var i = 0; i < entries.length; i += 1) {
+ var key = entries[i][0];
+ var values = entries[i][1];
+ var value;
+ if (values.length === 1) {
+ value = values[0];
+ } else {
+ value = values;
+ }
+
+ if (o.hasOwnProperty(key)) {
+ throw new Error('Object ' + o + ' already has key ' + key);
+ }
+
+ o[key] = value;
+ }
+
+ return o;
+}
+
+// Parse a `CFF` DICT object.
+// A dictionary contains key-value pairs in a compact tokenized format.
+function parseCFFDict(data, start, size) {
+ start = start !== undefined ? start : 0;
+ var parser = new parse.Parser(data, start);
+ var entries = [];
+ var operands = [];
+ size = size !== undefined ? size : data.length;
+
+ while (parser.relativeOffset < size) {
+ var op = parser.parseByte();
+
+ // The first byte for each dict item distinguishes between operator (key) and operand (value).
+ // Values <= 21 are operators.
+ if (op <= 21) {
+ // Two-byte operators have an initial escape byte of 12.
+ if (op === 12) {
+ op = 1200 + parser.parseByte();
+ }
+
+ entries.push([op, operands]);
+ operands = [];
+ } else {
+ // Since the operands (values) come before the operators (keys), we store all operands in a list
+ // until we encounter an operator.
+ operands.push(parseOperand(parser, op));
+ }
+ }
+
+ return entriesToObject(entries);
+}
+
+// Given a String Index (SID), return the value of the string.
+// Strings below index 392 are standard CFF strings and are not encoded in the font.
+function getCFFString(strings, index) {
+ if (index <= 390) {
+ index = encoding.cffStandardStrings[index];
+ } else {
+ index = strings[index - 391];
+ }
+
+ return index;
+}
+
+// Interpret a dictionary and return a new dictionary with readable keys and values for missing entries.
+// This function takes `meta` which is a list of objects containing `operand`, `name` and `default`.
+function interpretDict(dict, meta, strings) {
+ var newDict = {};
+
+ // Because we also want to include missing values, we start out from the meta list
+ // and lookup values in the dict.
+ for (var i = 0; i < meta.length; i += 1) {
+ var m = meta[i];
+ var value = dict[m.op];
+ if (value === undefined) {
+ value = m.value !== undefined ? m.value : null;
+ }
+
+ if (m.type === 'SID') {
+ value = getCFFString(strings, value);
+ }
+
+ newDict[m.name] = value;
+ }
+
+ return newDict;
+}
+
+// Parse the CFF header.
+function parseCFFHeader(data, start) {
+ var header = {};
+ header.formatMajor = parse.getCard8(data, start);
+ header.formatMinor = parse.getCard8(data, start + 1);
+ header.size = parse.getCard8(data, start + 2);
+ header.offsetSize = parse.getCard8(data, start + 3);
+ header.startOffset = start;
+ header.endOffset = start + 4;
+ return header;
+}
+
+var TOP_DICT_META = [
+ {name: 'version', op: 0, type: 'SID'},
+ {name: 'notice', op: 1, type: 'SID'},
+ {name: 'copyright', op: 1200, type: 'SID'},
+ {name: 'fullName', op: 2, type: 'SID'},
+ {name: 'familyName', op: 3, type: 'SID'},
+ {name: 'weight', op: 4, type: 'SID'},
+ {name: 'isFixedPitch', op: 1201, type: 'number', value: 0},
+ {name: 'italicAngle', op: 1202, type: 'number', value: 0},
+ {name: 'underlinePosition', op: 1203, type: 'number', value: -100},
+ {name: 'underlineThickness', op: 1204, type: 'number', value: 50},
+ {name: 'paintType', op: 1205, type: 'number', value: 0},
+ {name: 'charstringType', op: 1206, type: 'number', value: 2},
+ {name: 'fontMatrix', op: 1207, type: ['real', 'real', 'real', 'real', 'real', 'real'], value: [0.001, 0, 0, 0.001, 0, 0]},
+ {name: 'uniqueId', op: 13, type: 'number'},
+ {name: 'fontBBox', op: 5, type: ['number', 'number', 'number', 'number'], value: [0, 0, 0, 0]},
+ {name: 'strokeWidth', op: 1208, type: 'number', value: 0},
+ {name: 'xuid', op: 14, type: [], value: null},
+ {name: 'charset', op: 15, type: 'offset', value: 0},
+ {name: 'encoding', op: 16, type: 'offset', value: 0},
+ {name: 'charStrings', op: 17, type: 'offset', value: 0},
+ {name: 'private', op: 18, type: ['number', 'offset'], value: [0, 0]}
+];
+
+var PRIVATE_DICT_META = [
+ {name: 'subrs', op: 19, type: 'offset', value: 0},
+ {name: 'defaultWidthX', op: 20, type: 'number', value: 0},
+ {name: 'nominalWidthX', op: 21, type: 'number', value: 0}
+];
+
+// Parse the CFF top dictionary. A CFF table can contain multiple fonts, each with their own top dictionary.
+// The top dictionary contains the essential metadata for the font, together with the private dictionary.
+function parseCFFTopDict(data, strings) {
+ var dict = parseCFFDict(data, 0, data.byteLength);
+ return interpretDict(dict, TOP_DICT_META, strings);
+}
+
+// Parse the CFF private dictionary. We don't fully parse out all the values, only the ones we need.
+function parseCFFPrivateDict(data, start, size, strings) {
+ var dict = parseCFFDict(data, start, size);
+ return interpretDict(dict, PRIVATE_DICT_META, strings);
+}
+
+// Parse the CFF charset table, which contains internal names for all the glyphs.
+// This function will return a list of glyph names.
+// See Adobe TN #5176 chapter 13, "Charsets".
+function parseCFFCharset(data, start, nGlyphs, strings) {
+ var i;
+ var sid;
+ var count;
+ var parser = new parse.Parser(data, start);
+
+ // The .notdef glyph is not included, so subtract 1.
+ nGlyphs -= 1;
+ var charset = ['.notdef'];
+
+ var format = parser.parseCard8();
+ if (format === 0) {
+ for (i = 0; i < nGlyphs; i += 1) {
+ sid = parser.parseSID();
+ charset.push(getCFFString(strings, sid));
+ }
+ } else if (format === 1) {
+ while (charset.length <= nGlyphs) {
+ sid = parser.parseSID();
+ count = parser.parseCard8();
+ for (i = 0; i <= count; i += 1) {
+ charset.push(getCFFString(strings, sid));
+ sid += 1;
+ }
+ }
+ } else if (format === 2) {
+ while (charset.length <= nGlyphs) {
+ sid = parser.parseSID();
+ count = parser.parseCard16();
+ for (i = 0; i <= count; i += 1) {
+ charset.push(getCFFString(strings, sid));
+ sid += 1;
+ }
+ }
+ } else {
+ throw new Error('Unknown charset format ' + format);
+ }
+
+ return charset;
+}
+
+// Parse the CFF encoding data. Only one encoding can be specified per font.
+// See Adobe TN #5176 chapter 12, "Encodings".
+function parseCFFEncoding(data, start, charset) {
+ var i;
+ var code;
+ var enc = {};
+ var parser = new parse.Parser(data, start);
+ var format = parser.parseCard8();
+ if (format === 0) {
+ var nCodes = parser.parseCard8();
+ for (i = 0; i < nCodes; i += 1) {
+ code = parser.parseCard8();
+ enc[code] = i;
+ }
+ } else if (format === 1) {
+ var nRanges = parser.parseCard8();
+ code = 1;
+ for (i = 0; i < nRanges; i += 1) {
+ var first = parser.parseCard8();
+ var nLeft = parser.parseCard8();
+ for (var j = first; j <= first + nLeft; j += 1) {
+ enc[j] = code;
+ code += 1;
+ }
+ }
+ } else {
+ throw new Error('Unknown encoding format ' + format);
+ }
+
+ return new encoding.CffEncoding(enc, charset);
+}
+
+// Take in charstring code and return a Glyph object.
+// The encoding is described in the Type 2 Charstring Format
+// https://www.microsoft.com/typography/OTSPEC/charstr2.htm
+function parseCFFCharstring(font, glyph, code) {
+ var c1x;
+ var c1y;
+ var c2x;
+ var c2y;
+ var p = new path.Path();
+ var stack = [];
+ var nStems = 0;
+ var haveWidth = false;
+ var width = font.defaultWidthX;
+ var open = false;
+ var x = 0;
+ var y = 0;
+
+ function newContour(x, y) {
+ if (open) {
+ p.closePath();
+ }
+
+ p.moveTo(x, y);
+ open = true;
+ }
+
+ function parseStems() {
+ var hasWidthArg;
+
+ // The number of stem operators on the stack is always even.
+ // If the value is uneven, that means a width is specified.
+ hasWidthArg = stack.length % 2 !== 0;
+ if (hasWidthArg && !haveWidth) {
+ width = stack.shift() + font.nominalWidthX;
+ }
+
+ nStems += stack.length >> 1;
+ stack.length = 0;
+ haveWidth = true;
+ }
+
+ function parse(code) {
+ var b1;
+ var b2;
+ var b3;
+ var b4;
+ var codeIndex;
+ var subrCode;
+ var jpx;
+ var jpy;
+ var c3x;
+ var c3y;
+ var c4x;
+ var c4y;
+
+ var i = 0;
+ while (i < code.length) {
+ var v = code[i];
+ i += 1;
+ switch (v) {
+ case 1: // hstem
+ parseStems();
+ break;
+ case 3: // vstem
+ parseStems();
+ break;
+ case 4: // vmoveto
+ if (stack.length > 1 && !haveWidth) {
+ width = stack.shift() + font.nominalWidthX;
+ haveWidth = true;
+ }
+
+ y += stack.pop();
+ newContour(x, y);
+ break;
+ case 5: // rlineto
+ while (stack.length > 0) {
+ x += stack.shift();
+ y += stack.shift();
+ p.lineTo(x, y);
+ }
+
+ break;
+ case 6: // hlineto
+ while (stack.length > 0) {
+ x += stack.shift();
+ p.lineTo(x, y);
+ if (stack.length === 0) {
+ break;
+ }
+
+ y += stack.shift();
+ p.lineTo(x, y);
+ }
+
+ break;
+ case 7: // vlineto
+ while (stack.length > 0) {
+ y += stack.shift();
+ p.lineTo(x, y);
+ if (stack.length === 0) {
+ break;
+ }
+
+ x += stack.shift();
+ p.lineTo(x, y);
+ }
+
+ break;
+ case 8: // rrcurveto
+ while (stack.length > 0) {
+ c1x = x + stack.shift();
+ c1y = y + stack.shift();
+ c2x = c1x + stack.shift();
+ c2y = c1y + stack.shift();
+ x = c2x + stack.shift();
+ y = c2y + stack.shift();
+ p.curveTo(c1x, c1y, c2x, c2y, x, y);
+ }
+
+ break;
+ case 10: // callsubr
+ codeIndex = stack.pop() + font.subrsBias;
+ subrCode = font.subrs[codeIndex];
+ if (subrCode) {
+ parse(subrCode);
+ }
+
+ break;
+ case 11: // return
+ return;
+ case 12: // flex operators
+ v = code[i];
+ i += 1;
+ switch (v) {
+ case 35: // flex
+ // |- dx1 dy1 dx2 dy2 dx3 dy3 dx4 dy4 dx5 dy5 dx6 dy6 fd flex (12 35) |-
+ c1x = x + stack.shift(); // dx1
+ c1y = y + stack.shift(); // dy1
+ c2x = c1x + stack.shift(); // dx2
+ c2y = c1y + stack.shift(); // dy2
+ jpx = c2x + stack.shift(); // dx3
+ jpy = c2y + stack.shift(); // dy3
+ c3x = jpx + stack.shift(); // dx4
+ c3y = jpy + stack.shift(); // dy4
+ c4x = c3x + stack.shift(); // dx5
+ c4y = c3y + stack.shift(); // dy5
+ x = c4x + stack.shift(); // dx6
+ y = c4y + stack.shift(); // dy6
+ stack.shift(); // flex depth
+ p.curveTo(c1x, c1y, c2x, c2y, jpx, jpy);
+ p.curveTo(c3x, c3y, c4x, c4y, x, y);
+ break;
+ case 34: // hflex
+ // |- dx1 dx2 dy2 dx3 dx4 dx5 dx6 hflex (12 34) |-
+ c1x = x + stack.shift(); // dx1
+ c1y = y; // dy1
+ c2x = c1x + stack.shift(); // dx2
+ c2y = c1y + stack.shift(); // dy2
+ jpx = c2x + stack.shift(); // dx3
+ jpy = c2y; // dy3
+ c3x = jpx + stack.shift(); // dx4
+ c3y = c2y; // dy4
+ c4x = c3x + stack.shift(); // dx5
+ c4y = y; // dy5
+ x = c4x + stack.shift(); // dx6
+ p.curveTo(c1x, c1y, c2x, c2y, jpx, jpy);
+ p.curveTo(c3x, c3y, c4x, c4y, x, y);
+ break;
+ case 36: // hflex1
+ // |- dx1 dy1 dx2 dy2 dx3 dx4 dx5 dy5 dx6 hflex1 (12 36) |-
+ c1x = x + stack.shift(); // dx1
+ c1y = y + stack.shift(); // dy1
+ c2x = c1x + stack.shift(); // dx2
+ c2y = c1y + stack.shift(); // dy2
+ jpx = c2x + stack.shift(); // dx3
+ jpy = c2y; // dy3
+ c3x = jpx + stack.shift(); // dx4
+ c3y = c2y; // dy4
+ c4x = c3x + stack.shift(); // dx5
+ c4y = c3y + stack.shift(); // dy5
+ x = c4x + stack.shift(); // dx6
+ p.curveTo(c1x, c1y, c2x, c2y, jpx, jpy);
+ p.curveTo(c3x, c3y, c4x, c4y, x, y);
+ break;
+ case 37: // flex1
+ // |- dx1 dy1 dx2 dy2 dx3 dy3 dx4 dy4 dx5 dy5 d6 flex1 (12 37) |-
+ c1x = x + stack.shift(); // dx1
+ c1y = y + stack.shift(); // dy1
+ c2x = c1x + stack.shift(); // dx2
+ c2y = c1y + stack.shift(); // dy2
+ jpx = c2x + stack.shift(); // dx3
+ jpy = c2y + stack.shift(); // dy3
+ c3x = jpx + stack.shift(); // dx4
+ c3y = jpy + stack.shift(); // dy4
+ c4x = c3x + stack.shift(); // dx5
+ c4y = c3y + stack.shift(); // dy5
+ if (Math.abs(c4x - x) > Math.abs(c4y - y)) {
+ x = c4x + stack.shift();
+ } else {
+ y = c4y + stack.shift();
+ }
+
+ p.curveTo(c1x, c1y, c2x, c2y, jpx, jpy);
+ p.curveTo(c3x, c3y, c4x, c4y, x, y);
+ break;
+ default:
+ console.log('Glyph ' + glyph.index + ': unknown operator ' + 1200 + v);
+ stack.length = 0;
+ }
+ break;
+ case 14: // endchar
+ if (stack.length > 0 && !haveWidth) {
+ width = stack.shift() + font.nominalWidthX;
+ haveWidth = true;
+ }
+
+ if (open) {
+ p.closePath();
+ open = false;
+ }
+
+ break;
+ case 18: // hstemhm
+ parseStems();
+ break;
+ case 19: // hintmask
+ case 20: // cntrmask
+ parseStems();
+ i += (nStems + 7) >> 3;
+ break;
+ case 21: // rmoveto
+ if (stack.length > 2 && !haveWidth) {
+ width = stack.shift() + font.nominalWidthX;
+ haveWidth = true;
+ }
+
+ y += stack.pop();
+ x += stack.pop();
+ newContour(x, y);
+ break;
+ case 22: // hmoveto
+ if (stack.length > 1 && !haveWidth) {
+ width = stack.shift() + font.nominalWidthX;
+ haveWidth = true;
+ }
+
+ x += stack.pop();
+ newContour(x, y);
+ break;
+ case 23: // vstemhm
+ parseStems();
+ break;
+ case 24: // rcurveline
+ while (stack.length > 2) {
+ c1x = x + stack.shift();
+ c1y = y + stack.shift();
+ c2x = c1x + stack.shift();
+ c2y = c1y + stack.shift();
+ x = c2x + stack.shift();
+ y = c2y + stack.shift();
+ p.curveTo(c1x, c1y, c2x, c2y, x, y);
+ }
+
+ x += stack.shift();
+ y += stack.shift();
+ p.lineTo(x, y);
+ break;
+ case 25: // rlinecurve
+ while (stack.length > 6) {
+ x += stack.shift();
+ y += stack.shift();
+ p.lineTo(x, y);
+ }
+
+ c1x = x + stack.shift();
+ c1y = y + stack.shift();
+ c2x = c1x + stack.shift();
+ c2y = c1y + stack.shift();
+ x = c2x + stack.shift();
+ y = c2y + stack.shift();
+ p.curveTo(c1x, c1y, c2x, c2y, x, y);
+ break;
+ case 26: // vvcurveto
+ if (stack.length % 2) {
+ x += stack.shift();
+ }
+
+ while (stack.length > 0) {
+ c1x = x;
+ c1y = y + stack.shift();
+ c2x = c1x + stack.shift();
+ c2y = c1y + stack.shift();
+ x = c2x;
+ y = c2y + stack.shift();
+ p.curveTo(c1x, c1y, c2x, c2y, x, y);
+ }
+
+ break;
+ case 27: // hhcurveto
+ if (stack.length % 2) {
+ y += stack.shift();
+ }
+
+ while (stack.length > 0) {
+ c1x = x + stack.shift();
+ c1y = y;
+ c2x = c1x + stack.shift();
+ c2y = c1y + stack.shift();
+ x = c2x + stack.shift();
+ y = c2y;
+ p.curveTo(c1x, c1y, c2x, c2y, x, y);
+ }
+
+ break;
+ case 28: // shortint
+ b1 = code[i];
+ b2 = code[i + 1];
+ stack.push(((b1 << 24) | (b2 << 16)) >> 16);
+ i += 2;
+ break;
+ case 29: // callgsubr
+ codeIndex = stack.pop() + font.gsubrsBias;
+ subrCode = font.gsubrs[codeIndex];
+ if (subrCode) {
+ parse(subrCode);
+ }
+
+ break;
+ case 30: // vhcurveto
+ while (stack.length > 0) {
+ c1x = x;
+ c1y = y + stack.shift();
+ c2x = c1x + stack.shift();
+ c2y = c1y + stack.shift();
+ x = c2x + stack.shift();
+ y = c2y + (stack.length === 1 ? stack.shift() : 0);
+ p.curveTo(c1x, c1y, c2x, c2y, x, y);
+ if (stack.length === 0) {
+ break;
+ }
+
+ c1x = x + stack.shift();
+ c1y = y;
+ c2x = c1x + stack.shift();
+ c2y = c1y + stack.shift();
+ y = c2y + stack.shift();
+ x = c2x + (stack.length === 1 ? stack.shift() : 0);
+ p.curveTo(c1x, c1y, c2x, c2y, x, y);
+ }
+
+ break;
+ case 31: // hvcurveto
+ while (stack.length > 0) {
+ c1x = x + stack.shift();
+ c1y = y;
+ c2x = c1x + stack.shift();
+ c2y = c1y + stack.shift();
+ y = c2y + stack.shift();
+ x = c2x + (stack.length === 1 ? stack.shift() : 0);
+ p.curveTo(c1x, c1y, c2x, c2y, x, y);
+ if (stack.length === 0) {
+ break;
+ }
+
+ c1x = x;
+ c1y = y + stack.shift();
+ c2x = c1x + stack.shift();
+ c2y = c1y + stack.shift();
+ x = c2x + stack.shift();
+ y = c2y + (stack.length === 1 ? stack.shift() : 0);
+ p.curveTo(c1x, c1y, c2x, c2y, x, y);
+ }
+
+ break;
+ default:
+ if (v < 32) {
+ console.log('Glyph ' + glyph.index + ': unknown operator ' + v);
+ } else if (v < 247) {
+ stack.push(v - 139);
+ } else if (v < 251) {
+ b1 = code[i];
+ i += 1;
+ stack.push((v - 247) * 256 + b1 + 108);
+ } else if (v < 255) {
+ b1 = code[i];
+ i += 1;
+ stack.push(-(v - 251) * 256 - b1 - 108);
+ } else {
+ b1 = code[i];
+ b2 = code[i + 1];
+ b3 = code[i + 2];
+ b4 = code[i + 3];
+ i += 4;
+ stack.push(((b1 << 24) | (b2 << 16) | (b3 << 8) | b4) / 65536);
+ }
+ }
+ }
+ }
+
+ parse(code);
+
+ glyph.advanceWidth = width;
+ return p;
+}
+
+// Subroutines are encoded using the negative half of the number space.
+// See type 2 chapter 4.7 "Subroutine operators".
+function calcCFFSubroutineBias(subrs) {
+ var bias;
+ if (subrs.length < 1240) {
+ bias = 107;
+ } else if (subrs.length < 33900) {
+ bias = 1131;
+ } else {
+ bias = 32768;
+ }
+
+ return bias;
+}
+
+// Parse the `CFF` table, which contains the glyph outlines in PostScript format.
+function parseCFFTable(data, start, font) {
+ font.tables.cff = {};
+ var header = parseCFFHeader(data, start);
+ var nameIndex = parseCFFIndex(data, header.endOffset, parse.bytesToString);
+ var topDictIndex = parseCFFIndex(data, nameIndex.endOffset);
+ var stringIndex = parseCFFIndex(data, topDictIndex.endOffset, parse.bytesToString);
+ var globalSubrIndex = parseCFFIndex(data, stringIndex.endOffset);
+ font.gsubrs = globalSubrIndex.objects;
+ font.gsubrsBias = calcCFFSubroutineBias(font.gsubrs);
+
+ var topDictData = new DataView(new Uint8Array(topDictIndex.objects[0]).buffer);
+ var topDict = parseCFFTopDict(topDictData, stringIndex.objects);
+ font.tables.cff.topDict = topDict;
+
+ var privateDictOffset = start + topDict['private'][1];
+ var privateDict = parseCFFPrivateDict(data, privateDictOffset, topDict['private'][0], stringIndex.objects);
+ font.defaultWidthX = privateDict.defaultWidthX;
+ font.nominalWidthX = privateDict.nominalWidthX;
+
+ if (privateDict.subrs !== 0) {
+ var subrOffset = privateDictOffset + privateDict.subrs;
+ var subrIndex = parseCFFIndex(data, subrOffset);
+ font.subrs = subrIndex.objects;
+ font.subrsBias = calcCFFSubroutineBias(font.subrs);
+ } else {
+ font.subrs = [];
+ font.subrsBias = 0;
+ }
+
+ // Offsets in the top dict are relative to the beginning of the CFF data, so add the CFF start offset.
+ var charStringsIndex = parseCFFIndex(data, start + topDict.charStrings);
+ font.nGlyphs = charStringsIndex.objects.length;
+
+ var charset = parseCFFCharset(data, start + topDict.charset, font.nGlyphs, stringIndex.objects);
+ if (topDict.encoding === 0) { // Standard encoding
+ font.cffEncoding = new encoding.CffEncoding(encoding.cffStandardEncoding, charset);
+ } else if (topDict.encoding === 1) { // Expert encoding
+ font.cffEncoding = new encoding.CffEncoding(encoding.cffExpertEncoding, charset);
+ } else {
+ font.cffEncoding = parseCFFEncoding(data, start + topDict.encoding, charset);
+ }
+
+ // Prefer the CMAP encoding to the CFF encoding.
+ font.encoding = font.encoding || font.cffEncoding;
+
+ font.glyphs = new glyphset.GlyphSet(font);
+ for (var i = 0; i < font.nGlyphs; i += 1) {
+ var charString = charStringsIndex.objects[i];
+ font.glyphs.push(i, glyphset.cffGlyphLoader(font, i, parseCFFCharstring, charString));
+ }
+}
+
+// Convert a string to a String ID (SID).
+// The list of strings is modified in place.
+function encodeString(s, strings) {
+ var sid;
+
+ // Is the string in the CFF standard strings?
+ var i = encoding.cffStandardStrings.indexOf(s);
+ if (i >= 0) {
+ sid = i;
+ }
+
+ // Is the string already in the string index?
+ i = strings.indexOf(s);
+ if (i >= 0) {
+ sid = i + encoding.cffStandardStrings.length;
+ } else {
+ sid = encoding.cffStandardStrings.length + strings.length;
+ strings.push(s);
+ }
+
+ return sid;
+}
+
+function makeHeader() {
+ return new table.Table('Header', [
+ {name: 'major', type: 'Card8', value: 1},
+ {name: 'minor', type: 'Card8', value: 0},
+ {name: 'hdrSize', type: 'Card8', value: 4},
+ {name: 'major', type: 'Card8', value: 1}
+ ]);
+}
+
+function makeNameIndex(fontNames) {
+ var t = new table.Table('Name INDEX', [
+ {name: 'names', type: 'INDEX', value: []}
+ ]);
+ t.names = [];
+ for (var i = 0; i < fontNames.length; i += 1) {
+ t.names.push({name: 'name_' + i, type: 'NAME', value: fontNames[i]});
+ }
+
+ return t;
+}
+
+// Given a dictionary's metadata, create a DICT structure.
+function makeDict(meta, attrs, strings) {
+ var m = {};
+ for (var i = 0; i < meta.length; i += 1) {
+ var entry = meta[i];
+ var value = attrs[entry.name];
+ if (value !== undefined && !equals(value, entry.value)) {
+ if (entry.type === 'SID') {
+ value = encodeString(value, strings);
+ }
+
+ m[entry.op] = {name: entry.name, type: entry.type, value: value};
+ }
+ }
+
+ return m;
+}
+
+// The Top DICT houses the global font attributes.
+function makeTopDict(attrs, strings) {
+ var t = new table.Table('Top DICT', [
+ {name: 'dict', type: 'DICT', value: {}}
+ ]);
+ t.dict = makeDict(TOP_DICT_META, attrs, strings);
+ return t;
+}
+
+function makeTopDictIndex(topDict) {
+ var t = new table.Table('Top DICT INDEX', [
+ {name: 'topDicts', type: 'INDEX', value: []}
+ ]);
+ t.topDicts = [{name: 'topDict_0', type: 'TABLE', value: topDict}];
+ return t;
+}
+
+function makeStringIndex(strings) {
+ var t = new table.Table('String INDEX', [
+ {name: 'strings', type: 'INDEX', value: []}
+ ]);
+ t.strings = [];
+ for (var i = 0; i < strings.length; i += 1) {
+ t.strings.push({name: 'string_' + i, type: 'STRING', value: strings[i]});
+ }
+
+ return t;
+}
+
+function makeGlobalSubrIndex() {
+ // Currently we don't use subroutines.
+ return new table.Table('Global Subr INDEX', [
+ {name: 'subrs', type: 'INDEX', value: []}
+ ]);
+}
+
+function makeCharsets(glyphNames, strings) {
+ var t = new table.Table('Charsets', [
+ {name: 'format', type: 'Card8', value: 0}
+ ]);
+ for (var i = 0; i < glyphNames.length; i += 1) {
+ var glyphName = glyphNames[i];
+ var glyphSID = encodeString(glyphName, strings);
+ t.fields.push({name: 'glyph_' + i, type: 'SID', value: glyphSID});
+ }
+
+ return t;
+}
+
+function glyphToOps(glyph) {
+ var ops = [];
+ var path = glyph.path;
+ ops.push({name: 'width', type: 'NUMBER', value: glyph.advanceWidth});
+ var x = 0;
+ var y = 0;
+ for (var i = 0; i < path.commands.length; i += 1) {
+ var dx;
+ var dy;
+ var cmd = path.commands[i];
+ if (cmd.type === 'Q') {
+ // CFF only supports bézier curves, so convert the quad to a bézier.
+ var _13 = 1 / 3;
+ var _23 = 2 / 3;
+
+ // We're going to create a new command so we don't change the original path.
+ cmd = {
+ type: 'C',
+ x: cmd.x,
+ y: cmd.y,
+ x1: _13 * x + _23 * cmd.x1,
+ y1: _13 * y + _23 * cmd.y1,
+ x2: _13 * cmd.x + _23 * cmd.x1,
+ y2: _13 * cmd.y + _23 * cmd.y1
+ };
+ }
+
+ if (cmd.type === 'M') {
+ dx = Math.round(cmd.x - x);
+ dy = Math.round(cmd.y - y);
+ ops.push({name: 'dx', type: 'NUMBER', value: dx});
+ ops.push({name: 'dy', type: 'NUMBER', value: dy});
+ ops.push({name: 'rmoveto', type: 'OP', value: 21});
+ x = Math.round(cmd.x);
+ y = Math.round(cmd.y);
+ } else if (cmd.type === 'L') {
+ dx = Math.round(cmd.x - x);
+ dy = Math.round(cmd.y - y);
+ ops.push({name: 'dx', type: 'NUMBER', value: dx});
+ ops.push({name: 'dy', type: 'NUMBER', value: dy});
+ ops.push({name: 'rlineto', type: 'OP', value: 5});
+ x = Math.round(cmd.x);
+ y = Math.round(cmd.y);
+ } else if (cmd.type === 'C') {
+ var dx1 = Math.round(cmd.x1 - x);
+ var dy1 = Math.round(cmd.y1 - y);
+ var dx2 = Math.round(cmd.x2 - cmd.x1);
+ var dy2 = Math.round(cmd.y2 - cmd.y1);
+ dx = Math.round(cmd.x - cmd.x2);
+ dy = Math.round(cmd.y - cmd.y2);
+ ops.push({name: 'dx1', type: 'NUMBER', value: dx1});
+ ops.push({name: 'dy1', type: 'NUMBER', value: dy1});
+ ops.push({name: 'dx2', type: 'NUMBER', value: dx2});
+ ops.push({name: 'dy2', type: 'NUMBER', value: dy2});
+ ops.push({name: 'dx', type: 'NUMBER', value: dx});
+ ops.push({name: 'dy', type: 'NUMBER', value: dy});
+ ops.push({name: 'rrcurveto', type: 'OP', value: 8});
+ x = Math.round(cmd.x);
+ y = Math.round(cmd.y);
+ }
+
+ // Contours are closed automatically.
+
+ }
+
+ ops.push({name: 'endchar', type: 'OP', value: 14});
+ return ops;
+}
+
+function makeCharStringsIndex(glyphs) {
+ var t = new table.Table('CharStrings INDEX', [
+ {name: 'charStrings', type: 'INDEX', value: []}
+ ]);
+
+ for (var i = 0; i < glyphs.length; i += 1) {
+ var glyph = glyphs.get(i);
+ var ops = glyphToOps(glyph);
+ t.charStrings.push({name: glyph.name, type: 'CHARSTRING', value: ops});
+ }
+
+ return t;
+}
+
+function makePrivateDict(attrs, strings) {
+ var t = new table.Table('Private DICT', [
+ {name: 'dict', type: 'DICT', value: {}}
+ ]);
+ t.dict = makeDict(PRIVATE_DICT_META, attrs, strings);
+ return t;
+}
+
+function makePrivateDictIndex(privateDict) {
+ var t = new table.Table('Private DICT INDEX', [
+ {name: 'privateDicts', type: 'INDEX', value: []}
+ ]);
+ t.privateDicts = [{name: 'privateDict_0', type: 'TABLE', value: privateDict}];
+ return t;
+}
+
+function makeCFFTable(glyphs, options) {
+ var t = new table.Table('CFF ', [
+ {name: 'header', type: 'TABLE'},
+ {name: 'nameIndex', type: 'TABLE'},
+ {name: 'topDictIndex', type: 'TABLE'},
+ {name: 'stringIndex', type: 'TABLE'},
+ {name: 'globalSubrIndex', type: 'TABLE'},
+ {name: 'charsets', type: 'TABLE'},
+ {name: 'charStringsIndex', type: 'TABLE'},
+ {name: 'privateDictIndex', type: 'TABLE'}
+ ]);
+
+ var fontScale = 1 / options.unitsPerEm;
+ // We use non-zero values for the offsets so that the DICT encodes them.
+ // This is important because the size of the Top DICT plays a role in offset calculation,
+ // and the size shouldn't change after we've written correct offsets.
+ var attrs = {
+ version: options.version,
+ fullName: options.fullName,
+ familyName: options.familyName,
+ weight: options.weightName,
+ fontMatrix: [fontScale, 0, 0, fontScale, 0, 0],
+ charset: 999,
+ encoding: 0,
+ charStrings: 999,
+ private: [0, 999]
+ };
+
+ var privateAttrs = {};
+
+ var glyphNames = [];
+ var glyph;
+
+ // Skip first glyph (.notdef)
+ for (var i = 1; i < glyphs.length; i += 1) {
+ glyph = glyphs.get(i);
+ glyphNames.push(glyph.name);
+ }
+
+ var strings = [];
+
+ t.header = makeHeader();
+ t.nameIndex = makeNameIndex([options.postScriptName]);
+ var topDict = makeTopDict(attrs, strings);
+ t.topDictIndex = makeTopDictIndex(topDict);
+ t.globalSubrIndex = makeGlobalSubrIndex();
+ t.charsets = makeCharsets(glyphNames, strings);
+ t.charStringsIndex = makeCharStringsIndex(glyphs);
+ var privateDict = makePrivateDict(privateAttrs, strings);
+ t.privateDictIndex = makePrivateDictIndex(privateDict);
+
+ // Needs to come at the end, to encode all custom strings used in the font.
+ t.stringIndex = makeStringIndex(strings);
+
+ var startOffset = t.header.sizeOf() +
+ t.nameIndex.sizeOf() +
+ t.topDictIndex.sizeOf() +
+ t.stringIndex.sizeOf() +
+ t.globalSubrIndex.sizeOf();
+ attrs.charset = startOffset;
+
+ // We use the CFF standard encoding; proper encoding will be handled in cmap.
+ attrs.encoding = 0;
+ attrs.charStrings = attrs.charset + t.charsets.sizeOf();
+ attrs.private[1] = attrs.charStrings + t.charStringsIndex.sizeOf();
+
+ // Recreate the Top DICT INDEX with the correct offsets.
+ topDict = makeTopDict(attrs, strings);
+ t.topDictIndex = makeTopDictIndex(topDict);
+
+ return t;
+}
+
+exports.parse = parseCFFTable;
+exports.make = makeCFFTable;
+
+},{"../encoding":4,"../glyphset":7,"../parse":9,"../path":10,"../table":11}],13:[function(_dereq_,module,exports){
+// The `cmap` table stores the mappings from characters to glyphs.
+// https://www.microsoft.com/typography/OTSPEC/cmap.htm
+
+'use strict';
+
+var check = _dereq_('../check');
+var parse = _dereq_('../parse');
+var table = _dereq_('../table');
+
+// Parse the `cmap` table. This table stores the mappings from characters to glyphs.
+// There are many available formats, but we only support the Windows format 4.
+// This function returns a `CmapEncoding` object or null if no supported format could be found.
+function parseCmapTable(data, start) {
+ var i;
+ var cmap = {};
+ cmap.version = parse.getUShort(data, start);
+ check.argument(cmap.version === 0, 'cmap table version should be 0.');
+
+ // The cmap table can contain many sub-tables, each with their own format.
+ // We're only interested in a "platform 3" table. This is a Windows format.
+ cmap.numTables = parse.getUShort(data, start + 2);
+ var offset = -1;
+ for (i = 0; i < cmap.numTables; i += 1) {
+ var platformId = parse.getUShort(data, start + 4 + (i * 8));
+ var encodingId = parse.getUShort(data, start + 4 + (i * 8) + 2);
+ if (platformId === 3 && (encodingId === 1 || encodingId === 0)) {
+ offset = parse.getULong(data, start + 4 + (i * 8) + 4);
+ break;
+ }
+ }
+
+ if (offset === -1) {
+ // There is no cmap table in the font that we support, so return null.
+ // This font will be marked as unsupported.
+ return null;
+ }
+
+ var p = new parse.Parser(data, start + offset);
+ cmap.format = p.parseUShort();
+ check.argument(cmap.format === 4, 'Only format 4 cmap tables are supported.');
+
+ // Length in bytes of the sub-tables.
+ cmap.length = p.parseUShort();
+ cmap.language = p.parseUShort();
+
+ // segCount is stored x 2.
+ var segCount;
+ cmap.segCount = segCount = p.parseUShort() >> 1;
+
+ // Skip searchRange, entrySelector, rangeShift.
+ p.skip('uShort', 3);
+
+ // The "unrolled" mapping from character codes to glyph indices.
+ cmap.glyphIndexMap = {};
+
+ var endCountParser = new parse.Parser(data, start + offset + 14);
+ var startCountParser = new parse.Parser(data, start + offset + 16 + segCount * 2);
+ var idDeltaParser = new parse.Parser(data, start + offset + 16 + segCount * 4);
+ var idRangeOffsetParser = new parse.Parser(data, start + offset + 16 + segCount * 6);
+ var glyphIndexOffset = start + offset + 16 + segCount * 8;
+ for (i = 0; i < segCount - 1; i += 1) {
+ var glyphIndex;
+ var endCount = endCountParser.parseUShort();
+ var startCount = startCountParser.parseUShort();
+ var idDelta = idDeltaParser.parseShort();
+ var idRangeOffset = idRangeOffsetParser.parseUShort();
+ for (var c = startCount; c <= endCount; c += 1) {
+ if (idRangeOffset !== 0) {
+ // The idRangeOffset is relative to the current position in the idRangeOffset array.
+ // Take the current offset in the idRangeOffset array.
+ glyphIndexOffset = (idRangeOffsetParser.offset + idRangeOffsetParser.relativeOffset - 2);
+
+ // Add the value of the idRangeOffset, which will move us into the glyphIndex array.
+ glyphIndexOffset += idRangeOffset;
+
+ // Then add the character index of the current segment, multiplied by 2 for USHORTs.
+ glyphIndexOffset += (c - startCount) * 2;
+ glyphIndex = parse.getUShort(data, glyphIndexOffset);
+ if (glyphIndex !== 0) {
+ glyphIndex = (glyphIndex + idDelta) & 0xFFFF;
+ }
+ } else {
+ glyphIndex = (c + idDelta) & 0xFFFF;
+ }
+
+ cmap.glyphIndexMap[c] = glyphIndex;
+ }
+ }
+
+ return cmap;
+}
+
+function addSegment(t, code, glyphIndex) {
+ t.segments.push({
+ end: code,
+ start: code,
+ delta: -(code - glyphIndex),
+ offset: 0
+ });
+}
+
+function addTerminatorSegment(t) {
+ t.segments.push({
+ end: 0xFFFF,
+ start: 0xFFFF,
+ delta: 1,
+ offset: 0
+ });
+}
+
+function makeCmapTable(glyphs) {
+ var i;
+ var t = new table.Table('cmap', [
+ {name: 'version', type: 'USHORT', value: 0},
+ {name: 'numTables', type: 'USHORT', value: 1},
+ {name: 'platformID', type: 'USHORT', value: 3},
+ {name: 'encodingID', type: 'USHORT', value: 1},
+ {name: 'offset', type: 'ULONG', value: 12},
+ {name: 'format', type: 'USHORT', value: 4},
+ {name: 'length', type: 'USHORT', value: 0},
+ {name: 'language', type: 'USHORT', value: 0},
+ {name: 'segCountX2', type: 'USHORT', value: 0},
+ {name: 'searchRange', type: 'USHORT', value: 0},
+ {name: 'entrySelector', type: 'USHORT', value: 0},
+ {name: 'rangeShift', type: 'USHORT', value: 0}
+ ]);
+
+ t.segments = [];
+ for (i = 0; i < glyphs.length; i += 1) {
+ var glyph = glyphs.get(i);
+ for (var j = 0; j < glyph.unicodes.length; j += 1) {
+ addSegment(t, glyph.unicodes[j], i);
+ }
+
+ t.segments = t.segments.sort(function(a, b) {
+ return a.start - b.start;
+ });
+ }
+
+ addTerminatorSegment(t);
+
+ var segCount;
+ segCount = t.segments.length;
+ t.segCountX2 = segCount * 2;
+ t.searchRange = Math.pow(2, Math.floor(Math.log(segCount) / Math.log(2))) * 2;
+ t.entrySelector = Math.log(t.searchRange / 2) / Math.log(2);
+ t.rangeShift = t.segCountX2 - t.searchRange;
+
+ // Set up parallel segment arrays.
+ var endCounts = [];
+ var startCounts = [];
+ var idDeltas = [];
+ var idRangeOffsets = [];
+ var glyphIds = [];
+
+ for (i = 0; i < segCount; i += 1) {
+ var segment = t.segments[i];
+ endCounts = endCounts.concat({name: 'end_' + i, type: 'USHORT', value: segment.end});
+ startCounts = startCounts.concat({name: 'start_' + i, type: 'USHORT', value: segment.start});
+ idDeltas = idDeltas.concat({name: 'idDelta_' + i, type: 'SHORT', value: segment.delta});
+ idRangeOffsets = idRangeOffsets.concat({name: 'idRangeOffset_' + i, type: 'USHORT', value: segment.offset});
+ if (segment.glyphId !== undefined) {
+ glyphIds = glyphIds.concat({name: 'glyph_' + i, type: 'USHORT', value: segment.glyphId});
+ }
+ }
+
+ t.fields = t.fields.concat(endCounts);
+ t.fields.push({name: 'reservedPad', type: 'USHORT', value: 0});
+ t.fields = t.fields.concat(startCounts);
+ t.fields = t.fields.concat(idDeltas);
+ t.fields = t.fields.concat(idRangeOffsets);
+ t.fields = t.fields.concat(glyphIds);
+
+ t.length = 14 + // Subtable header
+ endCounts.length * 2 +
+ 2 + // reservedPad
+ startCounts.length * 2 +
+ idDeltas.length * 2 +
+ idRangeOffsets.length * 2 +
+ glyphIds.length * 2;
+
+ return t;
+}
+
+exports.parse = parseCmapTable;
+exports.make = makeCmapTable;
+
+},{"../check":2,"../parse":9,"../table":11}],14:[function(_dereq_,module,exports){
+// The `glyf` table describes the glyphs in TrueType outline format.
+// http://www.microsoft.com/typography/otspec/glyf.htm
+
+'use strict';
+
+var check = _dereq_('../check');
+var glyphset = _dereq_('../glyphset');
+var parse = _dereq_('../parse');
+var path = _dereq_('../path');
+
+// Parse the coordinate data for a glyph.
+function parseGlyphCoordinate(p, flag, previousValue, shortVectorBitMask, sameBitMask) {
+ var v;
+ if ((flag & shortVectorBitMask) > 0) {
+ // The coordinate is 1 byte long.
+ v = p.parseByte();
+ // The `same` bit is re-used for short values to signify the sign of the value.
+ if ((flag & sameBitMask) === 0) {
+ v = -v;
+ }
+
+ v = previousValue + v;
+ } else {
+ // The coordinate is 2 bytes long.
+ // If the `same` bit is set, the coordinate is the same as the previous coordinate.
+ if ((flag & sameBitMask) > 0) {
+ v = previousValue;
+ } else {
+ // Parse the coordinate as a signed 16-bit delta value.
+ v = previousValue + p.parseShort();
+ }
+ }
+
+ return v;
+}
+
+// Parse a TrueType glyph.
+function parseGlyph(glyph, data, start) {
+ var p = new parse.Parser(data, start);
+ glyph.numberOfContours = p.parseShort();
+ glyph.xMin = p.parseShort();
+ glyph.yMin = p.parseShort();
+ glyph.xMax = p.parseShort();
+ glyph.yMax = p.parseShort();
+ var flags;
+ var flag;
+ if (glyph.numberOfContours > 0) {
+ var i;
+ // This glyph is not a composite.
+ var endPointIndices = glyph.endPointIndices = [];
+ for (i = 0; i < glyph.numberOfContours; i += 1) {
+ endPointIndices.push(p.parseUShort());
+ }
+
+ glyph.instructionLength = p.parseUShort();
+ glyph.instructions = [];
+ for (i = 0; i < glyph.instructionLength; i += 1) {
+ glyph.instructions.push(p.parseByte());
+ }
+
+ var numberOfCoordinates = endPointIndices[endPointIndices.length - 1] + 1;
+ flags = [];
+ for (i = 0; i < numberOfCoordinates; i += 1) {
+ flag = p.parseByte();
+ flags.push(flag);
+ // If bit 3 is set, we repeat this flag n times, where n is the next byte.
+ if ((flag & 8) > 0) {
+ var repeatCount = p.parseByte();
+ for (var j = 0; j < repeatCount; j += 1) {
+ flags.push(flag);
+ i += 1;
+ }
+ }
+ }
+
+ check.argument(flags.length === numberOfCoordinates, 'Bad flags.');
+
+ if (endPointIndices.length > 0) {
+ var points = [];
+ var point;
+ // X/Y coordinates are relative to the previous point, except for the first point which is relative to 0,0.
+ if (numberOfCoordinates > 0) {
+ for (i = 0; i < numberOfCoordinates; i += 1) {
+ flag = flags[i];
+ point = {};
+ point.onCurve = !!(flag & 1);
+ point.lastPointOfContour = endPointIndices.indexOf(i) >= 0;
+ points.push(point);
+ }
+
+ var px = 0;
+ for (i = 0; i < numberOfCoordinates; i += 1) {
+ flag = flags[i];
+ point = points[i];
+ point.x = parseGlyphCoordinate(p, flag, px, 2, 16);
+ px = point.x;
+ }
+
+ var py = 0;
+ for (i = 0; i < numberOfCoordinates; i += 1) {
+ flag = flags[i];
+ point = points[i];
+ point.y = parseGlyphCoordinate(p, flag, py, 4, 32);
+ py = point.y;
+ }
+ }
+
+ glyph.points = points;
+ } else {
+ glyph.points = [];
+ }
+ } else if (glyph.numberOfContours === 0) {
+ glyph.points = [];
+ } else {
+ glyph.isComposite = true;
+ glyph.points = [];
+ glyph.components = [];
+ var moreComponents = true;
+ while (moreComponents) {
+ flags = p.parseUShort();
+ var component = {
+ glyphIndex: p.parseUShort(),
+ xScale: 1,
+ scale01: 0,
+ scale10: 0,
+ yScale: 1,
+ dx: 0,
+ dy: 0
+ };
+ if ((flags & 1) > 0) {
+ // The arguments are words
+ component.dx = p.parseShort();
+ component.dy = p.parseShort();
+ } else {
+ // The arguments are bytes
+ component.dx = p.parseChar();
+ component.dy = p.parseChar();
+ }
+
+ if ((flags & 8) > 0) {
+ // We have a scale
+ component.xScale = component.yScale = p.parseF2Dot14();
+ } else if ((flags & 64) > 0) {
+ // We have an X / Y scale
+ component.xScale = p.parseF2Dot14();
+ component.yScale = p.parseF2Dot14();
+ } else if ((flags & 128) > 0) {
+ // We have a 2x2 transformation
+ component.xScale = p.parseF2Dot14();
+ component.scale01 = p.parseF2Dot14();
+ component.scale10 = p.parseF2Dot14();
+ component.yScale = p.parseF2Dot14();
+ }
+
+ glyph.components.push(component);
+ moreComponents = !!(flags & 32);
+ }
+ }
+}
+
+// Transform an array of points and return a new array.
+function transformPoints(points, transform) {
+ var newPoints = [];
+ for (var i = 0; i < points.length; i += 1) {
+ var pt = points[i];
+ var newPt = {
+ x: transform.xScale * pt.x + transform.scale01 * pt.y + transform.dx,
+ y: transform.scale10 * pt.x + transform.yScale * pt.y + transform.dy,
+ onCurve: pt.onCurve,
+ lastPointOfContour: pt.lastPointOfContour
+ };
+ newPoints.push(newPt);
+ }
+
+ return newPoints;
+}
+
+function getContours(points) {
+ var contours = [];
+ var currentContour = [];
+ for (var i = 0; i < points.length; i += 1) {
+ var pt = points[i];
+ currentContour.push(pt);
+ if (pt.lastPointOfContour) {
+ contours.push(currentContour);
+ currentContour = [];
+ }
+ }
+
+ check.argument(currentContour.length === 0, 'There are still points left in the current contour.');
+ return contours;
+}
+
+// Convert the TrueType glyph outline to a Path.
+function getPath(points) {
+ var p = new path.Path();
+ if (!points) {
+ return p;
+ }
+
+ var contours = getContours(points);
+ for (var i = 0; i < contours.length; i += 1) {
+ var contour = contours[i];
+ var firstPt = contour[0];
+ var lastPt = contour[contour.length - 1];
+ var curvePt;
+ var realFirstPoint;
+ if (firstPt.onCurve) {
+ curvePt = null;
+ // The first point will be consumed by the moveTo command,
+ // so skip it in the loop.
+ realFirstPoint = true;
+ } else {
+ if (lastPt.onCurve) {
+ // If the first point is off-curve and the last point is on-curve,
+ // start at the last point.
+ firstPt = lastPt;
+ } else {
+ // If both first and last points are off-curve, start at their middle.
+ firstPt = { x: (firstPt.x + lastPt.x) / 2, y: (firstPt.y + lastPt.y) / 2 };
+ }
+
+ curvePt = firstPt;
+ // The first point is synthesized, so don't skip the real first point.
+ realFirstPoint = false;
+ }
+
+ p.moveTo(firstPt.x, firstPt.y);
+
+ for (var j = realFirstPoint ? 1 : 0; j < contour.length; j += 1) {
+ var pt = contour[j];
+ var prevPt = j === 0 ? firstPt : contour[j - 1];
+ if (prevPt.onCurve && pt.onCurve) {
+ // This is a straight line.
+ p.lineTo(pt.x, pt.y);
+ } else if (prevPt.onCurve && !pt.onCurve) {
+ curvePt = pt;
+ } else if (!prevPt.onCurve && !pt.onCurve) {
+ var midPt = { x: (prevPt.x + pt.x) / 2, y: (prevPt.y + pt.y) / 2 };
+ p.quadraticCurveTo(prevPt.x, prevPt.y, midPt.x, midPt.y);
+ curvePt = pt;
+ } else if (!prevPt.onCurve && pt.onCurve) {
+ // Previous point off-curve, this point on-curve.
+ p.quadraticCurveTo(curvePt.x, curvePt.y, pt.x, pt.y);
+ curvePt = null;
+ } else {
+ throw new Error('Invalid state.');
+ }
+ }
+
+ if (firstPt !== lastPt) {
+ // Connect the last and first points
+ if (curvePt) {
+ p.quadraticCurveTo(curvePt.x, curvePt.y, firstPt.x, firstPt.y);
+ } else {
+ p.lineTo(firstPt.x, firstPt.y);
+ }
+ }
+ }
+
+ p.closePath();
+ return p;
+}
+
+function buildPath(glyphs, glyph) {
+ if (glyph.isComposite) {
+ for (var j = 0; j < glyph.components.length; j += 1) {
+ var component = glyph.components[j];
+ var componentGlyph = glyphs.get(component.glyphIndex);
+ if (componentGlyph.points) {
+ var transformedPoints = transformPoints(componentGlyph.points, component);
+ glyph.points = glyph.points.concat(transformedPoints);
+ }
+ }
+ }
+
+ return getPath(glyph.points);
+}
+
+// Parse all the glyphs according to the offsets from the `loca` table.
+function parseGlyfTable(data, start, loca, font) {
+ var glyphs = new glyphset.GlyphSet(font);
+ var i;
+
+ // The last element of the loca table is invalid.
+ for (i = 0; i < loca.length - 1; i += 1) {
+ var offset = loca[i];
+ var nextOffset = loca[i + 1];
+ if (offset !== nextOffset) {
+ glyphs.push(i, glyphset.ttfGlyphLoader(font, i, parseGlyph, data, start + offset, buildPath));
+ } else {
+ glyphs.push(i, glyphset.glyphLoader(font, i));
+ }
+ }
+
+ return glyphs;
+}
+
+exports.parse = parseGlyfTable;
+
+},{"../check":2,"../glyphset":7,"../parse":9,"../path":10}],15:[function(_dereq_,module,exports){
+// The `GPOS` table contains kerning pairs, among other things.
+// https://www.microsoft.com/typography/OTSPEC/gpos.htm
+
+'use strict';
+
+var check = _dereq_('../check');
+var parse = _dereq_('../parse');
+
+// Parse ScriptList and FeatureList tables of GPOS, GSUB, GDEF, BASE, JSTF tables.
+// These lists are unused by now, this function is just the basis for a real parsing.
+function parseTaggedListTable(data, start) {
+ var p = new parse.Parser(data, start);
+ var n = p.parseUShort();
+ var list = [];
+ for (var i = 0; i < n; i++) {
+ list[p.parseTag()] = { offset: p.parseUShort() };
+ }
+
+ return list;
+}
+
+// Parse a coverage table in a GSUB, GPOS or GDEF table.
+// Format 1 is a simple list of glyph ids,
+// Format 2 is a list of ranges. It is expanded in a list of glyphs, maybe not the best idea.
+function parseCoverageTable(data, start) {
+ var p = new parse.Parser(data, start);
+ var format = p.parseUShort();
+ var count = p.parseUShort();
+ if (format === 1) {
+ return p.parseUShortList(count);
+ }
+ else if (format === 2) {
+ var coverage = [];
+ for (; count--;) {
+ var begin = p.parseUShort();
+ var end = p.parseUShort();
+ var index = p.parseUShort();
+ for (var i = begin; i <= end; i++) {
+ coverage[index++] = i;
+ }
+ }
+
+ return coverage;
+ }
+}
+
+// Parse a Class Definition Table in a GSUB, GPOS or GDEF table.
+// Returns a function that gets a class value from a glyph ID.
+function parseClassDefTable(data, start) {
+ var p = new parse.Parser(data, start);
+ var format = p.parseUShort();
+ if (format === 1) {
+ // Format 1 specifies a range of consecutive glyph indices, one class per glyph ID.
+ var startGlyph = p.parseUShort();
+ var glyphCount = p.parseUShort();
+ var classes = p.parseUShortList(glyphCount);
+ return function(glyphID) {
+ return classes[glyphID - startGlyph] || 0;
+ };
+ }
+ else if (format === 2) {
+ // Format 2 defines multiple groups of glyph indices that belong to the same class.
+ var rangeCount = p.parseUShort();
+ var startGlyphs = [];
+ var endGlyphs = [];
+ var classValues = [];
+ for (var i = 0; i < rangeCount; i++) {
+ startGlyphs[i] = p.parseUShort();
+ endGlyphs[i] = p.parseUShort();
+ classValues[i] = p.parseUShort();
+ }
+
+ return function(glyphID) {
+ var l = 0;
+ var r = startGlyphs.length - 1;
+ while (l < r) {
+ var c = (l + r + 1) >> 1;
+ if (glyphID < startGlyphs[c]) {
+ r = c - 1;
+ } else {
+ l = c;
+ }
+ }
+
+ if (startGlyphs[l] <= glyphID && glyphID <= endGlyphs[l]) {
+ return classValues[l] || 0;
+ }
+
+ return 0;
+ };
+ }
+}
+
+// Parse a pair adjustment positioning subtable, format 1 or format 2
+// The subtable is returned in the form of a lookup function.
+function parsePairPosSubTable(data, start) {
+ var p = new parse.Parser(data, start);
+ // This part is common to format 1 and format 2 subtables
+ var format = p.parseUShort();
+ var coverageOffset = p.parseUShort();
+ var coverage = parseCoverageTable(data, start + coverageOffset);
+ // valueFormat 4: XAdvance only, 1: XPlacement only, 0: no ValueRecord for second glyph
+ // Only valueFormat1=4 and valueFormat2=0 is supported.
+ var valueFormat1 = p.parseUShort();
+ var valueFormat2 = p.parseUShort();
+ var value1;
+ var value2;
+ if (valueFormat1 !== 4 || valueFormat2 !== 0) return;
+ var sharedPairSets = {};
+ if (format === 1) {
+ // Pair Positioning Adjustment: Format 1
+ var pairSetCount = p.parseUShort();
+ var pairSet = [];
+ // Array of offsets to PairSet tables-from beginning of PairPos subtable-ordered by Coverage Index
+ var pairSetOffsets = p.parseOffset16List(pairSetCount);
+ for (var firstGlyph = 0; firstGlyph < pairSetCount; firstGlyph++) {
+ var pairSetOffset = pairSetOffsets[firstGlyph];
+ var sharedPairSet = sharedPairSets[pairSetOffset];
+ if (!sharedPairSet) {
+ // Parse a pairset table in a pair adjustment subtable format 1
+ sharedPairSet = {};
+ p.relativeOffset = pairSetOffset;
+ var pairValueCount = p.parseUShort();
+ for (; pairValueCount--;) {
+ var secondGlyph = p.parseUShort();
+ if (valueFormat1) value1 = p.parseShort();
+ if (valueFormat2) value2 = p.parseShort();
+ // We only support valueFormat1 = 4 and valueFormat2 = 0,
+ // so value1 is the XAdvance and value2 is empty.
+ sharedPairSet[secondGlyph] = value1;
+ }
+ }
+
+ pairSet[coverage[firstGlyph]] = sharedPairSet;
+ }
+
+ return function(leftGlyph, rightGlyph) {
+ var pairs = pairSet[leftGlyph];
+ if (pairs) return pairs[rightGlyph];
+ };
+ }
+ else if (format === 2) {
+ // Pair Positioning Adjustment: Format 2
+ var classDef1Offset = p.parseUShort();
+ var classDef2Offset = p.parseUShort();
+ var class1Count = p.parseUShort();
+ var class2Count = p.parseUShort();
+ var getClass1 = parseClassDefTable(data, start + classDef1Offset);
+ var getClass2 = parseClassDefTable(data, start + classDef2Offset);
+
+ // Parse kerning values by class pair.
+ var kerningMatrix = [];
+ for (var i = 0; i < class1Count; i++) {
+ var kerningRow = kerningMatrix[i] = [];
+ for (var j = 0; j < class2Count; j++) {
+ if (valueFormat1) value1 = p.parseShort();
+ if (valueFormat2) value2 = p.parseShort();
+ // We only support valueFormat1 = 4 and valueFormat2 = 0,
+ // so value1 is the XAdvance and value2 is empty.
+ kerningRow[j] = value1;
+ }
+ }
+
+ // Convert coverage list to a hash
+ var covered = {};
+ for (i = 0; i < coverage.length; i++) covered[coverage[i]] = 1;
+
+ // Get the kerning value for a specific glyph pair.
+ return function(leftGlyph, rightGlyph) {
+ if (!covered[leftGlyph]) return;
+ var class1 = getClass1(leftGlyph);
+ var class2 = getClass2(rightGlyph);
+ var kerningRow = kerningMatrix[class1];
+
+ if (kerningRow) {
+ return kerningRow[class2];
+ }
+ };
+ }
+}
+
+// Parse a LookupTable (present in of GPOS, GSUB, GDEF, BASE, JSTF tables).
+function parseLookupTable(data, start) {
+ var p = new parse.Parser(data, start);
+ var lookupType = p.parseUShort();
+ var lookupFlag = p.parseUShort();
+ var useMarkFilteringSet = lookupFlag & 0x10;
+ var subTableCount = p.parseUShort();
+ var subTableOffsets = p.parseOffset16List(subTableCount);
+ var table = {
+ lookupType: lookupType,
+ lookupFlag: lookupFlag,
+ markFilteringSet: useMarkFilteringSet ? p.parseUShort() : -1
+ };
+ // LookupType 2, Pair adjustment
+ if (lookupType === 2) {
+ var subtables = [];
+ for (var i = 0; i < subTableCount; i++) {
+ subtables.push(parsePairPosSubTable(data, start + subTableOffsets[i]));
+ }
+ // Return a function which finds the kerning values in the subtables.
+ table.getKerningValue = function(leftGlyph, rightGlyph) {
+ for (var i = subtables.length; i--;) {
+ var value = subtables[i](leftGlyph, rightGlyph);
+ if (value !== undefined) return value;
+ }
+
+ return 0;
+ };
+ }
+
+ return table;
+}
+
+// Parse the `GPOS` table which contains, among other things, kerning pairs.
+// https://www.microsoft.com/typography/OTSPEC/gpos.htm
+function parseGposTable(data, start, font) {
+ var p = new parse.Parser(data, start);
+ var tableVersion = p.parseFixed();
+ check.argument(tableVersion === 1, 'Unsupported GPOS table version.');
+
+ // ScriptList and FeatureList - ignored for now
+ parseTaggedListTable(data, start + p.parseUShort());
+ // 'kern' is the feature we are looking for.
+ parseTaggedListTable(data, start + p.parseUShort());
+
+ // LookupList
+ var lookupListOffset = p.parseUShort();
+ p.relativeOffset = lookupListOffset;
+ var lookupCount = p.parseUShort();
+ var lookupTableOffsets = p.parseOffset16List(lookupCount);
+ var lookupListAbsoluteOffset = start + lookupListOffset;
+ for (var i = 0; i < lookupCount; i++) {
+ var table = parseLookupTable(data, lookupListAbsoluteOffset + lookupTableOffsets[i]);
+ if (table.lookupType === 2 && !font.getGposKerningValue) font.getGposKerningValue = table.getKerningValue;
+ }
+}
+
+exports.parse = parseGposTable;
+
+},{"../check":2,"../parse":9}],16:[function(_dereq_,module,exports){
+// The `head` table contains global information about the font.
+// https://www.microsoft.com/typography/OTSPEC/head.htm
+
+'use strict';
+
+var check = _dereq_('../check');
+var parse = _dereq_('../parse');
+var table = _dereq_('../table');
+
+// Parse the header `head` table
+function parseHeadTable(data, start) {
+ var head = {};
+ var p = new parse.Parser(data, start);
+ head.version = p.parseVersion();
+ head.fontRevision = Math.round(p.parseFixed() * 1000) / 1000;
+ head.checkSumAdjustment = p.parseULong();
+ head.magicNumber = p.parseULong();
+ check.argument(head.magicNumber === 0x5F0F3CF5, 'Font header has wrong magic number.');
+ head.flags = p.parseUShort();
+ head.unitsPerEm = p.parseUShort();
+ head.created = p.parseLongDateTime();
+ head.modified = p.parseLongDateTime();
+ head.xMin = p.parseShort();
+ head.yMin = p.parseShort();
+ head.xMax = p.parseShort();
+ head.yMax = p.parseShort();
+ head.macStyle = p.parseUShort();
+ head.lowestRecPPEM = p.parseUShort();
+ head.fontDirectionHint = p.parseShort();
+ head.indexToLocFormat = p.parseShort(); // 50
+ head.glyphDataFormat = p.parseShort();
+ return head;
+}
+
+function makeHeadTable(options) {
+ return new table.Table('head', [
+ {name: 'version', type: 'FIXED', value: 0x00010000},
+ {name: 'fontRevision', type: 'FIXED', value: 0x00010000},
+ {name: 'checkSumAdjustment', type: 'ULONG', value: 0},
+ {name: 'magicNumber', type: 'ULONG', value: 0x5F0F3CF5},
+ {name: 'flags', type: 'USHORT', value: 0},
+ {name: 'unitsPerEm', type: 'USHORT', value: 1000},
+ {name: 'created', type: 'LONGDATETIME', value: 0},
+ {name: 'modified', type: 'LONGDATETIME', value: 0},
+ {name: 'xMin', type: 'SHORT', value: 0},
+ {name: 'yMin', type: 'SHORT', value: 0},
+ {name: 'xMax', type: 'SHORT', value: 0},
+ {name: 'yMax', type: 'SHORT', value: 0},
+ {name: 'macStyle', type: 'USHORT', value: 0},
+ {name: 'lowestRecPPEM', type: 'USHORT', value: 0},
+ {name: 'fontDirectionHint', type: 'SHORT', value: 2},
+ {name: 'indexToLocFormat', type: 'SHORT', value: 0},
+ {name: 'glyphDataFormat', type: 'SHORT', value: 0}
+ ], options);
+}
+
+exports.parse = parseHeadTable;
+exports.make = makeHeadTable;
+
+},{"../check":2,"../parse":9,"../table":11}],17:[function(_dereq_,module,exports){
+// The `hhea` table contains information for horizontal layout.
+// https://www.microsoft.com/typography/OTSPEC/hhea.htm
+
+'use strict';
+
+var parse = _dereq_('../parse');
+var table = _dereq_('../table');
+
+// Parse the horizontal header `hhea` table
+function parseHheaTable(data, start) {
+ var hhea = {};
+ var p = new parse.Parser(data, start);
+ hhea.version = p.parseVersion();
+ hhea.ascender = p.parseShort();
+ hhea.descender = p.parseShort();
+ hhea.lineGap = p.parseShort();
+ hhea.advanceWidthMax = p.parseUShort();
+ hhea.minLeftSideBearing = p.parseShort();
+ hhea.minRightSideBearing = p.parseShort();
+ hhea.xMaxExtent = p.parseShort();
+ hhea.caretSlopeRise = p.parseShort();
+ hhea.caretSlopeRun = p.parseShort();
+ hhea.caretOffset = p.parseShort();
+ p.relativeOffset += 8;
+ hhea.metricDataFormat = p.parseShort();
+ hhea.numberOfHMetrics = p.parseUShort();
+ return hhea;
+}
+
+function makeHheaTable(options) {
+ return new table.Table('hhea', [
+ {name: 'version', type: 'FIXED', value: 0x00010000},
+ {name: 'ascender', type: 'FWORD', value: 0},
+ {name: 'descender', type: 'FWORD', value: 0},
+ {name: 'lineGap', type: 'FWORD', value: 0},
+ {name: 'advanceWidthMax', type: 'UFWORD', value: 0},
+ {name: 'minLeftSideBearing', type: 'FWORD', value: 0},
+ {name: 'minRightSideBearing', type: 'FWORD', value: 0},
+ {name: 'xMaxExtent', type: 'FWORD', value: 0},
+ {name: 'caretSlopeRise', type: 'SHORT', value: 1},
+ {name: 'caretSlopeRun', type: 'SHORT', value: 0},
+ {name: 'caretOffset', type: 'SHORT', value: 0},
+ {name: 'reserved1', type: 'SHORT', value: 0},
+ {name: 'reserved2', type: 'SHORT', value: 0},
+ {name: 'reserved3', type: 'SHORT', value: 0},
+ {name: 'reserved4', type: 'SHORT', value: 0},
+ {name: 'metricDataFormat', type: 'SHORT', value: 0},
+ {name: 'numberOfHMetrics', type: 'USHORT', value: 0}
+ ], options);
+}
+
+exports.parse = parseHheaTable;
+exports.make = makeHheaTable;
+
+},{"../parse":9,"../table":11}],18:[function(_dereq_,module,exports){
+// The `hmtx` table contains the horizontal metrics for all glyphs.
+// https://www.microsoft.com/typography/OTSPEC/hmtx.htm
+
+'use strict';
+
+var parse = _dereq_('../parse');
+var table = _dereq_('../table');
+
+// Parse the `hmtx` table, which contains the horizontal metrics for all glyphs.
+// This function augments the glyph array, adding the advanceWidth and leftSideBearing to each glyph.
+function parseHmtxTable(data, start, numMetrics, numGlyphs, glyphs) {
+ var advanceWidth;
+ var leftSideBearing;
+ var p = new parse.Parser(data, start);
+ for (var i = 0; i < numGlyphs; i += 1) {
+ // If the font is monospaced, only one entry is needed. This last entry applies to all subsequent glyphs.
+ if (i < numMetrics) {
+ advanceWidth = p.parseUShort();
+ leftSideBearing = p.parseShort();
+ }
+
+ var glyph = glyphs.get(i);
+ glyph.advanceWidth = advanceWidth;
+ glyph.leftSideBearing = leftSideBearing;
+ }
+}
+
+function makeHmtxTable(glyphs) {
+ var t = new table.Table('hmtx', []);
+ for (var i = 0; i < glyphs.length; i += 1) {
+ var glyph = glyphs.get(i);
+ var advanceWidth = glyph.advanceWidth || 0;
+ var leftSideBearing = glyph.leftSideBearing || 0;
+ t.fields.push({name: 'advanceWidth_' + i, type: 'USHORT', value: advanceWidth});
+ t.fields.push({name: 'leftSideBearing_' + i, type: 'SHORT', value: leftSideBearing});
+ }
+
+ return t;
+}
+
+exports.parse = parseHmtxTable;
+exports.make = makeHmtxTable;
+
+},{"../parse":9,"../table":11}],19:[function(_dereq_,module,exports){
+// The `kern` table contains kerning pairs.
+// Note that some fonts use the GPOS OpenType layout table to specify kerning.
+// https://www.microsoft.com/typography/OTSPEC/kern.htm
+
+'use strict';
+
+var check = _dereq_('../check');
+var parse = _dereq_('../parse');
+
+// Parse the `kern` table which contains kerning pairs.
+function parseKernTable(data, start) {
+ var pairs = {};
+ var p = new parse.Parser(data, start);
+ var tableVersion = p.parseUShort();
+ check.argument(tableVersion === 0, 'Unsupported kern table version.');
+ // Skip nTables.
+ p.skip('uShort', 1);
+ var subTableVersion = p.parseUShort();
+ check.argument(subTableVersion === 0, 'Unsupported kern sub-table version.');
+ // Skip subTableLength, subTableCoverage
+ p.skip('uShort', 2);
+ var nPairs = p.parseUShort();
+ // Skip searchRange, entrySelector, rangeShift.
+ p.skip('uShort', 3);
+ for (var i = 0; i < nPairs; i += 1) {
+ var leftIndex = p.parseUShort();
+ var rightIndex = p.parseUShort();
+ var value = p.parseShort();
+ pairs[leftIndex + ',' + rightIndex] = value;
+ }
+
+ return pairs;
+}
+
+exports.parse = parseKernTable;
+
+},{"../check":2,"../parse":9}],20:[function(_dereq_,module,exports){
+// The `loca` table stores the offsets to the locations of the glyphs in the font.
+// https://www.microsoft.com/typography/OTSPEC/loca.htm
+
+'use strict';
+
+var parse = _dereq_('../parse');
+
+// Parse the `loca` table. This table stores the offsets to the locations of the glyphs in the font,
+// relative to the beginning of the glyphData table.
+// The number of glyphs stored in the `loca` table is specified in the `maxp` table (under numGlyphs)
+// The loca table has two versions: a short version where offsets are stored as uShorts, and a long
+// version where offsets are stored as uLongs. The `head` table specifies which version to use
+// (under indexToLocFormat).
+function parseLocaTable(data, start, numGlyphs, shortVersion) {
+ var p = new parse.Parser(data, start);
+ var parseFn = shortVersion ? p.parseUShort : p.parseULong;
+ // There is an extra entry after the last index element to compute the length of the last glyph.
+ // That's why we use numGlyphs + 1.
+ var glyphOffsets = [];
+ for (var i = 0; i < numGlyphs + 1; i += 1) {
+ var glyphOffset = parseFn.call(p);
+ if (shortVersion) {
+ // The short table version stores the actual offset divided by 2.
+ glyphOffset *= 2;
+ }
+
+ glyphOffsets.push(glyphOffset);
+ }
+
+ return glyphOffsets;
+}
+
+exports.parse = parseLocaTable;
+
+},{"../parse":9}],21:[function(_dereq_,module,exports){
+// The `maxp` table establishes the memory requirements for the font.
+// We need it just to get the number of glyphs in the font.
+// https://www.microsoft.com/typography/OTSPEC/maxp.htm
+
+'use strict';
+
+var parse = _dereq_('../parse');
+var table = _dereq_('../table');
+
+// Parse the maximum profile `maxp` table.
+function parseMaxpTable(data, start) {
+ var maxp = {};
+ var p = new parse.Parser(data, start);
+ maxp.version = p.parseVersion();
+ maxp.numGlyphs = p.parseUShort();
+ if (maxp.version === 1.0) {
+ maxp.maxPoints = p.parseUShort();
+ maxp.maxContours = p.parseUShort();
+ maxp.maxCompositePoints = p.parseUShort();
+ maxp.maxCompositeContours = p.parseUShort();
+ maxp.maxZones = p.parseUShort();
+ maxp.maxTwilightPoints = p.parseUShort();
+ maxp.maxStorage = p.parseUShort();
+ maxp.maxFunctionDefs = p.parseUShort();
+ maxp.maxInstructionDefs = p.parseUShort();
+ maxp.maxStackElements = p.parseUShort();
+ maxp.maxSizeOfInstructions = p.parseUShort();
+ maxp.maxComponentElements = p.parseUShort();
+ maxp.maxComponentDepth = p.parseUShort();
+ }
+
+ return maxp;
+}
+
+function makeMaxpTable(numGlyphs) {
+ return new table.Table('maxp', [
+ {name: 'version', type: 'FIXED', value: 0x00005000},
+ {name: 'numGlyphs', type: 'USHORT', value: numGlyphs}
+ ]);
+}
+
+exports.parse = parseMaxpTable;
+exports.make = makeMaxpTable;
+
+},{"../parse":9,"../table":11}],22:[function(_dereq_,module,exports){
+// The `name` naming table.
+// https://www.microsoft.com/typography/OTSPEC/name.htm
+
+'use strict';
+
+var encode = _dereq_('../types').encode;
+var parse = _dereq_('../parse');
+var table = _dereq_('../table');
+
+// NameIDs for the name table.
+var nameTableNames = [
+ 'copyright', // 0
+ 'fontFamily', // 1
+ 'fontSubfamily', // 2
+ 'uniqueID', // 3
+ 'fullName', // 4
+ 'version', // 5
+ 'postScriptName', // 6
+ 'trademark', // 7
+ 'manufacturer', // 8
+ 'designer', // 9
+ 'description', // 10
+ 'manufacturerURL', // 11
+ 'designerURL', // 12
+ 'licence', // 13
+ 'licenceURL', // 14
+ 'reserved', // 15
+ 'preferredFamily', // 16
+ 'preferredSubfamily', // 17
+ 'compatibleFullName', // 18
+ 'sampleText', // 19
+ 'postScriptFindFontName', // 20
+ 'wwsFamily', // 21
+ 'wwsSubfamily' // 22
+];
+
+// Parse the naming `name` table
+// Only Windows Unicode English names are supported.
+// Format 1 additional fields are not supported
+function parseNameTable(data, start) {
+ var name = {};
+ var p = new parse.Parser(data, start);
+ name.format = p.parseUShort();
+ var count = p.parseUShort();
+ var stringOffset = p.offset + p.parseUShort();
+ var unknownCount = 0;
+ for (var i = 0; i < count; i++) {
+ var platformID = p.parseUShort();
+ var encodingID = p.parseUShort();
+ var languageID = p.parseUShort();
+ var nameID = p.parseUShort();
+ var property = nameTableNames[nameID];
+ var byteLength = p.parseUShort();
+ var offset = p.parseUShort();
+ // platformID - encodingID - languageID standard combinations :
+ // 1 - 0 - 0 : Macintosh, Roman, English
+ // 3 - 1 - 0x409 : Windows, Unicode BMP (UCS-2), en-US
+ if (platformID === 3 && encodingID === 1 && languageID === 0x409) {
+ var codePoints = [];
+ var length = byteLength / 2;
+ for (var j = 0; j < length; j++, offset += 2) {
+ codePoints[j] = parse.getShort(data, stringOffset + offset);
+ }
+
+ var str = String.fromCharCode.apply(null, codePoints);
+ if (property) {
+ name[property] = str;
+ }
+ else {
+ unknownCount++;
+ name['unknown' + unknownCount] = str;
+ }
+ }
+
+ }
+
+ if (name.format === 1) {
+ name.langTagCount = p.parseUShort();
+ }
+
+ return name;
+}
+
+function makeNameRecord(platformID, encodingID, languageID, nameID, length, offset) {
+ return new table.Table('NameRecord', [
+ {name: 'platformID', type: 'USHORT', value: platformID},
+ {name: 'encodingID', type: 'USHORT', value: encodingID},
+ {name: 'languageID', type: 'USHORT', value: languageID},
+ {name: 'nameID', type: 'USHORT', value: nameID},
+ {name: 'length', type: 'USHORT', value: length},
+ {name: 'offset', type: 'USHORT', value: offset}
+ ]);
+}
+
+function addMacintoshNameRecord(t, recordID, s, offset) {
+ // Macintosh, Roman, English
+ var stringBytes = encode.STRING(s);
+ t.records.push(makeNameRecord(1, 0, 0, recordID, stringBytes.length, offset));
+ t.strings.push(stringBytes);
+ offset += stringBytes.length;
+ return offset;
+}
+
+function addWindowsNameRecord(t, recordID, s, offset) {
+ // Windows, Unicode BMP (UCS-2), US English
+ var utf16Bytes = encode.UTF16(s);
+ t.records.push(makeNameRecord(3, 1, 0x0409, recordID, utf16Bytes.length, offset));
+ t.strings.push(utf16Bytes);
+ offset += utf16Bytes.length;
+ return offset;
+}
+
+function makeNameTable(options) {
+ var t = new table.Table('name', [
+ {name: 'format', type: 'USHORT', value: 0},
+ {name: 'count', type: 'USHORT', value: 0},
+ {name: 'stringOffset', type: 'USHORT', value: 0}
+ ]);
+ t.records = [];
+ t.strings = [];
+ var offset = 0;
+ var i;
+ var s;
+ // Add Macintosh records first
+ for (i = 0; i < nameTableNames.length; i += 1) {
+ if (options[nameTableNames[i]] !== undefined) {
+ s = options[nameTableNames[i]];
+ offset = addMacintoshNameRecord(t, i, s, offset);
+ }
+ }
+ // Then add Windows records
+ for (i = 0; i < nameTableNames.length; i += 1) {
+ if (options[nameTableNames[i]] !== undefined) {
+ s = options[nameTableNames[i]];
+ offset = addWindowsNameRecord(t, i, s, offset);
+ }
+ }
+
+ t.count = t.records.length;
+ t.stringOffset = 6 + t.count * 12;
+ for (i = 0; i < t.records.length; i += 1) {
+ t.fields.push({name: 'record_' + i, type: 'TABLE', value: t.records[i]});
+ }
+
+ for (i = 0; i < t.strings.length; i += 1) {
+ t.fields.push({name: 'string_' + i, type: 'LITERAL', value: t.strings[i]});
+ }
+
+ return t;
+}
+
+exports.parse = parseNameTable;
+exports.make = makeNameTable;
+
+},{"../parse":9,"../table":11,"../types":26}],23:[function(_dereq_,module,exports){
+// The `OS/2` table contains metrics required in OpenType fonts.
+// https://www.microsoft.com/typography/OTSPEC/os2.htm
+
+'use strict';
+
+var parse = _dereq_('../parse');
+var table = _dereq_('../table');
+
+var unicodeRanges = [
+ {begin: 0x0000, end: 0x007F}, // Basic Latin
+ {begin: 0x0080, end: 0x00FF}, // Latin-1 Supplement
+ {begin: 0x0100, end: 0x017F}, // Latin Extended-A
+ {begin: 0x0180, end: 0x024F}, // Latin Extended-B
+ {begin: 0x0250, end: 0x02AF}, // IPA Extensions
+ {begin: 0x02B0, end: 0x02FF}, // Spacing Modifier Letters
+ {begin: 0x0300, end: 0x036F}, // Combining Diacritical Marks
+ {begin: 0x0370, end: 0x03FF}, // Greek and Coptic
+ {begin: 0x2C80, end: 0x2CFF}, // Coptic
+ {begin: 0x0400, end: 0x04FF}, // Cyrillic
+ {begin: 0x0530, end: 0x058F}, // Armenian
+ {begin: 0x0590, end: 0x05FF}, // Hebrew
+ {begin: 0xA500, end: 0xA63F}, // Vai
+ {begin: 0x0600, end: 0x06FF}, // Arabic
+ {begin: 0x07C0, end: 0x07FF}, // NKo
+ {begin: 0x0900, end: 0x097F}, // Devanagari
+ {begin: 0x0980, end: 0x09FF}, // Bengali
+ {begin: 0x0A00, end: 0x0A7F}, // Gurmukhi
+ {begin: 0x0A80, end: 0x0AFF}, // Gujarati
+ {begin: 0x0B00, end: 0x0B7F}, // Oriya
+ {begin: 0x0B80, end: 0x0BFF}, // Tamil
+ {begin: 0x0C00, end: 0x0C7F}, // Telugu
+ {begin: 0x0C80, end: 0x0CFF}, // Kannada
+ {begin: 0x0D00, end: 0x0D7F}, // Malayalam
+ {begin: 0x0E00, end: 0x0E7F}, // Thai
+ {begin: 0x0E80, end: 0x0EFF}, // Lao
+ {begin: 0x10A0, end: 0x10FF}, // Georgian
+ {begin: 0x1B00, end: 0x1B7F}, // Balinese
+ {begin: 0x1100, end: 0x11FF}, // Hangul Jamo
+ {begin: 0x1E00, end: 0x1EFF}, // Latin Extended Additional
+ {begin: 0x1F00, end: 0x1FFF}, // Greek Extended
+ {begin: 0x2000, end: 0x206F}, // General Punctuation
+ {begin: 0x2070, end: 0x209F}, // Superscripts And Subscripts
+ {begin: 0x20A0, end: 0x20CF}, // Currency Symbol
+ {begin: 0x20D0, end: 0x20FF}, // Combining Diacritical Marks For Symbols
+ {begin: 0x2100, end: 0x214F}, // Letterlike Symbols
+ {begin: 0x2150, end: 0x218F}, // Number Forms
+ {begin: 0x2190, end: 0x21FF}, // Arrows
+ {begin: 0x2200, end: 0x22FF}, // Mathematical Operators
+ {begin: 0x2300, end: 0x23FF}, // Miscellaneous Technical
+ {begin: 0x2400, end: 0x243F}, // Control Pictures
+ {begin: 0x2440, end: 0x245F}, // Optical Character Recognition
+ {begin: 0x2460, end: 0x24FF}, // Enclosed Alphanumerics
+ {begin: 0x2500, end: 0x257F}, // Box Drawing
+ {begin: 0x2580, end: 0x259F}, // Block Elements
+ {begin: 0x25A0, end: 0x25FF}, // Geometric Shapes
+ {begin: 0x2600, end: 0x26FF}, // Miscellaneous Symbols
+ {begin: 0x2700, end: 0x27BF}, // Dingbats
+ {begin: 0x3000, end: 0x303F}, // CJK Symbols And Punctuation
+ {begin: 0x3040, end: 0x309F}, // Hiragana
+ {begin: 0x30A0, end: 0x30FF}, // Katakana
+ {begin: 0x3100, end: 0x312F}, // Bopomofo
+ {begin: 0x3130, end: 0x318F}, // Hangul Compatibility Jamo
+ {begin: 0xA840, end: 0xA87F}, // Phags-pa
+ {begin: 0x3200, end: 0x32FF}, // Enclosed CJK Letters And Months
+ {begin: 0x3300, end: 0x33FF}, // CJK Compatibility
+ {begin: 0xAC00, end: 0xD7AF}, // Hangul Syllables
+ {begin: 0xD800, end: 0xDFFF}, // Non-Plane 0 *
+ {begin: 0x10900, end: 0x1091F}, // Phoenicia
+ {begin: 0x4E00, end: 0x9FFF}, // CJK Unified Ideographs
+ {begin: 0xE000, end: 0xF8FF}, // Private Use Area (plane 0)
+ {begin: 0x31C0, end: 0x31EF}, // CJK Strokes
+ {begin: 0xFB00, end: 0xFB4F}, // Alphabetic Presentation Forms
+ {begin: 0xFB50, end: 0xFDFF}, // Arabic Presentation Forms-A
+ {begin: 0xFE20, end: 0xFE2F}, // Combining Half Marks
+ {begin: 0xFE10, end: 0xFE1F}, // Vertical Forms
+ {begin: 0xFE50, end: 0xFE6F}, // Small Form Variants
+ {begin: 0xFE70, end: 0xFEFF}, // Arabic Presentation Forms-B
+ {begin: 0xFF00, end: 0xFFEF}, // Halfwidth And Fullwidth Forms
+ {begin: 0xFFF0, end: 0xFFFF}, // Specials
+ {begin: 0x0F00, end: 0x0FFF}, // Tibetan
+ {begin: 0x0700, end: 0x074F}, // Syriac
+ {begin: 0x0780, end: 0x07BF}, // Thaana
+ {begin: 0x0D80, end: 0x0DFF}, // Sinhala
+ {begin: 0x1000, end: 0x109F}, // Myanmar
+ {begin: 0x1200, end: 0x137F}, // Ethiopic
+ {begin: 0x13A0, end: 0x13FF}, // Cherokee
+ {begin: 0x1400, end: 0x167F}, // Unified Canadian Aboriginal Syllabics
+ {begin: 0x1680, end: 0x169F}, // Ogham
+ {begin: 0x16A0, end: 0x16FF}, // Runic
+ {begin: 0x1780, end: 0x17FF}, // Khmer
+ {begin: 0x1800, end: 0x18AF}, // Mongolian
+ {begin: 0x2800, end: 0x28FF}, // Braille Patterns
+ {begin: 0xA000, end: 0xA48F}, // Yi Syllables
+ {begin: 0x1700, end: 0x171F}, // Tagalog
+ {begin: 0x10300, end: 0x1032F}, // Old Italic
+ {begin: 0x10330, end: 0x1034F}, // Gothic
+ {begin: 0x10400, end: 0x1044F}, // Deseret
+ {begin: 0x1D000, end: 0x1D0FF}, // Byzantine Musical Symbols
+ {begin: 0x1D400, end: 0x1D7FF}, // Mathematical Alphanumeric Symbols
+ {begin: 0xFF000, end: 0xFFFFD}, // Private Use (plane 15)
+ {begin: 0xFE00, end: 0xFE0F}, // Variation Selectors
+ {begin: 0xE0000, end: 0xE007F}, // Tags
+ {begin: 0x1900, end: 0x194F}, // Limbu
+ {begin: 0x1950, end: 0x197F}, // Tai Le
+ {begin: 0x1980, end: 0x19DF}, // New Tai Lue
+ {begin: 0x1A00, end: 0x1A1F}, // Buginese
+ {begin: 0x2C00, end: 0x2C5F}, // Glagolitic
+ {begin: 0x2D30, end: 0x2D7F}, // Tifinagh
+ {begin: 0x4DC0, end: 0x4DFF}, // Yijing Hexagram Symbols
+ {begin: 0xA800, end: 0xA82F}, // Syloti Nagri
+ {begin: 0x10000, end: 0x1007F}, // Linear B Syllabary
+ {begin: 0x10140, end: 0x1018F}, // Ancient Greek Numbers
+ {begin: 0x10380, end: 0x1039F}, // Ugaritic
+ {begin: 0x103A0, end: 0x103DF}, // Old Persian
+ {begin: 0x10450, end: 0x1047F}, // Shavian
+ {begin: 0x10480, end: 0x104AF}, // Osmanya
+ {begin: 0x10800, end: 0x1083F}, // Cypriot Syllabary
+ {begin: 0x10A00, end: 0x10A5F}, // Kharoshthi
+ {begin: 0x1D300, end: 0x1D35F}, // Tai Xuan Jing Symbols
+ {begin: 0x12000, end: 0x123FF}, // Cuneiform
+ {begin: 0x1D360, end: 0x1D37F}, // Counting Rod Numerals
+ {begin: 0x1B80, end: 0x1BBF}, // Sundanese
+ {begin: 0x1C00, end: 0x1C4F}, // Lepcha
+ {begin: 0x1C50, end: 0x1C7F}, // Ol Chiki
+ {begin: 0xA880, end: 0xA8DF}, // Saurashtra
+ {begin: 0xA900, end: 0xA92F}, // Kayah Li
+ {begin: 0xA930, end: 0xA95F}, // Rejang
+ {begin: 0xAA00, end: 0xAA5F}, // Cham
+ {begin: 0x10190, end: 0x101CF}, // Ancient Symbols
+ {begin: 0x101D0, end: 0x101FF}, // Phaistos Disc
+ {begin: 0x102A0, end: 0x102DF}, // Carian
+ {begin: 0x1F030, end: 0x1F09F} // Domino Tiles
+];
+
+function getUnicodeRange(unicode) {
+ for (var i = 0; i < unicodeRanges.length; i += 1) {
+ var range = unicodeRanges[i];
+ if (unicode >= range.begin && unicode < range.end) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+// Parse the OS/2 and Windows metrics `OS/2` table
+function parseOS2Table(data, start) {
+ var os2 = {};
+ var p = new parse.Parser(data, start);
+ os2.version = p.parseUShort();
+ os2.xAvgCharWidth = p.parseShort();
+ os2.usWeightClass = p.parseUShort();
+ os2.usWidthClass = p.parseUShort();
+ os2.fsType = p.parseUShort();
+ os2.ySubscriptXSize = p.parseShort();
+ os2.ySubscriptYSize = p.parseShort();
+ os2.ySubscriptXOffset = p.parseShort();
+ os2.ySubscriptYOffset = p.parseShort();
+ os2.ySuperscriptXSize = p.parseShort();
+ os2.ySuperscriptYSize = p.parseShort();
+ os2.ySuperscriptXOffset = p.parseShort();
+ os2.ySuperscriptYOffset = p.parseShort();
+ os2.yStrikeoutSize = p.parseShort();
+ os2.yStrikeoutPosition = p.parseShort();
+ os2.sFamilyClass = p.parseShort();
+ os2.panose = [];
+ for (var i = 0; i < 10; i++) {
+ os2.panose[i] = p.parseByte();
+ }
+
+ os2.ulUnicodeRange1 = p.parseULong();
+ os2.ulUnicodeRange2 = p.parseULong();
+ os2.ulUnicodeRange3 = p.parseULong();
+ os2.ulUnicodeRange4 = p.parseULong();
+ os2.achVendID = String.fromCharCode(p.parseByte(), p.parseByte(), p.parseByte(), p.parseByte());
+ os2.fsSelection = p.parseUShort();
+ os2.usFirstCharIndex = p.parseUShort();
+ os2.usLastCharIndex = p.parseUShort();
+ os2.sTypoAscender = p.parseShort();
+ os2.sTypoDescender = p.parseShort();
+ os2.sTypoLineGap = p.parseShort();
+ os2.usWinAscent = p.parseUShort();
+ os2.usWinDescent = p.parseUShort();
+ if (os2.version >= 1) {
+ os2.ulCodePageRange1 = p.parseULong();
+ os2.ulCodePageRange2 = p.parseULong();
+ }
+
+ if (os2.version >= 2) {
+ os2.sxHeight = p.parseShort();
+ os2.sCapHeight = p.parseShort();
+ os2.usDefaultChar = p.parseUShort();
+ os2.usBreakChar = p.parseUShort();
+ os2.usMaxContent = p.parseUShort();
+ }
+
+ return os2;
+}
+
+function makeOS2Table(options) {
+ return new table.Table('OS/2', [
+ {name: 'version', type: 'USHORT', value: 0x0003},
+ {name: 'xAvgCharWidth', type: 'SHORT', value: 0},
+ {name: 'usWeightClass', type: 'USHORT', value: 0},
+ {name: 'usWidthClass', type: 'USHORT', value: 0},
+ {name: 'fsType', type: 'USHORT', value: 0},
+ {name: 'ySubscriptXSize', type: 'SHORT', value: 650},
+ {name: 'ySubscriptYSize', type: 'SHORT', value: 699},
+ {name: 'ySubscriptXOffset', type: 'SHORT', value: 0},
+ {name: 'ySubscriptYOffset', type: 'SHORT', value: 140},
+ {name: 'ySuperscriptXSize', type: 'SHORT', value: 650},
+ {name: 'ySuperscriptYSize', type: 'SHORT', value: 699},
+ {name: 'ySuperscriptXOffset', type: 'SHORT', value: 0},
+ {name: 'ySuperscriptYOffset', type: 'SHORT', value: 479},
+ {name: 'yStrikeoutSize', type: 'SHORT', value: 49},
+ {name: 'yStrikeoutPosition', type: 'SHORT', value: 258},
+ {name: 'sFamilyClass', type: 'SHORT', value: 0},
+ {name: 'bFamilyType', type: 'BYTE', value: 0},
+ {name: 'bSerifStyle', type: 'BYTE', value: 0},
+ {name: 'bWeight', type: 'BYTE', value: 0},
+ {name: 'bProportion', type: 'BYTE', value: 0},
+ {name: 'bContrast', type: 'BYTE', value: 0},
+ {name: 'bStrokeVariation', type: 'BYTE', value: 0},
+ {name: 'bArmStyle', type: 'BYTE', value: 0},
+ {name: 'bLetterform', type: 'BYTE', value: 0},
+ {name: 'bMidline', type: 'BYTE', value: 0},
+ {name: 'bXHeight', type: 'BYTE', value: 0},
+ {name: 'ulUnicodeRange1', type: 'ULONG', value: 0},
+ {name: 'ulUnicodeRange2', type: 'ULONG', value: 0},
+ {name: 'ulUnicodeRange3', type: 'ULONG', value: 0},
+ {name: 'ulUnicodeRange4', type: 'ULONG', value: 0},
+ {name: 'achVendID', type: 'CHARARRAY', value: 'XXXX'},
+ {name: 'fsSelection', type: 'USHORT', value: 0},
+ {name: 'usFirstCharIndex', type: 'USHORT', value: 0},
+ {name: 'usLastCharIndex', type: 'USHORT', value: 0},
+ {name: 'sTypoAscender', type: 'SHORT', value: 0},
+ {name: 'sTypoDescender', type: 'SHORT', value: 0},
+ {name: 'sTypoLineGap', type: 'SHORT', value: 0},
+ {name: 'usWinAscent', type: 'USHORT', value: 0},
+ {name: 'usWinDescent', type: 'USHORT', value: 0},
+ {name: 'ulCodePageRange1', type: 'ULONG', value: 0},
+ {name: 'ulCodePageRange2', type: 'ULONG', value: 0},
+ {name: 'sxHeight', type: 'SHORT', value: 0},
+ {name: 'sCapHeight', type: 'SHORT', value: 0},
+ {name: 'usDefaultChar', type: 'USHORT', value: 0},
+ {name: 'usBreakChar', type: 'USHORT', value: 0},
+ {name: 'usMaxContext', type: 'USHORT', value: 0}
+ ], options);
+}
+
+exports.unicodeRanges = unicodeRanges;
+exports.getUnicodeRange = getUnicodeRange;
+exports.parse = parseOS2Table;
+exports.make = makeOS2Table;
+
+},{"../parse":9,"../table":11}],24:[function(_dereq_,module,exports){
+// The `post` table stores additional PostScript information, such as glyph names.
+// https://www.microsoft.com/typography/OTSPEC/post.htm
+
+'use strict';
+
+var encoding = _dereq_('../encoding');
+var parse = _dereq_('../parse');
+var table = _dereq_('../table');
+
+// Parse the PostScript `post` table
+function parsePostTable(data, start) {
+ var post = {};
+ var p = new parse.Parser(data, start);
+ var i;
+ post.version = p.parseVersion();
+ post.italicAngle = p.parseFixed();
+ post.underlinePosition = p.parseShort();
+ post.underlineThickness = p.parseShort();
+ post.isFixedPitch = p.parseULong();
+ post.minMemType42 = p.parseULong();
+ post.maxMemType42 = p.parseULong();
+ post.minMemType1 = p.parseULong();
+ post.maxMemType1 = p.parseULong();
+ switch (post.version) {
+ case 1:
+ post.names = encoding.standardNames.slice();
+ break;
+ case 2:
+ post.numberOfGlyphs = p.parseUShort();
+ post.glyphNameIndex = new Array(post.numberOfGlyphs);
+ for (i = 0; i < post.numberOfGlyphs; i++) {
+ post.glyphNameIndex[i] = p.parseUShort();
+ }
+
+ post.names = [];
+ for (i = 0; i < post.numberOfGlyphs; i++) {
+ if (post.glyphNameIndex[i] >= encoding.standardNames.length) {
+ var nameLength = p.parseChar();
+ post.names.push(p.parseString(nameLength));
+ }
+ }
+
+ break;
+ case 2.5:
+ post.numberOfGlyphs = p.parseUShort();
+ post.offset = new Array(post.numberOfGlyphs);
+ for (i = 0; i < post.numberOfGlyphs; i++) {
+ post.offset[i] = p.parseChar();
+ }
+
+ break;
+ }
+ return post;
+}
+
+function makePostTable() {
+ return new table.Table('post', [
+ {name: 'version', type: 'FIXED', value: 0x00030000},
+ {name: 'italicAngle', type: 'FIXED', value: 0},
+ {name: 'underlinePosition', type: 'FWORD', value: 0},
+ {name: 'underlineThickness', type: 'FWORD', value: 0},
+ {name: 'isFixedPitch', type: 'ULONG', value: 0},
+ {name: 'minMemType42', type: 'ULONG', value: 0},
+ {name: 'maxMemType42', type: 'ULONG', value: 0},
+ {name: 'minMemType1', type: 'ULONG', value: 0},
+ {name: 'maxMemType1', type: 'ULONG', value: 0}
+ ]);
+}
+
+exports.parse = parsePostTable;
+exports.make = makePostTable;
+
+},{"../encoding":4,"../parse":9,"../table":11}],25:[function(_dereq_,module,exports){
+// The `sfnt` wrapper provides organization for the tables in the font.
+// It is the top-level data structure in a font.
+// https://www.microsoft.com/typography/OTSPEC/otff.htm
+// Recommendations for creating OpenType Fonts:
+// http://www.microsoft.com/typography/otspec140/recom.htm
+
+'use strict';
+
+var check = _dereq_('../check');
+var table = _dereq_('../table');
+
+var cmap = _dereq_('./cmap');
+var cff = _dereq_('./cff');
+var head = _dereq_('./head');
+var hhea = _dereq_('./hhea');
+var hmtx = _dereq_('./hmtx');
+var maxp = _dereq_('./maxp');
+var _name = _dereq_('./name');
+var os2 = _dereq_('./os2');
+var post = _dereq_('./post');
+
+function log2(v) {
+ return Math.log(v) / Math.log(2) | 0;
+}
+
+function computeCheckSum(bytes) {
+ while (bytes.length % 4 !== 0) {
+ bytes.push(0);
+ }
+
+ var sum = 0;
+ for (var i = 0; i < bytes.length; i += 4) {
+ sum += (bytes[i] << 24) +
+ (bytes[i + 1] << 16) +
+ (bytes[i + 2] << 8) +
+ (bytes[i + 3]);
+ }
+
+ sum %= Math.pow(2, 32);
+ return sum;
+}
+
+function makeTableRecord(tag, checkSum, offset, length) {
+ return new table.Table('Table Record', [
+ {name: 'tag', type: 'TAG', value: tag !== undefined ? tag : ''},
+ {name: 'checkSum', type: 'ULONG', value: checkSum !== undefined ? checkSum : 0},
+ {name: 'offset', type: 'ULONG', value: offset !== undefined ? offset : 0},
+ {name: 'length', type: 'ULONG', value: length !== undefined ? length : 0}
+ ]);
+}
+
+function makeSfntTable(tables) {
+ var sfnt = new table.Table('sfnt', [
+ {name: 'version', type: 'TAG', value: 'OTTO'},
+ {name: 'numTables', type: 'USHORT', value: 0},
+ {name: 'searchRange', type: 'USHORT', value: 0},
+ {name: 'entrySelector', type: 'USHORT', value: 0},
+ {name: 'rangeShift', type: 'USHORT', value: 0}
+ ]);
+ sfnt.tables = tables;
+ sfnt.numTables = tables.length;
+ var highestPowerOf2 = Math.pow(2, log2(sfnt.numTables));
+ sfnt.searchRange = 16 * highestPowerOf2;
+ sfnt.entrySelector = log2(highestPowerOf2);
+ sfnt.rangeShift = sfnt.numTables * 16 - sfnt.searchRange;
+
+ var recordFields = [];
+ var tableFields = [];
+
+ var offset = sfnt.sizeOf() + (makeTableRecord().sizeOf() * sfnt.numTables);
+ while (offset % 4 !== 0) {
+ offset += 1;
+ tableFields.push({name: 'padding', type: 'BYTE', value: 0});
+ }
+
+ for (var i = 0; i < tables.length; i += 1) {
+ var t = tables[i];
+ check.argument(t.tableName.length === 4, 'Table name' + t.tableName + ' is invalid.');
+ var tableLength = t.sizeOf();
+ var tableRecord = makeTableRecord(t.tableName, computeCheckSum(t.encode()), offset, tableLength);
+ recordFields.push({name: tableRecord.tag + ' Table Record', type: 'TABLE', value: tableRecord});
+ tableFields.push({name: t.tableName + ' table', type: 'TABLE', value: t});
+ offset += tableLength;
+ check.argument(!isNaN(offset), 'Something went wrong calculating the offset.');
+ while (offset % 4 !== 0) {
+ offset += 1;
+ tableFields.push({name: 'padding', type: 'BYTE', value: 0});
+ }
+ }
+
+ // Table records need to be sorted alphabetically.
+ recordFields.sort(function(r1, r2) {
+ if (r1.value.tag > r2.value.tag) {
+ return 1;
+ } else {
+ return -1;
+ }
+ });
+
+ sfnt.fields = sfnt.fields.concat(recordFields);
+ sfnt.fields = sfnt.fields.concat(tableFields);
+ return sfnt;
+}
+
+// Get the metrics for a character. If the string has more than one character
+// this function returns metrics for the first available character.
+// You can provide optional fallback metrics if no characters are available.
+function metricsForChar(font, chars, notFoundMetrics) {
+ for (var i = 0; i < chars.length; i += 1) {
+ var glyphIndex = font.charToGlyphIndex(chars[i]);
+ if (glyphIndex > 0) {
+ var glyph = font.glyphs.get(glyphIndex);
+ return glyph.getMetrics();
+ }
+ }
+
+ return notFoundMetrics;
+}
+
+function average(vs) {
+ var sum = 0;
+ for (var i = 0; i < vs.length; i += 1) {
+ sum += vs[i];
+ }
+
+ return sum / vs.length;
+}
+
+// Convert the font object to a SFNT data structure.
+// This structure contains all the necessary tables and metadata to create a binary OTF file.
+function fontToSfntTable(font) {
+ var xMins = [];
+ var yMins = [];
+ var xMaxs = [];
+ var yMaxs = [];
+ var advanceWidths = [];
+ var leftSideBearings = [];
+ var rightSideBearings = [];
+ var firstCharIndex;
+ var lastCharIndex = 0;
+ var ulUnicodeRange1 = 0;
+ var ulUnicodeRange2 = 0;
+ var ulUnicodeRange3 = 0;
+ var ulUnicodeRange4 = 0;
+
+ for (var i = 0; i < font.glyphs.length; i += 1) {
+ var glyph = font.glyphs.get(i);
+ var unicode = glyph.unicode | 0;
+ if (firstCharIndex > unicode || firstCharIndex === null) {
+ firstCharIndex = unicode;
+ }
+
+ if (lastCharIndex < unicode) {
+ lastCharIndex = unicode;
+ }
+
+ var position = os2.getUnicodeRange(unicode);
+ if (position < 32) {
+ ulUnicodeRange1 |= 1 << position;
+ } else if (position < 64) {
+ ulUnicodeRange2 |= 1 << position - 32;
+ } else if (position < 96) {
+ ulUnicodeRange3 |= 1 << position - 64;
+ } else if (position < 123) {
+ ulUnicodeRange4 |= 1 << position - 96;
+ } else {
+ throw new Error('Unicode ranges bits > 123 are reserved for internal usage');
+ }
+ // Skip non-important characters.
+ if (glyph.name === '.notdef') continue;
+ var metrics = glyph.getMetrics();
+ xMins.push(metrics.xMin);
+ yMins.push(metrics.yMin);
+ xMaxs.push(metrics.xMax);
+ yMaxs.push(metrics.yMax);
+ leftSideBearings.push(metrics.leftSideBearing);
+ rightSideBearings.push(metrics.rightSideBearing);
+ advanceWidths.push(glyph.advanceWidth);
+ }
+
+ var globals = {
+ xMin: Math.min.apply(null, xMins),
+ yMin: Math.min.apply(null, yMins),
+ xMax: Math.max.apply(null, xMaxs),
+ yMax: Math.max.apply(null, yMaxs),
+ advanceWidthMax: Math.max.apply(null, advanceWidths),
+ advanceWidthAvg: average(advanceWidths),
+ minLeftSideBearing: Math.min.apply(null, leftSideBearings),
+ maxLeftSideBearing: Math.max.apply(null, leftSideBearings),
+ minRightSideBearing: Math.min.apply(null, rightSideBearings)
+ };
+ globals.ascender = font.ascender !== undefined ? font.ascender : globals.yMax;
+ globals.descender = font.descender !== undefined ? font.descender : globals.yMin;
+
+ var headTable = head.make({
+ unitsPerEm: font.unitsPerEm,
+ xMin: globals.xMin,
+ yMin: globals.yMin,
+ xMax: globals.xMax,
+ yMax: globals.yMax
+ });
+
+ var hheaTable = hhea.make({
+ ascender: globals.ascender,
+ descender: globals.descender,
+ advanceWidthMax: globals.advanceWidthMax,
+ minLeftSideBearing: globals.minLeftSideBearing,
+ minRightSideBearing: globals.minRightSideBearing,
+ xMaxExtent: globals.maxLeftSideBearing + (globals.xMax - globals.xMin),
+ numberOfHMetrics: font.glyphs.length
+ });
+
+ var maxpTable = maxp.make(font.glyphs.length);
+
+ var os2Table = os2.make({
+ xAvgCharWidth: Math.round(globals.advanceWidthAvg),
+ usWeightClass: 500, // Medium FIXME Make this configurable
+ usWidthClass: 5, // Medium (normal) FIXME Make this configurable
+ usFirstCharIndex: firstCharIndex,
+ usLastCharIndex: lastCharIndex,
+ ulUnicodeRange1: ulUnicodeRange1,
+ ulUnicodeRange2: ulUnicodeRange2,
+ ulUnicodeRange3: ulUnicodeRange3,
+ ulUnicodeRange4: ulUnicodeRange4,
+ // See http://typophile.com/node/13081 for more info on vertical metrics.
+ // We get metrics for typical characters (such as "x" for xHeight).
+ // We provide some fallback characters if characters are unavailable: their
+ // ordering was chosen experimentally.
+ sTypoAscender: globals.ascender,
+ sTypoDescender: globals.descender,
+ sTypoLineGap: 0,
+ usWinAscent: globals.ascender,
+ usWinDescent: -globals.descender,
+ sxHeight: metricsForChar(font, 'xyvw', {yMax: 0}).yMax,
+ sCapHeight: metricsForChar(font, 'HIKLEFJMNTZBDPRAGOQSUVWXY', globals).yMax,
+ usBreakChar: font.hasChar(' ') ? 32 : 0 // Use space as the break character, if available.
+ });
+
+ var hmtxTable = hmtx.make(font.glyphs);
+ var cmapTable = cmap.make(font.glyphs);
+
+ var fullName = font.familyName + ' ' + font.styleName;
+ var postScriptName = font.familyName.replace(/\s/g, '') + '-' + font.styleName;
+ var nameTable = _name.make({
+ copyright: font.copyright,
+ fontFamily: font.familyName,
+ fontSubfamily: font.styleName,
+ uniqueID: font.manufacturer + ':' + fullName,
+ fullName: fullName,
+ version: font.version,
+ postScriptName: postScriptName,
+ trademark: font.trademark,
+ manufacturer: font.manufacturer,
+ designer: font.designer,
+ description: font.description,
+ manufacturerURL: font.manufacturerURL,
+ designerURL: font.designerURL,
+ license: font.license,
+ licenseURL: font.licenseURL,
+ preferredFamily: font.familyName,
+ preferredSubfamily: font.styleName
+ });
+ var postTable = post.make();
+ var cffTable = cff.make(font.glyphs, {
+ version: font.version,
+ fullName: fullName,
+ familyName: font.familyName,
+ weightName: font.styleName,
+ postScriptName: postScriptName,
+ unitsPerEm: font.unitsPerEm
+ });
+ // Order the tables according to the the OpenType specification 1.4.
+ var tables = [headTable, hheaTable, maxpTable, os2Table, nameTable, cmapTable, postTable, cffTable, hmtxTable];
+
+ var sfntTable = makeSfntTable(tables);
+
+ // Compute the font's checkSum and store it in head.checkSumAdjustment.
+ var bytes = sfntTable.encode();
+ var checkSum = computeCheckSum(bytes);
+ var tableFields = sfntTable.fields;
+ var checkSumAdjusted = false;
+ for (i = 0; i < tableFields.length; i += 1) {
+ if (tableFields[i].name === 'head table') {
+ tableFields[i].value.checkSumAdjustment = 0xB1B0AFBA - checkSum;
+ checkSumAdjusted = true;
+ break;
+ }
+ }
+
+ if (!checkSumAdjusted) {
+ throw new Error('Could not find head table with checkSum to adjust.');
+ }
+
+ return sfntTable;
+}
+
+exports.computeCheckSum = computeCheckSum;
+exports.make = makeSfntTable;
+exports.fontToTable = fontToSfntTable;
+
+},{"../check":2,"../table":11,"./cff":12,"./cmap":13,"./head":16,"./hhea":17,"./hmtx":18,"./maxp":21,"./name":22,"./os2":23,"./post":24}],26:[function(_dereq_,module,exports){
+// Data types used in the OpenType font file.
+// All OpenType fonts use Motorola-style byte ordering (Big Endian)
+
+/* global WeakMap */
+
+'use strict';
+
+var check = _dereq_('./check');
+
+var LIMIT16 = 32768; // The limit at which a 16-bit number switches signs == 2^15
+var LIMIT32 = 2147483648; // The limit at which a 32-bit number switches signs == 2 ^ 31
+
+var decode = {};
+var encode = {};
+var sizeOf = {};
+
+// Return a function that always returns the same value.
+function constant(v) {
+ return function() {
+ return v;
+ };
+}
+
+// OpenType data types //////////////////////////////////////////////////////
+
+// Convert an 8-bit unsigned integer to a list of 1 byte.
+encode.BYTE = function(v) {
+ check.argument(v >= 0 && v <= 255, 'Byte value should be between 0 and 255.');
+ return [v];
+};
+
+sizeOf.BYTE = constant(1);
+
+// Convert a 8-bit signed integer to a list of 1 byte.
+encode.CHAR = function(v) {
+ return [v.charCodeAt(0)];
+};
+
+sizeOf.BYTE = constant(1);
+
+// Convert an ASCII string to a list of bytes.
+encode.CHARARRAY = function(v) {
+ var b = [];
+ for (var i = 0; i < v.length; i += 1) {
+ b.push(v.charCodeAt(i));
+ }
+
+ return b;
+};
+
+sizeOf.CHARARRAY = function(v) {
+ return v.length;
+};
+
+// Convert a 16-bit unsigned integer to a list of 2 bytes.
+encode.USHORT = function(v) {
+ return [(v >> 8) & 0xFF, v & 0xFF];
+};
+
+sizeOf.USHORT = constant(2);
+
+// Convert a 16-bit signed integer to a list of 2 bytes.
+encode.SHORT = function(v) {
+ // Two's complement
+ if (v >= LIMIT16) {
+ v = -(2 * LIMIT16 - v);
+ }
+
+ return [(v >> 8) & 0xFF, v & 0xFF];
+};
+
+sizeOf.SHORT = constant(2);
+
+// Convert a 24-bit unsigned integer to a list of 3 bytes.
+encode.UINT24 = function(v) {
+ return [(v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF];
+};
+
+sizeOf.UINT24 = constant(3);
+
+// Convert a 32-bit unsigned integer to a list of 4 bytes.
+encode.ULONG = function(v) {
+ return [(v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF];
+};
+
+sizeOf.ULONG = constant(4);
+
+// Convert a 32-bit unsigned integer to a list of 4 bytes.
+encode.LONG = function(v) {
+ // Two's complement
+ if (v >= LIMIT32) {
+ v = -(2 * LIMIT32 - v);
+ }
+
+ return [(v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF];
+};
+
+sizeOf.LONG = constant(4);
+
+encode.FIXED = encode.ULONG;
+sizeOf.FIXED = sizeOf.ULONG;
+
+encode.FWORD = encode.SHORT;
+sizeOf.FWORD = sizeOf.SHORT;
+
+encode.UFWORD = encode.USHORT;
+sizeOf.UFWORD = sizeOf.USHORT;
+
+// FIXME Implement LONGDATETIME
+encode.LONGDATETIME = function() {
+ return [0, 0, 0, 0, 0, 0, 0, 0];
+};
+
+sizeOf.LONGDATETIME = constant(8);
+
+// Convert a 4-char tag to a list of 4 bytes.
+encode.TAG = function(v) {
+ check.argument(v.length === 4, 'Tag should be exactly 4 ASCII characters.');
+ return [v.charCodeAt(0),
+ v.charCodeAt(1),
+ v.charCodeAt(2),
+ v.charCodeAt(3)];
+};
+
+sizeOf.TAG = constant(4);
+
+// CFF data types ///////////////////////////////////////////////////////////
+
+encode.Card8 = encode.BYTE;
+sizeOf.Card8 = sizeOf.BYTE;
+
+encode.Card16 = encode.USHORT;
+sizeOf.Card16 = sizeOf.USHORT;
+
+encode.OffSize = encode.BYTE;
+sizeOf.OffSize = sizeOf.BYTE;
+
+encode.SID = encode.USHORT;
+sizeOf.SID = sizeOf.USHORT;
+
+// Convert a numeric operand or charstring number to a variable-size list of bytes.
+encode.NUMBER = function(v) {
+ if (v >= -107 && v <= 107) {
+ return [v + 139];
+ } else if (v >= 108 && v <= 1131) {
+ v = v - 108;
+ return [(v >> 8) + 247, v & 0xFF];
+ } else if (v >= -1131 && v <= -108) {
+ v = -v - 108;
+ return [(v >> 8) + 251, v & 0xFF];
+ } else if (v >= -32768 && v <= 32767) {
+ return encode.NUMBER16(v);
+ } else {
+ return encode.NUMBER32(v);
+ }
+};
+
+sizeOf.NUMBER = function(v) {
+ return encode.NUMBER(v).length;
+};
+
+// Convert a signed number between -32768 and +32767 to a three-byte value.
+// This ensures we always use three bytes, but is not the most compact format.
+encode.NUMBER16 = function(v) {
+ return [28, (v >> 8) & 0xFF, v & 0xFF];
+};
+
+sizeOf.NUMBER16 = constant(2);
+
+// Convert a signed number between -(2^31) and +(2^31-1) to a four-byte value.
+// This is useful if you want to be sure you always use four bytes,
+// at the expense of wasting a few bytes for smaller numbers.
+encode.NUMBER32 = function(v) {
+ return [29, (v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF];
+};
+
+sizeOf.NUMBER32 = constant(4);
+
+encode.REAL = function(v) {
+ var value = v.toString();
+
+ // Some numbers use an epsilon to encode the value. (e.g. JavaScript will store 0.0000001 as 1e-7)
+ // This code converts it back to a number without the epsilon.
+ var m = /\.(\d*?)(?:9{5,20}|0{5,20})\d{0,2}(?:e(.+)|$)/.exec(value);
+ if (m) {
+ var epsilon = parseFloat('1e' + ((m[2] ? +m[2] : 0) + m[1].length));
+ value = (Math.round(v * epsilon) / epsilon).toString();
+ }
+
+ var nibbles = '';
+ var i;
+ var ii;
+ for (i = 0, ii = value.length; i < ii; i += 1) {
+ var c = value[i];
+ if (c === 'e') {
+ nibbles += value[++i] === '-' ? 'c' : 'b';
+ } else if (c === '.') {
+ nibbles += 'a';
+ } else if (c === '-') {
+ nibbles += 'e';
+ } else {
+ nibbles += c;
+ }
+ }
+
+ nibbles += (nibbles.length & 1) ? 'f' : 'ff';
+ var out = [30];
+ for (i = 0, ii = nibbles.length; i < ii; i += 2) {
+ out.push(parseInt(nibbles.substr(i, 2), 16));
+ }
+
+ return out;
+};
+
+sizeOf.REAL = function(v) {
+ return encode.REAL(v).length;
+};
+
+encode.NAME = encode.CHARARRAY;
+sizeOf.NAME = sizeOf.CHARARRAY;
+
+encode.STRING = encode.CHARARRAY;
+sizeOf.STRING = sizeOf.CHARARRAY;
+
+// Convert a ASCII string to a list of UTF16 bytes.
+encode.UTF16 = function(v) {
+ var b = [];
+ for (var i = 0; i < v.length; i += 1) {
+ b.push(0);
+ b.push(v.charCodeAt(i));
+ }
+
+ return b;
+};
+
+sizeOf.UTF16 = function(v) {
+ return v.length * 2;
+};
+
+// Convert a list of values to a CFF INDEX structure.
+// The values should be objects containing name / type / value.
+encode.INDEX = function(l) {
+ var i;
+ //var offset, offsets, offsetEncoder, encodedOffsets, encodedOffset, data,
+ // dataSize, i, v;
+ // Because we have to know which data type to use to encode the offsets,
+ // we have to go through the values twice: once to encode the data and
+ // calculate the offets, then again to encode the offsets using the fitting data type.
+ var offset = 1; // First offset is always 1.
+ var offsets = [offset];
+ var data = [];
+ var dataSize = 0;
+ for (i = 0; i < l.length; i += 1) {
+ var v = encode.OBJECT(l[i]);
+ Array.prototype.push.apply(data, v);
+ dataSize += v.length;
+ offset += v.length;
+ offsets.push(offset);
+ }
+
+ if (data.length === 0) {
+ return [0, 0];
+ }
+
+ var encodedOffsets = [];
+ var offSize = (1 + Math.floor(Math.log(dataSize) / Math.log(2)) / 8) | 0;
+ var offsetEncoder = [undefined, encode.BYTE, encode.USHORT, encode.UINT24, encode.ULONG][offSize];
+ for (i = 0; i < offsets.length; i += 1) {
+ var encodedOffset = offsetEncoder(offsets[i]);
+ Array.prototype.push.apply(encodedOffsets, encodedOffset);
+ }
+
+ return Array.prototype.concat(encode.Card16(l.length),
+ encode.OffSize(offSize),
+ encodedOffsets,
+ data);
+};
+
+sizeOf.INDEX = function(v) {
+ return encode.INDEX(v).length;
+};
+
+// Convert an object to a CFF DICT structure.
+// The keys should be numeric.
+// The values should be objects containing name / type / value.
+encode.DICT = function(m) {
+ var d = [];
+ var keys = Object.keys(m);
+ var length = keys.length;
+
+ for (var i = 0; i < length; i += 1) {
+ // Object.keys() return string keys, but our keys are always numeric.
+ var k = parseInt(keys[i], 0);
+ var v = m[k];
+ // Value comes before the key.
+ d = d.concat(encode.OPERAND(v.value, v.type));
+ d = d.concat(encode.OPERATOR(k));
+ }
+
+ return d;
+};
+
+sizeOf.DICT = function(m) {
+ return encode.DICT(m).length;
+};
+
+encode.OPERATOR = function(v) {
+ if (v < 1200) {
+ return [v];
+ } else {
+ return [12, v - 1200];
+ }
+};
+
+encode.OPERAND = function(v, type) {
+ var d = [];
+ if (Array.isArray(type)) {
+ for (var i = 0; i < type.length; i += 1) {
+ check.argument(v.length === type.length, 'Not enough arguments given for type' + type);
+ d = d.concat(encode.OPERAND(v[i], type[i]));
+ }
+ } else {
+ if (type === 'SID') {
+ d = d.concat(encode.NUMBER(v));
+ } else if (type === 'offset') {
+ // We make it easy for ourselves and always encode offsets as
+ // 4 bytes. This makes offset calculation for the top dict easier.
+ d = d.concat(encode.NUMBER32(v));
+ } else if (type === 'number') {
+ d = d.concat(encode.NUMBER(v));
+ } else if (type === 'real') {
+ d = d.concat(encode.REAL(v));
+ } else {
+ throw new Error('Unknown operand type ' + type);
+ // FIXME Add support for booleans
+ }
+ }
+
+ return d;
+};
+
+encode.OP = encode.BYTE;
+sizeOf.OP = sizeOf.BYTE;
+
+// memoize charstring encoding using WeakMap if available
+var wmm = typeof WeakMap === 'function' && new WeakMap();
+// Convert a list of CharString operations to bytes.
+encode.CHARSTRING = function(ops) {
+ if (wmm && wmm.has(ops)) {
+ return wmm.get(ops);
+ }
+
+ var d = [];
+ var length = ops.length;
+
+ for (var i = 0; i < length; i += 1) {
+ var op = ops[i];
+ d = d.concat(encode[op.type](op.value));
+ }
+
+ if (wmm) {
+ wmm.set(ops, d);
+ }
+
+ return d;
+};
+
+sizeOf.CHARSTRING = function(ops) {
+ return encode.CHARSTRING(ops).length;
+};
+
+// Utility functions ////////////////////////////////////////////////////////
+
+// Convert an object containing name / type / value to bytes.
+encode.OBJECT = function(v) {
+ var encodingFunction = encode[v.type];
+ check.argument(encodingFunction !== undefined, 'No encoding function for type ' + v.type);
+ return encodingFunction(v.value);
+};
+
+// Convert a table object to bytes.
+// A table contains a list of fields containing the metadata (name, type and default value).
+// The table itself has the field values set as attributes.
+encode.TABLE = function(table) {
+ var d = [];
+ var length = table.fields.length;
+
+ for (var i = 0; i < length; i += 1) {
+ var field = table.fields[i];
+ var encodingFunction = encode[field.type];
+ check.argument(encodingFunction !== undefined, 'No encoding function for field type ' + field.type);
+ var value = table[field.name];
+ if (value === undefined) {
+ value = field.value;
+ }
+
+ var bytes = encodingFunction(value);
+ d = d.concat(bytes);
+ }
+
+ return d;
+};
+
+// Merge in a list of bytes.
+encode.LITERAL = function(v) {
+ return v;
+};
+
+sizeOf.LITERAL = function(v) {
+ return v.length;
+};
+
+exports.decode = decode;
+exports.encode = encode;
+exports.sizeOf = sizeOf;
+
+},{"./check":2}],27:[function(_dereq_,module,exports){
+/*!
+ * Reqwest! A general purpose XHR connection manager
+ * license MIT (c) Dustin Diaz 2014
+ * https://github.com/ded/reqwest
+ */
+
+!function (name, context, definition) {
+ if (typeof module != 'undefined' && module.exports) module.exports = definition()
+ else if (typeof define == 'function' && define.amd) define(definition)
+ else context[name] = definition()
+}('reqwest', this, function () {
+
+ var win = window
+ , doc = document
+ , httpsRe = /^http/
+ , protocolRe = /(^\w+):\/\//
+ , twoHundo = /^(20\d|1223)$/ //http://stackoverflow.com/questions/10046972/msie-returns-status-code-of-1223-for-ajax-request
+ , byTag = 'getElementsByTagName'
+ , readyState = 'readyState'
+ , contentType = 'Content-Type'
+ , requestedWith = 'X-Requested-With'
+ , head = doc[byTag]('head')[0]
+ , uniqid = 0
+ , callbackPrefix = 'reqwest_' + (+new Date())
+ , lastValue // data stored by the most recent JSONP callback
+ , xmlHttpRequest = 'XMLHttpRequest'
+ , xDomainRequest = 'XDomainRequest'
+ , noop = function () {}
+
+ , isArray = typeof Array.isArray == 'function'
+ ? Array.isArray
+ : function (a) {
+ return a instanceof Array
+ }
+
+ , defaultHeaders = {
+ 'contentType': 'application/x-www-form-urlencoded'
+ , 'requestedWith': xmlHttpRequest
+ , 'accept': {
+ '*': 'text/javascript, text/html, application/xml, text/xml, */*'
+ , 'xml': 'application/xml, text/xml'
+ , 'html': 'text/html'
+ , 'text': 'text/plain'
+ , 'json': 'application/json, text/javascript'
+ , 'js': 'application/javascript, text/javascript'
+ }
+ }
+
+ , xhr = function(o) {
+ // is it x-domain
+ if (o['crossOrigin'] === true) {
+ var xhr = win[xmlHttpRequest] ? new XMLHttpRequest() : null
+ if (xhr && 'withCredentials' in xhr) {
+ return xhr
+ } else if (win[xDomainRequest]) {
+ return new XDomainRequest()
+ } else {
+ throw new Error('Browser does not support cross-origin requests')
+ }
+ } else if (win[xmlHttpRequest]) {
+ return new XMLHttpRequest()
+ } else {
+ return new ActiveXObject('Microsoft.XMLHTTP')
+ }
+ }
+ , globalSetupOptions = {
+ dataFilter: function (data) {
+ return data
+ }
+ }
+
+ function succeed(r) {
+ var protocol = protocolRe.exec(r.url);
+ protocol = (protocol && protocol[1]) || window.location.protocol;
+ return httpsRe.test(protocol) ? twoHundo.test(r.request.status) : !!r.request.response;
+ }
+
+ function handleReadyState(r, success, error) {
+ return function () {
+ // use _aborted to mitigate against IE err c00c023f
+ // (can't read props on aborted request objects)
+ if (r._aborted) return error(r.request)
+ if (r._timedOut) return error(r.request, 'Request is aborted: timeout')
+ if (r.request && r.request[readyState] == 4) {
+ r.request.onreadystatechange = noop
+ if (succeed(r)) success(r.request)
+ else
+ error(r.request)
+ }
+ }
+ }
+
+ function setHeaders(http, o) {
+ var headers = o['headers'] || {}
+ , h
+
+ headers['Accept'] = headers['Accept']
+ || defaultHeaders['accept'][o['type']]
+ || defaultHeaders['accept']['*']
+
+ var isAFormData = typeof FormData === 'function' && (o['data'] instanceof FormData);
+ // breaks cross-origin requests with legacy browsers
+ if (!o['crossOrigin'] && !headers[requestedWith]) headers[requestedWith] = defaultHeaders['requestedWith']
+ if (!headers[contentType] && !isAFormData) headers[contentType] = o['contentType'] || defaultHeaders['contentType']
+ for (h in headers)
+ headers.hasOwnProperty(h) && 'setRequestHeader' in http && http.setRequestHeader(h, headers[h])
+ }
+
+ function setCredentials(http, o) {
+ if (typeof o['withCredentials'] !== 'undefined' && typeof http.withCredentials !== 'undefined') {
+ http.withCredentials = !!o['withCredentials']
+ }
+ }
+
+ function generalCallback(data) {
+ lastValue = data
+ }
+
+ function urlappend (url, s) {
+ return url + (/\?/.test(url) ? '&' : '?') + s
+ }
+
+ function handleJsonp(o, fn, err, url) {
+ var reqId = uniqid++
+ , cbkey = o['jsonpCallback'] || 'callback' // the 'callback' key
+ , cbval = o['jsonpCallbackName'] || reqwest.getcallbackPrefix(reqId)
+ , cbreg = new RegExp('((^|\\?|&)' + cbkey + ')=([^&]+)')
+ , match = url.match(cbreg)
+ , script = doc.createElement('script')
+ , loaded = 0
+ , isIE10 = navigator.userAgent.indexOf('MSIE 10.0') !== -1
+
+ if (match) {
+ if (match[3] === '?') {
+ url = url.replace(cbreg, '$1=' + cbval) // wildcard callback func name
+ } else {
+ cbval = match[3] // provided callback func name
+ }
+ } else {
+ url = urlappend(url, cbkey + '=' + cbval) // no callback details, add 'em
+ }
+
+ win[cbval] = generalCallback
+
+ script.type = 'text/javascript'
+ script.src = url
+ script.async = true
+ if (typeof script.onreadystatechange !== 'undefined' && !isIE10) {
+ // need this for IE due to out-of-order onreadystatechange(), binding script
+ // execution to an event listener gives us control over when the script
+ // is executed. See http://jaubourg.net/2010/07/loading-script-as-onclick-handler-of.html
+ script.htmlFor = script.id = '_reqwest_' + reqId
+ }
+
+ script.onload = script.onreadystatechange = function () {
+ if ((script[readyState] && script[readyState] !== 'complete' && script[readyState] !== 'loaded') || loaded) {
+ return false
+ }
+ script.onload = script.onreadystatechange = null
+ script.onclick && script.onclick()
+ // Call the user callback with the last value stored and clean up values and scripts.
+ fn(lastValue)
+ lastValue = undefined
+ head.removeChild(script)
+ loaded = 1
+ }
+
+ // Add the script to the DOM head
+ head.appendChild(script)
+
+ // Enable JSONP timeout
+ return {
+ abort: function () {
+ script.onload = script.onreadystatechange = null
+ err({}, 'Request is aborted: timeout', {})
+ lastValue = undefined
+ head.removeChild(script)
+ loaded = 1
+ }
+ }
+ }
+
+ function getRequest(fn, err) {
+ var o = this.o
+ , method = (o['method'] || 'GET').toUpperCase()
+ , url = typeof o === 'string' ? o : o['url']
+ // convert non-string objects to query-string form unless o['processData'] is false
+ , data = (o['processData'] !== false && o['data'] && typeof o['data'] !== 'string')
+ ? reqwest.toQueryString(o['data'])
+ : (o['data'] || null)
+ , http
+ , sendWait = false
+
+ // if we're working on a GET request and we have data then we should append
+ // query string to end of URL and not post data
+ if ((o['type'] == 'jsonp' || method == 'GET') && data) {
+ url = urlappend(url, data)
+ data = null
+ }
+
+ if (o['type'] == 'jsonp') return handleJsonp(o, fn, err, url)
+
+ // get the xhr from the factory if passed
+ // if the factory returns null, fall-back to ours
+ http = (o.xhr && o.xhr(o)) || xhr(o)
+
+ http.open(method, url, o['async'] === false ? false : true)
+ setHeaders(http, o)
+ setCredentials(http, o)
+ if (win[xDomainRequest] && http instanceof win[xDomainRequest]) {
+ http.onload = fn
+ http.onerror = err
+ // NOTE: see
+ // http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/30ef3add-767c-4436-b8a9-f1ca19b4812e
+ http.onprogress = function() {}
+ sendWait = true
+ } else {
+ http.onreadystatechange = handleReadyState(this, fn, err)
+ }
+ o['before'] && o['before'](http)
+ if (sendWait) {
+ setTimeout(function () {
+ http.send(data)
+ }, 200)
+ } else {
+ http.send(data)
+ }
+ return http
+ }
+
+ function Reqwest(o, fn) {
+ this.o = o
+ this.fn = fn
+
+ init.apply(this, arguments)
+ }
+
+ function setType(header) {
+ // json, javascript, text/plain, text/html, xml
+ if (header.match('json')) return 'json'
+ if (header.match('javascript')) return 'js'
+ if (header.match('text')) return 'html'
+ if (header.match('xml')) return 'xml'
+ }
+
+ function init(o, fn) {
+
+ this.url = typeof o == 'string' ? o : o['url']
+ this.timeout = null
+
+ // whether request has been fulfilled for purpose
+ // of tracking the Promises
+ this._fulfilled = false
+ // success handlers
+ this._successHandler = function(){}
+ this._fulfillmentHandlers = []
+ // error handlers
+ this._errorHandlers = []
+ // complete (both success and fail) handlers
+ this._completeHandlers = []
+ this._erred = false
+ this._responseArgs = {}
+
+ var self = this
+
+ fn = fn || function () {}
+
+ if (o['timeout']) {
+ this.timeout = setTimeout(function () {
+ timedOut()
+ }, o['timeout'])
+ }
+
+ if (o['success']) {
+ this._successHandler = function () {
+ o['success'].apply(o, arguments)
+ }
+ }
+
+ if (o['error']) {
+ this._errorHandlers.push(function () {
+ o['error'].apply(o, arguments)
+ })
+ }
+
+ if (o['complete']) {
+ this._completeHandlers.push(function () {
+ o['complete'].apply(o, arguments)
+ })
+ }
+
+ function complete (resp) {
+ o['timeout'] && clearTimeout(self.timeout)
+ self.timeout = null
+ while (self._completeHandlers.length > 0) {
+ self._completeHandlers.shift()(resp)
+ }
+ }
+
+ function success (resp) {
+ var type = o['type'] || resp && setType(resp.getResponseHeader('Content-Type')) // resp can be undefined in IE
+ resp = (type !== 'jsonp') ? self.request : resp
+ // use global data filter on response text
+ var filteredResponse = globalSetupOptions.dataFilter(resp.responseText, type)
+ , r = filteredResponse
+ try {
+ resp.responseText = r
+ } catch (e) {
+ // can't assign this in IE<=8, just ignore
+ }
+ if (r) {
+ switch (type) {
+ case 'json':
+ try {
+ resp = win.JSON ? win.JSON.parse(r) : eval('(' + r + ')')
+ } catch (err) {
+ return error(resp, 'Could not parse JSON in response', err)
+ }
+ break
+ case 'js':
+ resp = eval(r)
+ break
+ case 'html':
+ resp = r
+ break
+ case 'xml':
+ resp = resp.responseXML
+ && resp.responseXML.parseError // IE trololo
+ && resp.responseXML.parseError.errorCode
+ && resp.responseXML.parseError.reason
+ ? null
+ : resp.responseXML
+ break
+ }
+ }
+
+ self._responseArgs.resp = resp
+ self._fulfilled = true
+ fn(resp)
+ self._successHandler(resp)
+ while (self._fulfillmentHandlers.length > 0) {
+ resp = self._fulfillmentHandlers.shift()(resp)
+ }
+
+ complete(resp)
+ }
+
+ function timedOut() {
+ self._timedOut = true
+ self.request.abort()
+ }
+
+ function error(resp, msg, t) {
+ resp = self.request
+ self._responseArgs.resp = resp
+ self._responseArgs.msg = msg
+ self._responseArgs.t = t
+ self._erred = true
+ while (self._errorHandlers.length > 0) {
+ self._errorHandlers.shift()(resp, msg, t)
+ }
+ complete(resp)
+ }
+
+ this.request = getRequest.call(this, success, error)
+ }
+
+ Reqwest.prototype = {
+ abort: function () {
+ this._aborted = true
+ this.request.abort()
+ }
+
+ , retry: function () {
+ init.call(this, this.o, this.fn)
+ }
+
+ /**
+ * Small deviation from the Promises A CommonJs specification
+ * http://wiki.commonjs.org/wiki/Promises/A
+ */
+
+ /**
+ * `then` will execute upon successful requests
+ */
+ , then: function (success, fail) {
+ success = success || function () {}
+ fail = fail || function () {}
+ if (this._fulfilled) {
+ this._responseArgs.resp = success(this._responseArgs.resp)
+ } else if (this._erred) {
+ fail(this._responseArgs.resp, this._responseArgs.msg, this._responseArgs.t)
+ } else {
+ this._fulfillmentHandlers.push(success)
+ this._errorHandlers.push(fail)
+ }
+ return this
+ }
+
+ /**
+ * `always` will execute whether the request succeeds or fails
+ */
+ , always: function (fn) {
+ if (this._fulfilled || this._erred) {
+ fn(this._responseArgs.resp)
+ } else {
+ this._completeHandlers.push(fn)
+ }
+ return this
+ }
+
+ /**
+ * `fail` will execute when the request fails
+ */
+ , fail: function (fn) {
+ if (this._erred) {
+ fn(this._responseArgs.resp, this._responseArgs.msg, this._responseArgs.t)
+ } else {
+ this._errorHandlers.push(fn)
+ }
+ return this
+ }
+ , 'catch': function (fn) {
+ return this.fail(fn)
+ }
+ }
+
+ function reqwest(o, fn) {
+ return new Reqwest(o, fn)
+ }
+
+ // normalize newline variants according to spec -> CRLF
+ function normalize(s) {
+ return s ? s.replace(/\r?\n/g, '\r\n') : ''
+ }
+
+ function serial(el, cb) {
+ var n = el.name
+ , t = el.tagName.toLowerCase()
+ , optCb = function (o) {
+ // IE gives value="" even where there is no value attribute
+ // 'specified' ref: http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-862529273
+ if (o && !o['disabled'])
+ cb(n, normalize(o['attributes']['value'] && o['attributes']['value']['specified'] ? o['value'] : o['text']))
+ }
+ , ch, ra, val, i
+
+ // don't serialize elements that are disabled or without a name
+ if (el.disabled || !n) return
+
+ switch (t) {
+ case 'input':
+ if (!/reset|button|image|file/i.test(el.type)) {
+ ch = /checkbox/i.test(el.type)
+ ra = /radio/i.test(el.type)
+ val = el.value
+ // WebKit gives us "" instead of "on" if a checkbox has no value, so correct it here
+ ;(!(ch || ra) || el.checked) && cb(n, normalize(ch && val === '' ? 'on' : val))
+ }
+ break
+ case 'textarea':
+ cb(n, normalize(el.value))
+ break
+ case 'select':
+ if (el.type.toLowerCase() === 'select-one') {
+ optCb(el.selectedIndex >= 0 ? el.options[el.selectedIndex] : null)
+ } else {
+ for (i = 0; el.length && i < el.length; i++) {
+ el.options[i].selected && optCb(el.options[i])
+ }
+ }
+ break
+ }
+ }
+
+ // collect up all form elements found from the passed argument elements all
+ // the way down to child elements; pass a '<form>' or form fields.
+ // called with 'this'=callback to use for serial() on each element
+ function eachFormElement() {
+ var cb = this
+ , e, i
+ , serializeSubtags = function (e, tags) {
+ var i, j, fa
+ for (i = 0; i < tags.length; i++) {
+ fa = e[byTag](tags[i])
+ for (j = 0; j < fa.length; j++) serial(fa[j], cb)
+ }
+ }
+
+ for (i = 0; i < arguments.length; i++) {
+ e = arguments[i]
+ if (/input|select|textarea/i.test(e.tagName)) serial(e, cb)
+ serializeSubtags(e, [ 'input', 'select', 'textarea' ])
+ }
+ }
+
+ // standard query string style serialization
+ function serializeQueryString() {
+ return reqwest.toQueryString(reqwest.serializeArray.apply(null, arguments))
+ }
+
+ // { 'name': 'value', ... } style serialization
+ function serializeHash() {
+ var hash = {}
+ eachFormElement.apply(function (name, value) {
+ if (name in hash) {
+ hash[name] && !isArray(hash[name]) && (hash[name] = [hash[name]])
+ hash[name].push(value)
+ } else hash[name] = value
+ }, arguments)
+ return hash
+ }
+
+ // [ { name: 'name', value: 'value' }, ... ] style serialization
+ reqwest.serializeArray = function () {
+ var arr = []
+ eachFormElement.apply(function (name, value) {
+ arr.push({name: name, value: value})
+ }, arguments)
+ return arr
+ }
+
+ reqwest.serialize = function () {
+ if (arguments.length === 0) return ''
+ var opt, fn
+ , args = Array.prototype.slice.call(arguments, 0)
+
+ opt = args.pop()
+ opt && opt.nodeType && args.push(opt) && (opt = null)
+ opt && (opt = opt.type)
+
+ if (opt == 'map') fn = serializeHash
+ else if (opt == 'array') fn = reqwest.serializeArray
+ else fn = serializeQueryString
+
+ return fn.apply(null, args)
+ }
+
+ reqwest.toQueryString = function (o, trad) {
+ var prefix, i
+ , traditional = trad || false
+ , s = []
+ , enc = encodeURIComponent
+ , add = function (key, value) {
+ // If value is a function, invoke it and return its value
+ value = ('function' === typeof value) ? value() : (value == null ? '' : value)
+ s[s.length] = enc(key) + '=' + enc(value)
+ }
+ // If an array was passed in, assume that it is an array of form elements.
+ if (isArray(o)) {
+ for (i = 0; o && i < o.length; i++) add(o[i]['name'], o[i]['value'])
+ } else {
+ // If traditional, encode the "old" way (the way 1.3.2 or older
+ // did it), otherwise encode params recursively.
+ for (prefix in o) {
+ if (o.hasOwnProperty(prefix)) buildParams(prefix, o[prefix], traditional, add)
+ }
+ }
+
+ // spaces should be + according to spec
+ return s.join('&').replace(/%20/g, '+')
+ }
+
+ function buildParams(prefix, obj, traditional, add) {
+ var name, i, v
+ , rbracket = /\[\]$/
+
+ if (isArray(obj)) {
+ // Serialize array item.
+ for (i = 0; obj && i < obj.length; i++) {
+ v = obj[i]
+ if (traditional || rbracket.test(prefix)) {
+ // Treat each array item as a scalar.
+ add(prefix, v)
+ } else {
+ buildParams(prefix + '[' + (typeof v === 'object' ? i : '') + ']', v, traditional, add)
+ }
+ }
+ } else if (obj && obj.toString() === '[object Object]') {
+ // Serialize object item.
+ for (name in obj) {
+ buildParams(prefix + '[' + name + ']', obj[name], traditional, add)
+ }
+
+ } else {
+ // Serialize scalar item.
+ add(prefix, obj)
+ }
+ }
+
+ reqwest.getcallbackPrefix = function () {
+ return callbackPrefix
+ }
+
+ // jQuery and Zepto compatibility, differences can be remapped here so you can call
+ // .ajax.compat(options, callback)
+ reqwest.compat = function (o, fn) {
+ if (o) {
+ o['type'] && (o['method'] = o['type']) && delete o['type']
+ o['dataType'] && (o['type'] = o['dataType'])
+ o['jsonpCallback'] && (o['jsonpCallbackName'] = o['jsonpCallback']) && delete o['jsonpCallback']
+ o['jsonp'] && (o['jsonpCallback'] = o['jsonp'])
+ }
+ return new Reqwest(o, fn)
+ }
+
+ reqwest.ajaxSetup = function (options) {
+ options = options || {}
+ for (var k in options) {
+ globalSetupOptions[k] = options[k]
+ }
+ }
+
+ return reqwest
+});
+
+},{}],28:[function(_dereq_,module,exports){
+/**
+ * @module Shape
+ * @submodule 3D Primitives
+ * @for p5
+ * @requires core
+ * @requires p5.Geometry3D
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+_dereq_('./p5.Geometry3D');
+
+/**
+ * Draw a plane with given a width and height
+ * @method plane
+ * @param {Number} width width of the plane
+ * @param {Number} height height of the plane
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * //draw a plane with width 200 and height 200
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ * background(200);
+ * plane(200, 200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.plane = function(width, height){
+
+ width = width || 50;
+ height = height || 50;
+
+ //details for plane are highly optional
+ var detailX = typeof arguments[2] === Number ? arguments[2] : 1;
+ var detailY = typeof arguments[3] === Number ? arguments[3] : 1;
+
+ var gId = 'plane|'+width+'|'+height+'|'+detailX+'|'+detailY;
+
+ if(!this._renderer.geometryInHash(gId)){
+
+ var geometry3d = new p5.Geometry3D();
+
+ var createPlane = function(u, v){
+ var x = 2 * width * u - width;
+ var y = 2 * height * v - height;
+ var z = 0;
+ return new p5.Vector(x, y, z);
+ };
+
+ geometry3d.parametricGeometry(createPlane, detailX, detailY);
+
+ var obj = geometry3d.generateObj();
+
+ this._renderer.initBuffer(gId, [obj]);
+
+ }
+
+ this._renderer.drawBuffer(gId);
+
+};
+
+/**
+ * Draw a sphere with given raduis
+ * @method sphere
+ * @param {Number} radius radius of circle
+ * @param {Number} [detail] number of segments,
+ * the more segments the smoother geometry
+ * default is 24. Avoid detail number above
+ * 150, it may crash the browser.
+ * @example
+ * <div>
+ * <code>
+ * // draw a sphere with radius 200
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ * background(200);
+ * sphere(200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.sphere = function(radius, detail){
+
+ radius = radius || 50;
+
+ var detailX = detail || 24;
+ var detailY = detail || 16;
+
+ var gId = 'sphere|'+radius+'|'+detailX+'|'+detailY;
+
+ if(!this._renderer.geometryInHash(gId)){
+
+ var geometry3d = new p5.Geometry3D();
+
+ var createSphere = function(u, v){
+ var theta = 2 * Math.PI * u;
+ var phi = Math.PI * v - Math.PI / 2;
+ var x = radius * Math.cos(phi) * Math.sin(theta);
+ var y = radius * Math.sin(phi);
+ var z = radius * Math.cos(phi) * Math.cos(theta);
+ return new p5.Vector(x, y, z);
+ };
+
+ geometry3d.parametricGeometry(createSphere, detailX, detailY);
+
+ var obj = geometry3d.generateObj(true, true);
+
+ this._renderer.initBuffer(gId, [obj]);
+ }
+
+ this._renderer.drawBuffer(gId);
+
+ return this;
+};
+
+/**
+ * Draw an ellipsoid with given radius
+ * @method ellipsoid
+ * @param {Number} radiusx xradius of circle
+ * @param {Number} radiusy yradius of circle
+ * @param {Number} radiusz zradius of circle
+ * @param {Number} [detail] Number of segments.
+ * The more segments, the smoother the
+ * geometry (default is 24). Avoid detail
+ * number above 150. It may crash the
+ * browser.
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * // draw an ellipsoid with radius 200, 300 and 400
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ * background(200);
+ * ellipsoid(200,300,400);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.ellipsoid = function(radiusx, radiusy, radiusz, detail){
+
+ radiusx = radiusx || 50;
+ radiusy = radiusy || 50;
+ radiusz = radiusz || 50;
+
+ var detailX = detail || 24;
+ var detailY = detail || 24;
+
+ var gId = 'ellipsoid|'+radiusx+'|'+radiusy+
+ '|'+radiusz+'|'+detailX+'|'+detailY;
+
+
+ if(!this._renderer.geometryInHash(gId)){
+
+ var geometry3d = new p5.Geometry3D();
+
+ var createEllipsoid = function(u, v){
+ var theta = 2 * Math.PI * u;
+ var phi = Math.PI * v - Math.PI / 2;
+ var x = radiusx * Math.cos(phi) * Math.sin(theta);
+ var y = radiusy * Math.sin(phi);
+ var z = radiusz * Math.cos(phi) * Math.cos(theta);
+ return new p5.Vector(x, y, z);
+ };
+
+ geometry3d.parametricGeometry(createEllipsoid, detailX, detailY);
+
+ var obj = geometry3d.generateObj(true, true);
+
+ this._renderer.initBuffer(gId, [obj]);
+ }
+
+ this._renderer.drawBuffer(gId);
+
+ return this;
+};
+
+/**
+ * Draw a cylinder with given radius and height
+ * @method cylinder
+ * @param {Number} radius radius of the surface
+ * @param {Number} height height of the cylinder
+ * @param {Number} [detail] number of segments,
+ * the more segments the smoother geometry
+ * default is 24. Avoid detail number above
+ * 150. It may crash the browser.
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * //draw a spinning cylinder with radius 200 and height 200
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ * background(200);
+ * rotateX(frameCount * 0.01);
+ * rotateZ(frameCount * 0.01);
+ * cylinder(200, 200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.cylinder = function(radius, height, detail){
+
+ radius = radius || 50;
+ height = height || 50;
+
+ var detailX = detail || 24;
+ var detailY = detail || 16;
+
+ var gId = 'cylinder|'+radius+'|'+height+'|'+detailX+'|'+detailY;
+
+ if(!this._renderer.geometryInHash(gId)){
+
+ var geometry3d = new p5.Geometry3D();
+
+ var createCylinder = function(u, v){
+ var theta = 2 * Math.PI * u;
+ var x = radius * Math.sin(theta);
+ var y = 2 * height * v - height;
+ var z = radius * Math.cos(theta);
+ return new p5.Vector(x, y, z);
+ };
+
+ geometry3d.parametricGeometry(createCylinder, detailX, detailY);
+ var obj = geometry3d.generateObj(true);
+
+ var createTop = function(u, v){
+ var theta = 2 * Math.PI * u;
+ var x = radius * Math.sin(-theta);
+ var y = height;
+ var z = radius * Math.cos(theta);
+ if(v === 0){
+ return new p5.Vector(0, height, 0);
+ }
+ else{
+ return new p5.Vector(x, y, z);
+ }
+ };
+
+ var geometry3d1 = new p5.Geometry3D();
+ geometry3d1.parametricGeometry(
+ createTop, detailX, 1);
+ var obj1 = geometry3d1.generateObj();
+
+ var createBottom = function(u, v){
+ var theta = 2 * Math.PI * u;
+ var x = radius * Math.sin(theta);
+ var y = -height;
+ var z = radius * Math.cos(theta);
+ if(v === 0){
+ return new p5.Vector(0, -height, 0);
+ }else{
+ return new p5.Vector(x, y, z);
+ }
+ };
+
+ var geometry3d2 = new p5.Geometry3D();
+ geometry3d2.parametricGeometry(
+ createBottom, detailX, 1);
+ var obj2 = geometry3d2.generateObj();
+
+
+ this._renderer.initBuffer(gId, [obj, obj1, obj2]);
+ }
+
+ this._renderer.drawBuffer(gId);
+
+ return this;
+};
+
+
+/**
+ * Draw a cone with given radius and height
+ * @method cone
+ * @param {Number} radius radius of the bottom surface
+ * @param {Number} height height of the cone
+ * @param {Number} [detail] number of segments,
+ * the more segments the smoother geometry
+ * default is 24. Avoid detail number above
+ * 150. It may crash the browser.
+ * @example
+ * <div>
+ * <code>
+ * //draw a spinning cone with radius 200 and height 200
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ * background(200);
+ * rotateX(frameCount * 0.01);
+ * rotateZ(frameCount * 0.01);
+ * cone(200, 200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.cone = function(radius, height, detail){
+
+ radius = radius || 50;
+ height = height || 50;
+
+ var detailX = detail || 24;
+ var detailY = detail || 16;
+
+ var gId = 'cone|'+radius+'|'+height+'|'+detailX+'|'+detailY;
+
+ if(!this._renderer.geometryInHash(gId)){
+
+ var geometry3d = new p5.Geometry3D();
+
+ var createCone = function(u, v){
+ var theta = 2 * Math.PI * u;
+ var x = radius * (1 - v) * Math.sin(theta);
+ var y = 2 * height * v - height;
+ var z = radius * (1 - v) * Math.cos(theta);
+ return new p5.Vector(x, y, z);
+ };
+
+ geometry3d.parametricGeometry(createCone, detailX, detailY);
+ var obj = geometry3d.generateObj(true);
+
+ var geometry3d1 = new p5.Geometry3D();
+ var createBottom = function(u, v){
+ var theta = 2 * Math.PI * u;
+ var x = radius * (1 - v) * Math.sin(-theta);
+ var y = -height;
+ var z = radius * (1 - v) * Math.cos(theta);
+ return new p5.Vector(x, y, z);
+ };
+
+ geometry3d1.parametricGeometry(
+ createBottom, detailX, 1);
+ var obj1 = geometry3d1.generateObj();
+
+ this._renderer.initBuffer(gId, [obj, obj1]);
+ }
+
+ this._renderer.drawBuffer(gId);
+
+ return this;
+};
+
+
+/**
+ * Draw a torus with given radius and tube radius
+ * @method torus
+ * @param {Number} radius radius of the whole ring
+ * @param {Number} tubeRadius radius of the tube
+ * @param {Number} [detail] number of segments,
+ * the more segments the smoother geometry
+ * default is 24. Avoid detail number above
+ * 150. It may crash the browser.
+ * @example
+ * <div>
+ * <code>
+ * //draw a spinning torus with radius 200 and tube radius 60
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ * background(200);
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * torus(200, 60);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.torus = function(radius, tubeRadius, detail){
+
+ radius = radius || 50;
+ tubeRadius = tubeRadius || 10;
+
+ var detailX = detail || 24;
+ var detailY = detail || 16;
+
+ var gId = 'torus|'+radius+'|'+tubeRadius+'|'+detailX+'|'+detailY;
+
+ if(!this._renderer.geometryInHash(gId)){
+
+ var geometry3d = new p5.Geometry3D();
+
+ var createTorus = function(u, v){
+ var theta = 2 * Math.PI * u;
+ var phi = 2 * Math.PI * v;
+ var x = (radius + tubeRadius * Math.cos(phi)) * Math.cos(theta);
+ var y = (radius + tubeRadius * Math.cos(phi)) * Math.sin(theta);
+ var z = tubeRadius * Math.sin(phi);
+ return new p5.Vector(x, y, z);
+ };
+
+ geometry3d.parametricGeometry(createTorus, detailX, detailY);
+
+ var obj = geometry3d.generateObj(true);
+
+ this._renderer.initBuffer(gId, [obj]);
+ }
+
+ this._renderer.drawBuffer(gId);
+
+ return this;
+};
+
+/**
+ * Draw a box with given width, height and depth
+ * @method box
+ * @param {Number} width width of the box
+ * @param {Number} height height of the box
+ * @param {Number} depth depth of the box
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * //draw a spinning box with width, height and depth 200
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ * background(200);
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * box(200, 200, 200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.box = function(width, height, depth){
+
+ width = width || 50;
+ height = height || width;
+ depth = depth || width;
+
+ //details for box are highly optional
+ var detailX = typeof arguments[3] === Number ? arguments[3] : 1;
+ var detailY = typeof arguments[4] === Number ? arguments[4] : 1;
+
+ var gId = 'cube|'+width+'|'+height+'|'+depth+'|'+detailX+'|'+detailY;
+
+ if(!this._renderer.geometryInHash(gId)){
+
+ var geometry3d = new p5.Geometry3D();
+
+ var createPlane1 = function(u, v){
+ var x = 2 * width * u - width;
+ var y = 2 * height * v - height;
+ var z = depth;
+ return new p5.Vector(x, y, z);
+ };
+ var createPlane2 = function(u, v){
+ var x = 2 * width * ( 1 - u ) - width;
+ var y = 2 * height * v - height;
+ var z = -depth;
+ return new p5.Vector(x, y, z);
+ };
+ var createPlane3 = function(u, v){
+ var x = 2 * width * ( 1 - u ) - width;
+ var y = height;
+ var z = 2 * depth * v - depth;
+ return new p5.Vector(x, y, z);
+ };
+ var createPlane4 = function(u, v){
+ var x = 2 * width * u - width;
+ var y = -height;
+ var z = 2 * depth * v - depth;
+ return new p5.Vector(x, y, z);
+ };
+ var createPlane5 = function(u, v){
+ var x = width;
+ var y = 2 * height * u - height;
+ var z = 2 * depth * v - depth;
+ return new p5.Vector(x, y, z);
+ };
+ var createPlane6 = function(u, v){
+ var x = -width;
+ var y = 2 * height * ( 1 - u ) - height;
+ var z = 2 * depth * v - depth;
+ return new p5.Vector(x, y, z);
+ };
+
+ geometry3d.parametricGeometry(
+ createPlane1, detailX, detailY, geometry3d.vertices.length);
+ geometry3d.parametricGeometry(
+ createPlane2, detailX, detailY, geometry3d.vertices.length);
+ geometry3d.parametricGeometry(
+ createPlane3, detailX, detailY, geometry3d.vertices.length);
+ geometry3d.parametricGeometry(
+ createPlane4, detailX, detailY, geometry3d.vertices.length);
+ geometry3d.parametricGeometry(
+ createPlane5, detailX, detailY, geometry3d.vertices.length);
+ geometry3d.parametricGeometry(
+ createPlane6, detailX, detailY, geometry3d.vertices.length);
+
+ var obj = geometry3d.generateObj();
+
+ this._renderer.initBuffer(gId, [obj]);
+ }
+
+ this._renderer.drawBuffer(gId);
+
+ return this;
+
+};
+
+module.exports = p5;
+},{"../core/core":48,"./p5.Geometry3D":34}],29:[function(_dereq_,module,exports){
+/**
+ * @module Lights, Camera
+ * @submodule Camera
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * Sets camera position
+ * @method camera
+ * @param {Number} x camera postion value on x axis
+ * @param {Number} y camera postion value on y axis
+ * @param {Number} z camera postion value on z axis
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ * function draw(){
+ * //move the camera away from the plane by a sin wave
+ * camera(0, 0, sin(frameCount * 0.01) * 100);
+ * plane(120, 120);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.camera = function(x, y, z){
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ this._validateParameters(
+ 'camera',
+ args,
+ ['Number', 'Number', 'Number']
+ );
+ //what it manipulates is the model view matrix
+ this._renderer.translate(-x, -y, -z);
+};
+
+/**
+ * Sets perspective camera
+ * @method perspective
+ * @param {Number} fovy camera frustum vertical field of view,
+ * from bottom to top of view, in degrees
+ * @param {Number} aspect camera frustum aspect ratio
+ * @param {Number} near frustum near plane length
+ * @param {Number} far frustum far plane length
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * //drag mouse to toggle the world!
+ * //you will see there's a vanish point
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * perspective(60 / 180 * PI, width/height, 0.1, 100);
+ * }
+ * function draw(){
+ * background(200);
+ * orbitControl();
+ * for(var i = -1; i < 2; i++){
+ * for(var j = -2; j < 3; j++){
+ * push();
+ * translate(i*160, 0, j*160);
+ * box(40, 40, 40);
+ * pop();
+ * }
+ * }
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.perspective = function(fovy,aspect,near,far) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ this._validateParameters(
+ 'perspective',
+ args,
+ ['Number', 'Number', 'Number', 'Number']
+ );
+ this._renderer.uPMatrix = p5.Matrix.identity();
+ this._renderer.uPMatrix.perspective(fovy,aspect,near,far);
+ this._renderer._setCamera = true;
+};
+
+/**
+ * Setup ortho camera
+ * @method ortho
+ * @param {Number} left camera frustum left plane
+ * @param {Number} right camera frustum right plane
+ * @param {Number} bottom camera frustum bottom plane
+ * @param {Number} top camera frustum top plane
+ * @param {Number} near camera frustum near plane
+ * @param {Number} far camera frustum far plane
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * //drag mouse to toggle the world!
+ * //there's no vanish point
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * ortho(-width/2, width/2, height/2, -height/2, 0.1, 100);
+ * }
+ * function draw(){
+ * background(200);
+ * orbitControl();
+ * for(var i = -1; i < 2; i++){
+ * for(var j = -2; j < 3; j++){
+ * push();
+ * translate(i*160, 0, j*160);
+ * box(40, 40, 40);
+ * pop();
+ * }
+ * }
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.ortho = function(left,right,bottom,top,near,far) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ this._validateParameters(
+ 'ortho',
+ args,
+ ['Number', 'Number', 'Number', 'Number', 'Number', 'Number']
+ );
+ left /= this.width;
+ right /= this.width;
+ top /= this.height;
+ bottom /= this.height;
+ this._renderer.uPMatrix = p5.Matrix.identity();
+ this._renderer.uPMatrix.ortho(left,right,bottom,top,near,far);
+ this._renderer._setCamera = true;
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],30:[function(_dereq_,module,exports){
+//@TODO: documentation of immediate mode
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+//////////////////////////////////////////////
+// _primitives2D in 3D space
+//////////////////////////////////////////////
+
+p5.Renderer3D.prototype._primitives2D = function(arr){
+ this._setDefaultCamera();
+ var gl = this.GL;
+ var shaderProgram = this._getColorVertexShader();
+
+ //create vertice buffer
+ var vertexPositionBuffer = this.verticeBuffer;
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);
+
+ gl.bufferData(
+ gl.ARRAY_BUFFER, new Float32Array(arr), gl.STATIC_DRAW);
+ gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,
+ 3, gl.FLOAT, false, 0, 0);
+
+ //create vertexcolor buffer
+ var vertexColorBuffer = this.colorBuffer;
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);
+ var color = this._getCurColor();
+ var colors = [];
+ for(var i = 0; i < arr.length / 3; i++){
+ colors = colors.concat(color);
+ }
+
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
+ gl.vertexAttribPointer(shaderProgram.vertexColorAttribute,
+ 4, gl.FLOAT, false, 0, 0);
+
+ //matrix
+ var mId = 'vertexColorVert|vertexColorFrag';
+ this.setMatrixUniforms(mId);
+};
+
+p5.Renderer3D.prototype.point = function(x, y, z){
+ var gl = this.GL;
+ this._primitives2D([x, y, z]);
+ gl.drawArrays(gl.POINTS, 0, 1);
+ return this;
+};
+
+p5.Renderer3D.prototype.line = function(x1, y1, z1, x2, y2, z2){
+ var gl = this.GL;
+ this._primitives2D([x1, y1, z1, x2, y2, z2]);
+ gl.drawArrays(gl.LINES, 0, 2);
+ return this;
+};
+
+p5.Renderer3D.prototype.triangle = function
+(x1, y1, z1, x2, y2, z2, x3, y3, z3){
+ var gl = this.GL;
+ this._primitives2D([x1, y1, z1, x2, y2, z2, x3, y3, z3]);
+ this._strokeCheck();
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
+ return this;
+};
+
+//@TODO: how to define the order of 4 points
+p5.Renderer3D.prototype.quad = function
+(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4){
+ var gl = this.GL;
+ this._primitives2D(
+ [x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4]);
+ this._strokeCheck();
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+ return this;
+};
+
+p5.Renderer3D.prototype.beginShape = function(mode){
+ this.shapeMode = mode;
+ this.verticeStack = [];
+ return this;
+};
+
+p5.Renderer3D.prototype.vertex = function(x, y, z){
+ this.verticeStack.push(x, y, z);
+ return this;
+};
+
+p5.Renderer3D.prototype.endShape = function(){
+ var gl = this.GL;
+ this._primitives2D(this.verticeStack);
+ this.verticeStack = [];
+
+ switch(this.shapeMode){
+ case 'POINTS':
+ gl.drawArrays(gl.POINTS, 0, 1);
+ break;
+ case 'LINES':
+ gl.drawArrays(gl.LINES, 0, 2);
+ break;
+ case 'TRIANGLES':
+ this._strokeCheck();
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
+ break;
+ case 'TRIANGLE_STRIP':
+ this._strokeCheck();
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+ break;
+ default:
+ this._strokeCheck();
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
+ break;
+ }
+ return this;
+};
+
+//@TODO: figure out how to actually do stroke on shapes in 3D
+p5.Renderer3D.prototype._strokeCheck = function(){
+ if(this.drawMode === 'stroke'){
+ throw new Error(
+ 'stroke for shapes in 3D not yet implemented, use fill for now :('
+ );
+ }
+};
+
+//@TODO
+p5.Renderer3D.prototype.strokeWeight = function() {
+ throw new Error('strokeWeight for 3d not yet implemented');
+};
+
+//////////////////////////////////////////////
+// COLOR
+//////////////////////////////////////////////
+
+p5.Renderer3D.prototype.fill = function(r, g, b, a) {
+ var color = this._pInst.color.apply(this, arguments);
+ var colorNormalized = color._array;
+ this.curColor = colorNormalized;
+ this.drawMode = 'fill';
+ return this;
+};
+
+p5.Renderer3D.prototype.stroke = function(r, g, b, a) {
+ var color = this._pInst.color.apply(this, arguments);
+ var colorNormalized = color._array;
+ this.curColor = colorNormalized;
+ this.drawMode = 'stroke';
+ return this;
+};
+
+p5.Renderer3D.prototype._getColorVertexShader = function(){
+ var gl = this.GL;
+ var mId = 'vertexColorVert|vertexColorFrag';
+ var shaderProgram;
+
+ if(!this.materialInHash(mId)){
+ shaderProgram =
+ this.initShaders('vertexColorVert', 'vertexColorFrag', true);
+ this.mHash[mId] = shaderProgram;
+ shaderProgram.vertexColorAttribute =
+ gl.getAttribLocation(shaderProgram, 'aVertexColor');
+ gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);
+ }else{
+ shaderProgram = this.mHash[mId];
+ }
+ return shaderProgram;
+};
+
+module.exports = p5.Renderer3D;
+
+},{"../core/core":48}],31:[function(_dereq_,module,exports){
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+//@TODO: implement full orbit controls including
+//pan, zoom, quaternion rotation, etc.
+p5.prototype.orbitControl = function(){
+ if(this.mouseIsPressed){
+ this.rotateY((this.mouseX - this.width / 2) / (this.width / 2));
+ this.rotateX((this.mouseY - this.height / 2) / (this.width / 2));
+ }
+ return this;
+};
+
+module.exports = p5;
+},{"../core/core":48}],32:[function(_dereq_,module,exports){
+/**
+ * @module Lights, Camera
+ * @submodule Lights
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * Creates an ambient light with a color
+ * @method ambientLight
+ * @param {Number|Array|String|p5.Color} v1 gray value,
+ * red or hue value (depending on the current color mode),
+ * or color Array, or CSS color string
+ * @param {Number} [v2] optional: green or saturation value
+ * @param {Number} [v3] optional: blue or brightness value
+ * @param {Number} [a] optional: opacity
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ * function draw(){
+ * background(0);
+ * ambientLight(150);
+ * ambientMaterial(250);
+ * sphere(200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.ambientLight = function(v1, v2, v3, a){
+ var gl = this._renderer.GL;
+ var shaderProgram = this._renderer._getShader(
+ 'lightVert', 'lightTextureFrag');
+
+ gl.useProgram(shaderProgram);
+ shaderProgram.uAmbientColor = gl.getUniformLocation(
+ shaderProgram,
+ 'uAmbientColor[' + this._renderer.ambientLightCount + ']');
+
+ var color = this._renderer._pInst.color.apply(
+ this._renderer._pInst, arguments);
+ var colors = color._array;
+
+ gl.uniform3f( shaderProgram.uAmbientColor,
+ colors[0], colors[1], colors[2]);
+
+ //in case there's no material color for the geometry
+ shaderProgram.uMaterialColor = gl.getUniformLocation(
+ shaderProgram, 'uMaterialColor' );
+ gl.uniform4f( shaderProgram.uMaterialColor, 1, 1, 1, 1);
+
+ this._renderer.ambientLightCount ++;
+ shaderProgram.uAmbientLightCount =
+ gl.getUniformLocation(shaderProgram, 'uAmbientLightCount');
+ gl.uniform1i(shaderProgram.uAmbientLightCount,
+ this._renderer.ambientLightCount);
+
+ return this;
+};
+
+/**
+ * Creates a directional light with a color and a direction
+ * @method directionalLight
+ * @param {Number|Array|String|p5.Color} v1 gray value,
+ * red or hue value (depending on the current color mode),
+ * or color Array, or CSS color string
+ * @param {Number} [v2] optional: green or saturation value
+ * @param {Number} [v3] optional: blue or brightness value
+ * @param {Number} [a] optional: opacity
+ * @param {Number|p5.Vector} x x axis direction or a p5.Vector
+ * @param {Number} [y] optional: y axis direction
+ * @param {Number} [z] optional: z axis direction
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ * function draw(){
+ * background(0);
+ * //move your mouse to change light direction
+ * var dirX = (mouseX / width - 0.5) *2;
+ * var dirY = (mouseY / height - 0.5) *(-2);
+ * directionalLight(250, 250, 250, dirX, dirY, 0.25);
+ * ambientMaterial(250);
+ * sphere(200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.directionalLight = function(v1, v2, v3, a, x, y, z) {
+ // TODO(jgessner): Find an example using this and profile it.
+ // var args = new Array(arguments.length);
+ // for (var i = 0; i < args.length; ++i) {
+ // args[i] = arguments[i];
+ // }
+ // this._validateParameters(
+ // 'directionalLight',
+ // args,
+ // [
+ // //rgbaxyz
+ // ['Number', 'Number', 'Number', 'Number', 'Number', 'Number', 'Number'],
+ // //rgbxyz
+ // ['Number', 'Number', 'Number', 'Number', 'Number', 'Number'],
+ // //caxyz
+ // ['Number', 'Number', 'Number', 'Number', 'Number'],
+ // //cxyz
+ // ['Number', 'Number', 'Number', 'Number'],
+ // ['String', 'Number', 'Number', 'Number'],
+ // ['Array', 'Number', 'Number', 'Number'],
+ // ['Object', 'Number', 'Number', 'Number'],
+ // //rgbavector
+ // ['Number', 'Number', 'Number', 'Number', 'Object'],
+ // //rgbvector
+ // ['Number', 'Number', 'Number', 'Object'],
+ // //cavector
+ // ['Number', 'Number', 'Object'],
+ // //cvector
+ // ['Number', 'Object'],
+ // ['String', 'Object'],
+ // ['Array', 'Object'],
+ // ['Object', 'Object']
+ // ]
+ // );
+
+ var gl = this._renderer.GL;
+ var shaderProgram = this._renderer._getShader(
+ 'lightVert', 'lightTextureFrag');
+
+ gl.useProgram(shaderProgram);
+ shaderProgram.uDirectionalColor = gl.getUniformLocation(
+ shaderProgram,
+ 'uDirectionalColor[' + this._renderer.directionalLightCount + ']');
+
+ //@TODO: check parameters number
+ var color = this._renderer._pInst.color.apply(
+ this._renderer._pInst, [v1, v2, v3]);
+ var colors = color._array;
+
+ gl.uniform3f( shaderProgram.uDirectionalColor,
+ colors[0], colors[1], colors[2]);
+
+ var _x, _y, _z;
+
+ if(typeof arguments[arguments.length-1] === 'number'){
+ _x = arguments[arguments.length-3];
+ _y = arguments[arguments.length-2];
+ _z = arguments[arguments.length-1];
+
+ }else{
+ try{
+ _x = arguments[arguments.length-1].x;
+ _y = arguments[arguments.length-1].y;
+ _z = arguments[arguments.length-1].z;
+ }
+ catch(error){
+ throw error;
+ }
+ }
+
+ shaderProgram.uLightingDirection = gl.getUniformLocation(
+ shaderProgram,
+ 'uLightingDirection[' + this._renderer.directionalLightCount + ']');
+ gl.uniform3f( shaderProgram.uLightingDirection, _x, _y, _z);
+
+ //in case there's no material color for the geometry
+ shaderProgram.uMaterialColor = gl.getUniformLocation(
+ shaderProgram, 'uMaterialColor' );
+ gl.uniform4f( shaderProgram.uMaterialColor, 1, 1, 1, 1);
+
+ this._renderer.directionalLightCount ++;
+ shaderProgram.uDirectionalLightCount =
+ gl.getUniformLocation(shaderProgram, 'uDirectionalLightCount');
+ gl.uniform1i(shaderProgram.uDirectionalLightCount,
+ this._renderer.directionalLightCount);
+
+ return this;
+};
+
+/**
+ * Creates a point light with a color and a light position
+ * @method pointLight
+ * @param {Number|Array|String|p5.Color} v1 gray value,
+ * red or hue value (depending on the current color mode),
+ * or color Array, or CSS color string
+ * @param {Number} [v2] optional: green or saturation value
+ * @param {Number} [v3] optional: blue or brightness value
+ * @param {Number} [a] optional: opacity
+ * @param {Number|p5.Vector} x x axis position or a p5.Vector
+ * @param {Number} [y] optional: y axis position
+ * @param {Number} [z] optional: z axis position
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ * function draw(){
+ * background(0);
+ * //move your mouse to change light position
+ * var locY = (mouseY / height - 0.5) *(-2);
+ * var locX = (mouseX / width - 0.5) *2;
+ * //to set the light position,
+ * //think of the world's coordinate as:
+ * // -1,1 -------- 1,1
+ * // | |
+ * // | |
+ * // | |
+ * // -1,-1---------1,-1
+ * pointLight(250, 250, 250, locX, locY, 0);
+ * ambientMaterial(250);
+ * sphere(200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.pointLight = function(v1, v2, v3, a, x, y, z) {
+ // TODO(jgessner): Find an example using this and profile it.
+ // var args = new Array(arguments.length);
+ // for (var i = 0; i < args.length; ++i) {
+ // args[i] = arguments[i];
+ // }
+ // this._validateParameters(
+ // 'pointLight',
+ // arguments,
+ // [
+ // //rgbaxyz
+ // ['Number', 'Number', 'Number', 'Number', 'Number', 'Number', 'Number'],
+ // //rgbxyz
+ // ['Number', 'Number', 'Number', 'Number', 'Number', 'Number'],
+ // //caxyz
+ // ['Number', 'Number', 'Number', 'Number', 'Number'],
+ // //cxyz
+ // ['Number', 'Number', 'Number', 'Number'],
+ // ['String', 'Number', 'Number', 'Number'],
+ // ['Array', 'Number', 'Number', 'Number'],
+ // ['Object', 'Number', 'Number', 'Number'],
+ // //rgbavector
+ // ['Number', 'Number', 'Number', 'Number', 'Object'],
+ // //rgbvector
+ // ['Number', 'Number', 'Number', 'Object'],
+ // //cavector
+ // ['Number', 'Number', 'Object'],
+ // //cvector
+ // ['Number', 'Object'],
+ // ['String', 'Object'],
+ // ['Array', 'Object'],
+ // ['Object', 'Object']
+ // ]
+ // );
+
+ var gl = this._renderer.GL;
+ var shaderProgram = this._renderer._getShader(
+ 'lightVert', 'lightTextureFrag');
+
+ gl.useProgram(shaderProgram);
+ shaderProgram.uPointLightColor = gl.getUniformLocation(
+ shaderProgram,
+ 'uPointLightColor[' + this._renderer.pointLightCount + ']');
+
+ //@TODO: check parameters number
+ var color = this._renderer._pInst.color.apply(
+ this._renderer._pInst, [v1, v2, v3]);
+ var colors = color._array;
+
+ gl.uniform3f( shaderProgram.uPointLightColor,
+ colors[0], colors[1], colors[2]);
+
+ var _x, _y, _z;
+
+ if(typeof arguments[arguments.length-1] === 'number'){
+ _x = arguments[arguments.length-3];
+ _y = arguments[arguments.length-2];
+ _z = arguments[arguments.length-1];
+
+ }else{
+ try{
+ _x = arguments[arguments.length-1].x;
+ _y = arguments[arguments.length-1].y;
+ _z = arguments[arguments.length-1].z;
+ }
+ catch(error){
+ throw error;
+ }
+ }
+
+ shaderProgram.uPointLightLocation = gl.getUniformLocation(
+ shaderProgram,
+ 'uPointLightLocation[' + this._renderer.pointLightCount + ']');
+ gl.uniform3f( shaderProgram.uPointLightLocation, _x, _y, _z);
+
+ //in case there's no material color for the geometry
+ shaderProgram.uMaterialColor = gl.getUniformLocation(
+ shaderProgram, 'uMaterialColor' );
+ gl.uniform4f( shaderProgram.uMaterialColor, 1, 1, 1, 1);
+
+ this._renderer.pointLightCount ++;
+ shaderProgram.uPointLightCount =
+ gl.getUniformLocation(shaderProgram, 'uPointLightCount');
+ gl.uniform1i(shaderProgram.uPointLightCount,
+ this._renderer.pointLightCount);
+
+ return this;
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],33:[function(_dereq_,module,exports){
+/**
+ * @module Lights, Camera
+ * @submodule Material
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+//require('./p5.Texture');
+
+/**
+ * Normal material for geometry
+ * @method normalMaterial
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ * background(0);
+ * normalMaterial();
+ * sphere(200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.normalMaterial = function(){
+ this._renderer._getShader('normalVert', 'normalFrag');
+ return this;
+};
+
+/**
+ * Texture for geometry
+ * @method texture
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * var img;
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * img = loadImage("assets/laDefense.jpg");
+ * }
+ *
+ * function draw(){
+ * background(0);
+ * rotateZ(frameCount * 0.01);
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * //pass image as texture
+ * texture(img);
+ * box(200, 200, 200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.texture = function(image){
+ var gl = this._renderer.GL;
+ var shaderProgram = this._renderer._getShader('lightVert',
+ 'lightTextureFrag');
+ gl.useProgram(shaderProgram);
+ if (image instanceof p5.Image) {
+ //check if image is already used as texture
+ if(!image.isTexture){
+ //createTexture and set isTexture to true
+ var tex = gl.createTexture();
+ image.createTexture(tex);
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
+ image._setProperty('isTexture', true);
+ }
+ //otherwise we're good to bind texture without creating
+ //a new one on the gl
+ else {
+ //TODO
+ }
+ image.loadPixels();
+ var data = new Uint8Array(image.pixels);
+ gl.texImage2D(gl.TEXTURE_2D, 0,
+ gl.RGBA, image.width, image.height,
+ 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
+ }
+ //if param is a video
+ else if (image instanceof p5.MediaElement){
+ if(!image.loadedmetadata) {return;}
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA,
+ gl.UNSIGNED_BYTE, image.elt);
+ }
+ else {
+ //@TODO handle following cases:
+ //- 2D canvas (p5 inst)
+ }
+ if (_isPowerOf2(image.width) && _isPowerOf2(image.height)) {
+ gl.generateMipmap(gl.TEXTURE_2D);
+ } else {
+ //@TODO this is problematic
+ //image.width = _nextHighestPOT(image.width);
+ //image.height = _nextHighestPOT(image.height);
+ gl.texParameteri(gl.TEXTURE_2D,
+ gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D,
+ gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D,
+ gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D,
+ gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+ }
+ //this is where we'd activate multi textures
+ //eg. gl.activeTexture(gl.TEXTURE0 + (unit || 0));
+ //but for now we just have a single texture.
+ //@TODO need to extend this functionality
+ //gl.activeTexture(gl.TEXTURE0 + 0);
+ //gl.bindTexture(gl.TEXTURE_2D, tex);
+ gl.uniform1i(gl.getUniformLocation(shaderProgram, 'uSampler'), 0);
+ gl.uniform1i(gl.getUniformLocation(shaderProgram, 'isTexture'), true);
+ return this;
+};
+
+/**
+ * Helper functions; Checks whether val is a pot
+ * more info on power of 2 here:
+ * https://www.opengl.org/wiki/NPOT_Texture
+ * @param {Number} value
+ * @return {Boolean}
+ */
+function _isPowerOf2 (value){
+ return (value & (value - 1)) === 0;
+}
+
+/**
+ * returns the next highest power of 2 value
+ * @param {Number} value [description]
+ * @return {Number} [description]
+ */
+// function _nextHighestPOT (value){
+// --value;
+// for (var i = 1; i < 32; i <<= 1) {
+// value = value | value >> i;
+// }
+// return value + 1;
+// }
+
+/**
+ * Basic material for geometry with a given color
+ * @method basicMaterial
+ * @param {Number|Array|String|p5.Color} v1 gray value,
+ * red or hue value (depending on the current color mode),
+ * or color Array, or CSS color string
+ * @param {Number} [v2] optional: green or saturation value
+ * @param {Number} [v3] optional: blue or brightness value
+ * @param {Number} [a] optional: opacity
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ * background(0);
+ * basicMaterial(250, 0, 0);
+ * rotateX(frameCount * 0.01);
+ * rotateY(frameCount * 0.01);
+ * rotateZ(frameCount * 0.01);
+ * box(200, 200, 200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.basicMaterial = function(v1, v2, v3, a){
+ var gl = this._renderer.GL;
+
+ var shaderProgram = this._renderer._getShader('normalVert', 'basicFrag');
+
+ gl.useProgram(shaderProgram);
+ shaderProgram.uMaterialColor = gl.getUniformLocation(
+ shaderProgram, 'uMaterialColor' );
+
+ var color = this._renderer._pInst.color.apply(
+ this._renderer._pInst, arguments);
+ var colors = color._array;
+
+ gl.uniform4f( shaderProgram.uMaterialColor,
+ colors[0], colors[1], colors[2], colors[3]);
+
+ return this;
+
+};
+
+/**
+ * Ambient material for geometry with a given color
+ * @method ambientMaterial
+ * @param {Number|Array|String|p5.Color} v1 gray value,
+ * red or hue value (depending on the current color mode),
+ * or color Array, or CSS color string
+ * @param {Number} [v2] optional: green or saturation value
+ * @param {Number} [v3] optional: blue or brightness value
+ * @param {Number} [a] optional: opacity
+* @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ * function draw(){
+ * background(0);
+ * ambientLight(100);
+ * pointLight(250, 250, 250, 100, 100, 0);
+ * ambientMaterial(250);
+ * sphere(200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.ambientMaterial = function(v1, v2, v3, a) {
+ var gl = this._renderer.GL;
+ var shaderProgram =
+ this._renderer._getShader('lightVert', 'lightTextureFrag');
+
+ gl.useProgram(shaderProgram);
+ shaderProgram.uMaterialColor = gl.getUniformLocation(
+ shaderProgram, 'uMaterialColor' );
+
+ var color = this._renderer._pInst.color.apply(
+ this._renderer._pInst, arguments);
+ var colors = color._array;
+
+ gl.uniform4f(shaderProgram.uMaterialColor,
+ colors[0], colors[1], colors[2], colors[3]);
+
+ shaderProgram.uSpecular = gl.getUniformLocation(
+ shaderProgram, 'uSpecular' );
+ gl.uniform1i(shaderProgram.uSpecular, false);
+
+ gl.uniform1i(gl.getUniformLocation(shaderProgram, 'isTexture'), false);
+
+ return this;
+};
+
+/**
+ * Specular material for geometry with a given color
+ * @method specularMaterial
+ * @param {Number|Array|String|p5.Color} v1 gray value,
+ * red or hue value (depending on the current color mode),
+ * or color Array, or CSS color string
+ * @param {Number} [v2] optional: green or saturation value
+ * @param {Number} [v3] optional: blue or brightness value
+ * @param {Number} [a] optional: opacity
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ * function draw(){
+ * background(0);
+ * ambientLight(100);
+ * pointLight(250, 250, 250, 100, 100, 0);
+ * specularMaterial(250);
+ * sphere(200);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.specularMaterial = function(v1, v2, v3, a) {
+ var gl = this._renderer.GL;
+ var shaderProgram =
+ this._renderer._getShader('lightVert', 'lightTextureFrag');
+ gl.uniform1i(gl.getUniformLocation(shaderProgram, 'isTexture'), false);
+ gl.useProgram(shaderProgram);
+ shaderProgram.uMaterialColor = gl.getUniformLocation(
+ shaderProgram, 'uMaterialColor' );
+
+ var color = this._renderer._pInst.color.apply(
+ this._renderer._pInst, arguments);
+ var colors = color._array;
+
+ gl.uniform4f(shaderProgram.uMaterialColor,
+ colors[0], colors[1], colors[2], colors[3]);
+
+ shaderProgram.uSpecular = gl.getUniformLocation(
+ shaderProgram, 'uSpecular' );
+ gl.uniform1i(shaderProgram.uSpecular, true);
+
+ return this;
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],34:[function(_dereq_,module,exports){
+//some of the functions are adjusted from Three.js(http://threejs.org)
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * p5 Geometry3D class
+ */
+p5.Geometry3D = function(){
+ //an array holding every vertice
+ //each vertex is a p5.Vector
+ this.vertices = [];
+ //an array holding each normals for each vertice
+ //each normal is a p5.Vector
+ this.vertexNormals = [];
+ //an array holding each three indecies of vertices that form a face
+ //[[0, 1, 2], [1, 2, 3], ...]
+ this.faces = [];
+ //an array holding every noraml for each face
+ //each faceNormal is a p5.Vector
+ //[[p5.Vector, p5.Vector, p5.Vector],[p5.Vector, p5.Vector, p5.Vector],...]
+ this.faceNormals = [];
+ //an array of array holding uvs (group according to faces)
+ //[[[0, 0], [1, 0], [1, 0]],...]
+ this.uvs = [];
+};
+
+/**
+ * generate geometriy with parametric method
+ * @param {Function} func callback function for how to generate geometry
+ * @param {Number} detailX number of vertices on horizontal surface
+ * @param {Number} detailY number of vertices on horizontal surface
+ * @param {Number} offset offset of vertices index
+ */
+p5.Geometry3D.prototype.parametricGeometry = function
+//@TODO: put func as the last parameters
+(func, detailX, detailY, offset){
+
+ var i, j, p;
+ var u, v;
+ offset = offset || 0;
+ this.detailX = detailX;
+ this.detailY = detailY;
+
+ var sliceCount = detailX + 1;
+ for (i = 0; i <= detailY; i++){
+ v = i / detailY;
+ for (j = 0; j <= detailX; j++){
+ u = j / detailX;
+ p = func(u, v);
+ this.vertices.push(p);
+ }
+ }
+
+ var a, b, c, d;
+ var uva, uvb, uvc, uvd;
+
+ for (i = 0; i < detailY; i++){
+ for (j = 0; j < detailX; j++){
+ a = i * sliceCount + j + offset;
+ b = i * sliceCount + j + 1 + offset;
+ c = (i + 1)* sliceCount + j + 1 + offset;
+ d = (i + 1)* sliceCount + j + offset;
+
+ uva = [j/detailX, i/detailY];
+ uvb = [(j + 1)/ detailX, i/detailY];
+ uvc = [(j + 1)/ detailX, (i + 1)/detailY];
+ uvd = [j/detailX, (i + 1)/detailY];
+
+ this.faces.push([a, b, d]);
+ this.uvs.push([uva, uvb, uvd]);
+
+ this.faces.push([b, c, d]);
+ this.uvs.push([uvb, uvc, uvd]);
+ }
+ }
+};
+
+/**
+ * compute faceNormals for a geometry
+ */
+p5.Geometry3D.prototype.computeFaceNormals = function(){
+
+ var cb = new p5.Vector();
+ var ab = new p5.Vector();
+
+ for (var f = 0; f < this.faces.length; f++){
+ var face = this.faces[f];
+ var vA = this.vertices[face[0]];
+ var vB = this.vertices[face[1]];
+ var vC = this.vertices[face[2]];
+
+ p5.Vector.sub(vC, vB, cb);
+ p5.Vector.sub(vA, vB, ab);
+
+ var normal = p5.Vector.cross(ab, cb);
+ normal.normalize();
+ normal.mult(-1);
+ this.faceNormals[f] = normal;
+ }
+
+};
+
+/**
+ * compute vertexNormals for a geometry
+ */
+p5.Geometry3D.prototype.computeVertexNormals = function (){
+
+ var v, f, face, faceNormal, vertices;
+ var vertexNormals = [];
+
+ vertices = new Array(this.vertices.length);
+ for (v = 0; v < this.vertices.length; v++) {
+ vertices[v] = new p5.Vector();
+ }
+
+ for (f = 0; f < this.faces.length; f++) {
+ face = this.faces[f];
+ faceNormal = this.faceNormals[f];
+
+ vertices[face[0]].add(faceNormal);
+ vertices[face[1]].add(faceNormal);
+ vertices[face[2]].add(faceNormal);
+ }
+
+ for (v = 0; v < this.vertices.length; v++) {
+ vertices[v].normalize();
+ }
+
+ for (f = 0; f < this.faces.length; f++) {
+ face = this.faces[f];
+ vertexNormals[f] = [];
+ vertexNormals[f][0]= vertices[face[0]].copy();
+ vertexNormals[f][1]= vertices[face[1]].copy();
+ vertexNormals[f][2]= vertices[face[2]].copy();
+ }
+
+ for (f = 0; f < this.faces.length; f++){
+ face = this.faces[f];
+ faceNormal = this.faceNormals[f];
+ this.vertexNormals[face[0]] = vertexNormals[f][0];
+ this.vertexNormals[face[1]] = vertexNormals[f][1];
+ this.vertexNormals[face[2]] = vertexNormals[f][2];
+ }
+
+};
+
+p5.Geometry3D.prototype.averageNormals = function() {
+
+ for(var i = 0; i <= this.detailY; i++){
+ var offset = this.detailX + 1;
+ var temp = p5.Vector
+ .add(this.vertexNormals[i*offset],
+ this.vertexNormals[i*offset + this.detailX]);
+ temp = p5.Vector.div(temp, 2);
+ this.vertexNormals[i*offset] = temp;
+ this.vertexNormals[i*offset + this.detailX] = temp;
+ }
+};
+
+p5.Geometry3D.prototype.averagePoleNormals = function() {
+
+ //average the north pole
+ var sum = new p5.Vector(0, 0, 0);
+ for(var i = 0; i < this.detailX; i++){
+ sum.add(this.vertexNormals[i]);
+ }
+ sum = p5.Vector.div(sum, this.detailX);
+
+ for(i = 0; i < this.detailX; i++){
+ this.vertexNormals[i] = sum;
+ }
+
+ //average the south pole
+ sum = new p5.Vector(0, 0, 0);
+ for(i = this.vertices.length - 1;
+ i > this.vertices.length - 1 - this.detailX; i--){
+ sum.add(this.vertexNormals[i]);
+ }
+ sum = p5.Vector.div(sum, this.detailX);
+
+ for(i = this.vertices.length - 1;
+ i > this.vertices.length - 1 - this.detailX; i--){
+ this.vertexNormals[i] = sum;
+ }
+};
+
+/**
+ * [generateUV description]
+ * @param {Array} faces [description]
+ * @param {Array} uvs [description]
+ */
+p5.Geometry3D.prototype.generateUV = function(faces, uvs){
+
+ faces = flatten(faces);
+ uvs = flatten(uvs);
+ var arr = [];
+ faces.forEach(function(item, index){
+ arr[item] = uvs[index];
+ });
+ return flatten(arr);
+};
+
+
+/**
+ * generate an object containing information needed to create buffer
+ */
+p5.Geometry3D.prototype.generateObj = function(average, sphere){
+
+ this.computeFaceNormals();
+ this.computeVertexNormals();
+
+ if(average){
+ this.averageNormals();
+ }
+
+ if(sphere){
+ this.averagePoleNormals();
+ }
+
+ var obj = {
+ vertices: turnVectorArrayIntoNumberArray(this.vertices),
+ vertexNormals: turnVectorArrayIntoNumberArray(this.vertexNormals),
+ uvs: this.generateUV(this.faces, this.uvs),
+ faces: flatten(this.faces),
+ len: this.faces.length * 3
+ };
+ return obj;
+};
+
+/**
+ * turn a two dimensional array into one dimensional array
+ * @param {Array} arr 2-dimensional array
+ * @return {Array} 1-dimensional array
+ * [[1, 2, 3],[4, 5, 6]] -> [1, 2, 3, 4, 5, 6]
+ */
+function flatten(arr){
+ return arr.reduce(function(a, b){
+ return a.concat(b);
+ });
+}
+
+/**
+ * turn an array of Vector into a one dimensional array of numbers
+ * @param {Array} arr an array of p5.Vector
+ * @return {Array]} a one dimensional array of numbers
+ * [p5.Vector(1, 2, 3), p5.Vector(4, 5, 6)] ->
+ * [1, 2, 3, 4, 5, 6]
+ */
+function turnVectorArrayIntoNumberArray(arr){
+ return flatten(arr.map(function(item){
+ return [item.x, item.y, item.z];
+ }));
+}
+
+module.exports = p5.Geometry3D;
+},{"../core/core":48}],35:[function(_dereq_,module,exports){
+/**
+* @requires constants
+* @todo see methods below needing further implementation.
+* future consideration: implement SIMD optimizations
+* when browser compatibility becomes available
+* https://developer.mozilla.org/en-US/docs/Web/JavaScript/
+* Reference/Global_Objects/SIMD
+*/
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var polarGeometry = _dereq_('../math/polargeometry');
+var constants = _dereq_('../core/constants');
+var GLMAT_ARRAY_TYPE = (
+ typeof Float32Array !== 'undefined') ?
+ Float32Array : Array;
+
+/**
+ * A class to describe a 4x4 matrix
+ * for model and view matrix manipulation in the p5js webgl renderer.
+ * class p5.Matrix
+ * @constructor
+ * @param {Array} [mat4] array literal of our 4x4 matrix
+ */
+p5.Matrix = function() {
+ // This is how it comes in with createMatrix()
+ if(arguments[0] instanceof p5) {
+ // save reference to p5 if passed in
+ this.p5 = arguments[0];
+ this.mat4 = arguments[1] || new GLMAT_ARRAY_TYPE([
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+ ]);
+ // This is what we'll get with new p5.Matrix()
+ // a mat4 identity matrix
+ } else {
+ this.mat4 = arguments[0] || new GLMAT_ARRAY_TYPE([
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+ ]);
+ }
+ return this;
+};
+
+/**
+ * Sets the x, y, and z component of the vector using two or three separate
+ * variables, the data from a p5.Matrix, or the values from a float array.
+ *
+ * @param {p5.Matrix|Array} [inMatrix] the input p5.Matrix or
+ * an Array of length 16
+ */
+p5.Matrix.prototype.set = function (inMatrix) {
+ if (inMatrix instanceof p5.Matrix) {
+ this.mat4 = inMatrix.mat4;
+ return this;
+ }
+ else if (inMatrix instanceof GLMAT_ARRAY_TYPE) {
+ this.mat4 = inMatrix;
+ return this;
+ }
+ return this;
+};
+
+/**
+ * Gets a copy of the vector, returns a p5.Matrix object.
+ *
+ * @return {p5.Matrix} the copy of the p5.Matrix object
+ */
+p5.Matrix.prototype.get = function () {
+ return new p5.Matrix(this.mat4);
+};
+
+/**
+ * return a copy of a matrix
+ * @return {p5.Matrix} the result matrix
+ */
+p5.Matrix.prototype.copy = function(){
+ var copied = new p5.Matrix();
+ copied.mat4[0] = this.mat4[0];
+ copied.mat4[1] = this.mat4[1];
+ copied.mat4[2] = this.mat4[2];
+ copied.mat4[3] = this.mat4[3];
+ copied.mat4[4] = this.mat4[4];
+ copied.mat4[5] = this.mat4[5];
+ copied.mat4[6] = this.mat4[6];
+ copied.mat4[7] = this.mat4[7];
+ copied.mat4[8] = this.mat4[8];
+ copied.mat4[9] = this.mat4[9];
+ copied.mat4[10] = this.mat4[10];
+ copied.mat4[11] = this.mat4[11];
+ copied.mat4[12] = this.mat4[12];
+ copied.mat4[13] = this.mat4[13];
+ copied.mat4[14] = this.mat4[14];
+ copied.mat4[15] = this.mat4[15];
+ return copied;
+};
+
+/**
+ * return an identity matrix
+ * @return {p5.Matrix} the result matrix
+ */
+p5.Matrix.identity = function(){
+ return new p5.Matrix();
+};
+
+/**
+ * transpose according to a given matrix
+ * @param {p5.Matrix | Typed Array} a the matrix to be based on to transpose
+ * @return {p5.Matrix} this
+ */
+p5.Matrix.prototype.transpose = function(a){
+ var a01, a02, a03, a12, a13, a23;
+ if(a instanceof p5.Matrix){
+ a01 = a.mat4[1];
+ a02 = a.mat4[2];
+ a03 = a.mat4[3];
+ a12 = a.mat4[6];
+ a13 = a.mat4[7];
+ a23 = a.mat4[11];
+
+ this.mat4[0] = a.mat4[0];
+ this.mat4[1] = a.mat4[4];
+ this.mat4[2] = a.mat4[8];
+ this.mat4[3] = a.mat4[12];
+ this.mat4[4] = a01;
+ this.mat4[5] = a.mat4[5];
+ this.mat4[6] = a.mat4[9];
+ this.mat4[7] = a.mat4[13];
+ this.mat4[8] = a02;
+ this.mat4[9] = a12;
+ this.mat4[10] = a.mat4[10];
+ this.mat4[11] = a.mat4[14];
+ this.mat4[12] = a03;
+ this.mat4[13] = a13;
+ this.mat4[14] = a23;
+ this.mat4[15] = a.mat4[15];
+
+ }else if(a instanceof GLMAT_ARRAY_TYPE){
+ a01 = a[1];
+ a02 = a[2];
+ a03 = a[3];
+ a12 = a[6];
+ a13 = a[7];
+ a23 = a[11];
+
+ this.mat4[0] = a[0];
+ this.mat4[1] = a[4];
+ this.mat4[2] = a[8];
+ this.mat4[3] = a[12];
+ this.mat4[4] = a01;
+ this.mat4[5] = a[5];
+ this.mat4[6] = a[9];
+ this.mat4[7] = a[13];
+ this.mat4[8] = a02;
+ this.mat4[9] = a12;
+ this.mat4[10] = a[10];
+ this.mat4[11] = a[14];
+ this.mat4[12] = a03;
+ this.mat4[13] = a13;
+ this.mat4[14] = a23;
+ this.mat4[15] = a[15];
+ }
+ return this;
+};
+
+/**
+ * invert matrix according to a give matrix
+ * @param {p5.Matrix or Typed Array} a the matrix to be based on to invert
+ * @return {p5.Matrix} this
+ */
+p5.Matrix.prototype.invert = function(a){
+ var a00, a01, a02, a03, a10, a11, a12, a13,
+ a20, a21, a22, a23, a30, a31, a32, a33;
+ if(a instanceof p5.Matrix){
+ a00 = a.mat4[0];
+ a01 = a.mat4[1];
+ a02 = a.mat4[2];
+ a03 = a.mat4[3];
+ a10 = a.mat4[4];
+ a11 = a.mat4[5];
+ a12 = a.mat4[6];
+ a13 = a.mat4[7];
+ a20 = a.mat4[8];
+ a21 = a.mat4[9];
+ a22 = a.mat4[10];
+ a23 = a.mat4[11];
+ a30 = a.mat4[12];
+ a31 = a.mat4[13];
+ a32 = a.mat4[14];
+ a33 = a.mat4[15];
+ }else if(a instanceof GLMAT_ARRAY_TYPE){
+ a00 = a[0];
+ a01 = a[1];
+ a02 = a[2];
+ a03 = a[3];
+ a10 = a[4];
+ a11 = a[5];
+ a12 = a[6];
+ a13 = a[7];
+ a20 = a[8];
+ a21 = a[9];
+ a22 = a[10];
+ a23 = a[11];
+ a30 = a[12];
+ a31 = a[13];
+ a32 = a[14];
+ a33 = a[15];
+ }
+ var b00 = a00 * a11 - a01 * a10,
+ b01 = a00 * a12 - a02 * a10,
+ b02 = a00 * a13 - a03 * a10,
+ b03 = a01 * a12 - a02 * a11,
+ b04 = a01 * a13 - a03 * a11,
+ b05 = a02 * a13 - a03 * a12,
+ b06 = a20 * a31 - a21 * a30,
+ b07 = a20 * a32 - a22 * a30,
+ b08 = a20 * a33 - a23 * a30,
+ b09 = a21 * a32 - a22 * a31,
+ b10 = a21 * a33 - a23 * a31,
+ b11 = a22 * a33 - a23 * a32,
+
+ // Calculate the determinant
+ det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 -
+ b04 * b07 + b05 * b06;
+
+ if (!det) {
+ return null;
+ }
+ det = 1.0 / det;
+
+ this.mat4[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
+ this.mat4[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
+ this.mat4[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
+ this.mat4[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
+ this.mat4[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
+ this.mat4[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
+ this.mat4[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
+ this.mat4[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
+ this.mat4[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
+ this.mat4[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
+ this.mat4[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
+ this.mat4[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
+ this.mat4[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
+ this.mat4[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
+ this.mat4[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
+ this.mat4[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
+
+ return this;
+};
+
+/**
+ * inspired by Toji's mat4 determinant
+ * @return {Number} Determinant of our 4x4 matrix
+ */
+p5.Matrix.prototype.determinant = function(){
+ var d00 = (this.mat4[0] * this.mat4[5]) - (this.mat4[1] * this.mat4[4]),
+ d01 = (this.mat4[0] * this.mat4[6]) - (this.mat4[2] * this.mat4[4]),
+ d02 = (this.mat4[0] * this.mat4[7]) - (this.mat4[3] * this.mat4[4]),
+ d03 = (this.mat4[1] * this.mat4[6]) - (this.mat4[2] * this.mat4[5]),
+ d04 = (this.mat4[1] * this.mat4[7]) - (this.mat4[3] * this.mat4[5]),
+ d05 = (this.mat4[2] * this.mat4[7]) - (this.mat4[3] * this.mat4[6]),
+ d06 = (this.mat4[8] * this.mat4[13]) - (this.mat4[9] * this.mat4[12]),
+ d07 = (this.mat4[8] * this.mat4[14]) - (this.mat4[10] * this.mat4[12]),
+ d08 = (this.mat4[8] * this.mat4[15]) - (this.mat4[11] * this.mat4[12]),
+ d09 = (this.mat4[9] * this.mat4[14]) - (this.mat4[10] * this.mat4[13]),
+ d10 = (this.mat4[9] * this.mat4[15]) - (this.mat4[11] * this.mat4[13]),
+ d11 = (this.mat4[10] * this.mat4[15]) - (this.mat4[11] * this.mat4[14]);
+
+ // Calculate the determinant
+ return d00 * d11 - d01 * d10 + d02 * d09 +
+ d03 * d08 - d04 * d07 + d05 * d06;
+};
+
+/**
+ * multiply two mat4s
+ * @param {p5.Matrix | Array} multMatrix The matrix we want to multiply by
+ * @return {p5.Matrix} this
+ */
+p5.Matrix.prototype.mult = function(multMatrix){
+ var _dest = new GLMAT_ARRAY_TYPE(16);
+ var _src = new GLMAT_ARRAY_TYPE(16);
+
+ if(multMatrix instanceof p5.Matrix) {
+ _src = multMatrix.mat4;
+ }
+ else if(multMatrix instanceof GLMAT_ARRAY_TYPE){
+ _src = multMatrix;
+ }
+
+ // each row is used for the multiplier
+ var b0 = this.mat4[0], b1 = this.mat4[1],
+ b2 = this.mat4[2], b3 = this.mat4[3];
+ _dest[0] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12];
+ _dest[1] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13];
+ _dest[2] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14];
+ _dest[3] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15];
+
+ b0 = this.mat4[4];
+ b1 = this.mat4[5];
+ b2 = this.mat4[6];
+ b3 = this.mat4[7];
+ _dest[4] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12];
+ _dest[5] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13];
+ _dest[6] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14];
+ _dest[7] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15];
+
+ b0 = this.mat4[8];
+ b1 = this.mat4[9];
+ b2 = this.mat4[10];
+ b3 = this.mat4[11];
+ _dest[8] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12];
+ _dest[9] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13];
+ _dest[10] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14];
+ _dest[11] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15];
+
+ b0 = this.mat4[12];
+ b1 = this.mat4[13];
+ b2 = this.mat4[14];
+ b3 = this.mat4[15];
+ _dest[12] = b0*_src[0] + b1*_src[4] + b2*_src[8] + b3*_src[12];
+ _dest[13] = b0*_src[1] + b1*_src[5] + b2*_src[9] + b3*_src[13];
+ _dest[14] = b0*_src[2] + b1*_src[6] + b2*_src[10] + b3*_src[14];
+ _dest[15] = b0*_src[3] + b1*_src[7] + b2*_src[11] + b3*_src[15];
+
+ this.mat4 = _dest;
+
+ return this;
+};
+
+/**
+ * scales a p5.Matrix by scalars or a vector
+ * @param {p5.Vector | Array }
+ * vector to scale by
+ * @return {p5.Matrix} this
+ */
+p5.Matrix.prototype.scale = function() {
+ var x,y,z;
+ var args = new Array(arguments.length);
+ for(var i = 0; i < args.length; i++) {
+ args[i] = arguments[i];
+ }
+ //if our 1st arg is a type p5.Vector
+ if (args[0] instanceof p5.Vector){
+ x = args[0].x;
+ y = args[0].y;
+ z = args[0].z;
+ }
+ //otherwise if it's an array
+ else if (args[0] instanceof Array){
+ x = args[0][0];
+ y = args[0][1];
+ z = args[0][2];
+ }
+ var _dest = new GLMAT_ARRAY_TYPE(16);
+ _dest[0] = this.mat4[0] * x;
+ _dest[1] = this.mat4[1] * x;
+ _dest[2] = this.mat4[2] * x;
+ _dest[3] = this.mat4[3] * x;
+ _dest[4] = this.mat4[4] * y;
+ _dest[5] = this.mat4[5] * y;
+ _dest[6] = this.mat4[6] * y;
+ _dest[7] = this.mat4[7] * y;
+ _dest[8] = this.mat4[8] * z;
+ _dest[9] = this.mat4[9] * z;
+ _dest[10] = this.mat4[10] * z;
+ _dest[11] = this.mat4[11] * z;
+ _dest[12] = this.mat4[12];
+ _dest[13] = this.mat4[13];
+ _dest[14] = this.mat4[14];
+ _dest[15] = this.mat4[15];
+
+ this.mat4 = _dest;
+ return this;
+};
+
+/**
+ * rotate our Matrix around an axis by the given angle.
+ * @param {Number} a The angle of rotation in radians
+ * @param {p5.Vector | Array} axis the axis(es) to rotate around
+ * @return {p5.Matrix} this
+ * inspired by Toji's gl-matrix lib, mat4 rotation
+ */
+p5.Matrix.prototype.rotate = function(a, axis){
+ var x, y, z, _a, len;
+
+ if (this.p5) {
+ if (this.p5._angleMode === constants.DEGREES) {
+ _a = polarGeometry.degreesToRadians(a);
+ }
+ }
+ else {
+ _a = a;
+ }
+ if (axis instanceof p5.Vector) {
+ x = axis.x;
+ y = axis.y;
+ z = axis.z;
+ }
+ else if (axis instanceof Array) {
+ x = axis[0];
+ y = axis[1];
+ z = axis[2];
+ }
+
+ len = Math.sqrt(x * x + y * y + z * z);
+ x *= (1/len);
+ y *= (1/len);
+ z *= (1/len);
+
+ var a00 = this.mat4[0];
+ var a01 = this.mat4[1];
+ var a02 = this.mat4[2];
+ var a03 = this.mat4[3];
+ var a10 = this.mat4[4];
+ var a11 = this.mat4[5];
+ var a12 = this.mat4[6];
+ var a13 = this.mat4[7];
+ var a20 = this.mat4[8];
+ var a21 = this.mat4[9];
+ var a22 = this.mat4[10];
+ var a23 = this.mat4[11];
+
+ //sin,cos, and tan of respective angle
+ var sA = Math.sin(_a);
+ var cA = Math.cos(_a);
+ var tA = 1 - cA;
+ // Construct the elements of the rotation matrix
+ var b00 = x * x * tA + cA;
+ var b01 = y * x * tA + z * sA;
+ var b02 = z * x * tA - y * sA;
+ var b10 = x * y * tA - z * sA;
+ var b11 = y * y * tA + cA;
+ var b12 = z * y * tA + x * sA;
+ var b20 = x * z * tA + y * sA;
+ var b21 = y * z * tA - x * sA;
+ var b22 = z * z * tA + cA;
+
+ // rotation-specific matrix multiplication
+ this.mat4[0] = a00 * b00 + a10 * b01 + a20 * b02;
+ this.mat4[1] = a01 * b00 + a11 * b01 + a21 * b02;
+ this.mat4[2] = a02 * b00 + a12 * b01 + a22 * b02;
+ this.mat4[3] = a03 * b00 + a13 * b01 + a23 * b02;
+ this.mat4[4] = a00 * b10 + a10 * b11 + a20 * b12;
+ this.mat4[5] = a01 * b10 + a11 * b11 + a21 * b12;
+ this.mat4[6] = a02 * b10 + a12 * b11 + a22 * b12;
+ this.mat4[7] = a03 * b10 + a13 * b11 + a23 * b12;
+ this.mat4[8] = a00 * b20 + a10 * b21 + a20 * b22;
+ this.mat4[9] = a01 * b20 + a11 * b21 + a21 * b22;
+ this.mat4[10] = a02 * b20 + a12 * b21 + a22 * b22;
+ this.mat4[11] = a03 * b20 + a13 * b21 + a23 * b22;
+
+ return this;
+};
+
+/**
+ * @todo finish implementing this method!
+ * translates
+ * @param {Array} v vector to translate by
+ * @return {p5.Matrix} this
+ */
+p5.Matrix.prototype.translate = function(v){
+ var x = v[0],
+ y = v[1],
+ z = v[2] || 0;
+ this.mat4[12] =
+ this.mat4[0] * x +this.mat4[4] * y +this.mat4[8] * z +this.mat4[12];
+ this.mat4[13] =
+ this.mat4[1] * x +this.mat4[5] * y +this.mat4[9] * z +this.mat4[13];
+ this.mat4[14] =
+ this.mat4[2] * x +this.mat4[6] * y +this.mat4[10] * z +this.mat4[14];
+ this.mat4[15] =
+ this.mat4[3] * x +this.mat4[7] * y +this.mat4[11] * z +this.mat4[15];
+};
+
+p5.Matrix.prototype.rotateX = function(a){
+ this.rotate(a, [1,0,0]);
+};
+p5.Matrix.prototype.rotateY = function(a){
+ this.rotate(a, [0,1,0]);
+};
+p5.Matrix.prototype.rotateZ = function(a){
+ this.rotate(a, [0,0,1]);
+};
+
+/**
+ * sets the perspective matrix
+ * @param {Number} fovy [description]
+ * @param {Number} aspect [description]
+ * @param {Number} near near clipping plane
+ * @param {Number} far far clipping plane
+ * @return {void}
+ */
+p5.Matrix.prototype.perspective = function(fovy,aspect,near,far){
+
+ var f = 1.0 / Math.tan(fovy / 2),
+ nf = 1 / (near - far);
+
+ this.mat4[0] = f / aspect;
+ this.mat4[1] = 0;
+ this.mat4[2] = 0;
+ this.mat4[3] = 0;
+ this.mat4[4] = 0;
+ this.mat4[5] = f;
+ this.mat4[6] = 0;
+ this.mat4[7] = 0;
+ this.mat4[8] = 0;
+ this.mat4[9] = 0;
+ this.mat4[10] = (far + near) * nf;
+ this.mat4[11] = -1;
+ this.mat4[12] = 0;
+ this.mat4[13] = 0;
+ this.mat4[14] = (2 * far * near) * nf;
+ this.mat4[15] = 0;
+
+ return this;
+
+};
+
+/**
+ * sets the ortho matrix
+ * @param {Number} left [description]
+ * @param {Number} right [description]
+ * @param {Number} bottom [description]
+ * @param {Number} top [description]
+ * @param {Number} near near clipping plane
+ * @param {Number} far far clipping plane
+ * @return {void}
+ */
+p5.Matrix.prototype.ortho = function(left,right,bottom,top,near,far){
+
+ var lr = 1 / (left - right),
+ bt = 1 / (bottom - top),
+ nf = 1 / (near - far);
+ this.mat4[0] = -2 * lr;
+ this.mat4[1] = 0;
+ this.mat4[2] = 0;
+ this.mat4[3] = 0;
+ this.mat4[4] = 0;
+ this.mat4[5] = -2 * bt;
+ this.mat4[6] = 0;
+ this.mat4[7] = 0;
+ this.mat4[8] = 0;
+ this.mat4[9] = 0;
+ this.mat4[10] = 2 * nf;
+ this.mat4[11] = 0;
+ this.mat4[12] = (left + right) * lr;
+ this.mat4[13] = (top + bottom) * bt;
+ this.mat4[14] = (far + near) * nf;
+ this.mat4[15] = 1;
+
+ return this;
+};
+
+/**
+ * PRIVATE
+ */
+// matrix methods adapted from:
+// https://developer.mozilla.org/en-US/docs/Web/WebGL/
+// gluPerspective
+//
+// function _makePerspective(fovy, aspect, znear, zfar){
+// var ymax = znear * Math.tan(fovy * Math.PI / 360.0);
+// var ymin = -ymax;
+// var xmin = ymin * aspect;
+// var xmax = ymax * aspect;
+// return _makeFrustum(xmin, xmax, ymin, ymax, znear, zfar);
+// }
+
+////
+//// glFrustum
+////
+//function _makeFrustum(left, right, bottom, top, znear, zfar){
+// var X = 2*znear/(right-left);
+// var Y = 2*znear/(top-bottom);
+// var A = (right+left)/(right-left);
+// var B = (top+bottom)/(top-bottom);
+// var C = -(zfar+znear)/(zfar-znear);
+// var D = -2*zfar*znear/(zfar-znear);
+// var frustrumMatrix =[
+// X, 0, A, 0,
+// 0, Y, B, 0,
+// 0, 0, C, D,
+// 0, 0, -1, 0
+//];
+//return frustrumMatrix;
+// }
+
+// function _setMVPMatrices(){
+////an identity matrix
+////@TODO use the p5.Matrix class to abstract away our MV matrices and
+///other math
+//var _mvMatrix =
+//[
+// 1.0,0.0,0.0,0.0,
+// 0.0,1.0,0.0,0.0,
+// 0.0,0.0,1.0,0.0,
+// 0.0,0.0,0.0,1.0
+//];
+
+module.exports = p5.Matrix;
+},{"../core/constants":47,"../core/core":48,"../math/polargeometry":77}],36:[function(_dereq_,module,exports){
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var shader = _dereq_('./shader');
+_dereq_('../core/p5.Renderer');
+_dereq_('./p5.Matrix');
+var uMVMatrixStack = [];
+var RESOLUTION = 1000;
+
+//@TODO should probably implement an override for these attributes
+var attributes = {
+ alpha: true,
+ depth: true,
+ stencil: true,
+ antialias: false,
+ premultipliedAlpha: false,
+ preserveDrawingBuffer: false
+};
+
+/**
+ * 3D graphics class. Can also be used as an off-screen graphics buffer.
+ * A p5.Renderer3D object can be constructed
+ *
+ */
+p5.Renderer3D = function(elt, pInst, isMainCanvas) {
+ p5.Renderer.call(this, elt, pInst, isMainCanvas);
+
+ try {
+ this.drawingContext = this.canvas.getContext('webgl', attributes) ||
+ this.canvas.getContext('experimental-webgl', attributes);
+ if (this.drawingContext === null) {
+ throw new Error('Error creating webgl context');
+ } else {
+ console.log('p5.Renderer3D: enabled webgl context');
+ }
+ } catch (er) {
+ throw new Error(er);
+ }
+
+ this.isP3D = true; //lets us know we're in 3d mode
+ this.GL = this.drawingContext;
+ var gl = this.GL;
+ gl.clearColor(1.0, 1.0, 1.0, 1.0); //background is initialized white
+ gl.clearDepth(1);
+ gl.enable(gl.DEPTH_TEST);
+ gl.depthFunc(gl.LEQUAL);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
+ this._init();
+ return this;
+};
+
+p5.Renderer3D.prototype = Object.create(p5.Renderer.prototype);
+
+p5.Renderer3D.prototype._applyDefaults = function() {
+ return this;
+};
+
+//////////////////////////////////////////////
+// Setting
+//////////////////////////////////////////////
+
+p5.Renderer3D.prototype._init = function(first_argument) {
+ var gl = this.GL;
+ //for our default matrices
+ this.initMatrix();
+ this.initHash();
+ //for immedidate mode
+ this.verticeStack = [];
+ this.verticeBuffer = gl.createBuffer();
+ this.colorBuffer = gl.createBuffer();
+ //for camera
+ this._setCamera = false;
+ //for counting lights
+ this.ambientLightCount = 0;
+ this.directionalLightCount = 0;
+ this.pointLightCount = 0;
+};
+
+p5.Renderer3D.prototype._update = function() {
+ this.resetMatrix();
+ this.translate(0, 0, -800);
+ this.ambientLightCount = 0;
+ this.directionalLightCount = 0;
+ this.pointLightCount = 0;
+ this.verticeStack = [];
+};
+
+/**
+ * [resize description]
+ * @param {[type]} w [description]
+ * @param {[tyoe]} h [description]
+ * @return {[type]} [description]
+ */
+p5.Renderer3D.prototype.resize = function(w,h) {
+ var gl = this.GL;
+ p5.Renderer.prototype.resize.call(this, w, h);
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
+};
+
+/**
+ * [background description]
+ * @return {[type]} [description]
+ */
+p5.Renderer3D.prototype.background = function() {
+ var gl = this.GL;
+ var _col = this._pInst.color.apply(this._pInst, arguments);
+ // gl.clearColor(0.0,0.0,0.0,1.0);
+ var _r = (_col.levels[0]) / 255;
+ var _g = (_col.levels[1]) / 255;
+ var _b = (_col.levels[2]) / 255;
+ var _a = (_col.levels[3]) / 255;
+ gl.clearColor(_r, _g, _b, _a);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+};
+
+//@TODO implement this
+// p5.Renderer3D.prototype.clear = function() {
+//@TODO
+// };
+
+//////////////////////////////////////////////
+// SHADER
+//////////////////////////////////////////////
+
+/**
+ * [initShaders description]
+ * @param {[type]} vertId [description]
+ * @param {[type]} fragId [description]
+ * @return {[type]} [description]
+ */
+p5.Renderer3D.prototype.initShaders = function(vertId, fragId, immediateMode) {
+ var gl = this.GL;
+ //set up our default shaders by:
+ // 1. create the shader,
+ // 2. load the shader source,
+ // 3. compile the shader
+ var _vertShader = gl.createShader(gl.VERTEX_SHADER);
+ //load in our default vertex shader
+ gl.shaderSource(_vertShader, shader[vertId]);
+ gl.compileShader(_vertShader);
+ // if our vertex shader failed compilation?
+ if (!gl.getShaderParameter(_vertShader, gl.COMPILE_STATUS)) {
+ alert('Yikes! An error occurred compiling the shaders:' +
+ gl.getShaderInfoLog(_vertShader));
+ return null;
+ }
+
+ var _fragShader = gl.createShader(gl.FRAGMENT_SHADER);
+ //load in our material frag shader
+ gl.shaderSource(_fragShader, shader[fragId]);
+ gl.compileShader(_fragShader);
+ // if our frag shader failed compilation?
+ if (!gl.getShaderParameter(_fragShader, gl.COMPILE_STATUS)) {
+ alert('Darn! An error occurred compiling the shaders:' +
+ gl.getShaderInfoLog(_fragShader));
+ return null;
+ }
+
+ var shaderProgram = gl.createProgram();
+ gl.attachShader(shaderProgram, _vertShader);
+ gl.attachShader(shaderProgram, _fragShader);
+ gl.linkProgram(shaderProgram);
+ if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
+ alert('Snap! Error linking shader program');
+ }
+ //END SHADERS SETUP
+
+ this._getLocation(shaderProgram, immediateMode);
+
+ return shaderProgram;
+};
+
+p5.Renderer3D.prototype._getLocation = function(shaderProgram, immediateMode) {
+ var gl = this.GL;
+ gl.useProgram(shaderProgram);
+ shaderProgram.uResolution =
+ gl.getUniformLocation(shaderProgram, 'uResolution');
+ gl.uniform1f(shaderProgram.uResolution, RESOLUTION);
+
+ //vertex position Attribute
+ shaderProgram.vertexPositionAttribute =
+ gl.getAttribLocation(shaderProgram, 'aPosition');
+ gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
+
+ //projection Matrix uniform
+ shaderProgram.uPMatrixUniform =
+ gl.getUniformLocation(shaderProgram, 'uProjectionMatrix');
+ //model view Matrix uniform
+ shaderProgram.uMVMatrixUniform =
+ gl.getUniformLocation(shaderProgram, 'uModelViewMatrix');
+
+ //@TODO: figure out a better way instead of if statement
+ if(immediateMode === undefined){
+ //vertex normal Attribute
+ shaderProgram.vertexNormalAttribute =
+ gl.getAttribLocation(shaderProgram, 'aNormal');
+ gl.enableVertexAttribArray(shaderProgram.vertexNormalAttribute);
+
+ //normal Matrix uniform
+ shaderProgram.uNMatrixUniform =
+ gl.getUniformLocation(shaderProgram, 'uNormalMatrix');
+
+ //texture coordinate Attribute
+ shaderProgram.textureCoordAttribute =
+ gl.getAttribLocation(shaderProgram, 'aTexCoord');
+ gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute);
+
+ shaderProgram.samplerUniform =
+ gl.getUniformLocation(shaderProgram, 'uSampler');
+ }
+};
+
+p5.Renderer3D.prototype.setMatrixUniforms = function(shaderKey) {
+ var gl = this.GL;
+ var shaderProgram = this.mHash[shaderKey];
+
+ gl.useProgram(shaderProgram);
+
+ gl.uniformMatrix4fv(
+ shaderProgram.uPMatrixUniform,
+ false, this.uPMatrix.mat4);
+
+ gl.uniformMatrix4fv(
+ shaderProgram.uMVMatrixUniform,
+ false, this.uMVMatrix.mat4);
+
+ this.uNMatrix = new p5.Matrix();
+ this.uNMatrix.invert(this.uMVMatrix);
+ this.uNMatrix.transpose(this.uNMatrix);
+
+ gl.uniformMatrix4fv(
+ shaderProgram.uNMatrixUniform,
+ false, this.uNMatrix.mat4);
+};
+//////////////////////////////////////////////
+// GET CURRENT | for shader and color
+//////////////////////////////////////////////
+p5.Renderer3D.prototype._getShader = function(vertId, fragId, immediateMode) {
+ var mId = vertId+ '|' + fragId;
+ //create it and put it into hashTable
+ if(!this.materialInHash(mId)){
+ var shaderProgram = this.initShaders(vertId, fragId, immediateMode);
+ this.mHash[mId] = shaderProgram;
+ }
+ this.curShaderId = mId;
+
+ return this.mHash[this.curShaderId];
+};
+
+p5.Renderer3D.prototype._getCurShaderId = function(){
+ //if it's not defined yet
+ if(this.curShaderId === undefined){
+ //default shader: normalMaterial()
+ var mId = 'normalVert|normalFrag';
+ var shaderProgram = this.initShaders('normalVert', 'normalFrag');
+ this.mHash[mId] = shaderProgram;
+ this.curShaderId = mId;
+ }
+
+ return this.curShaderId;
+};
+
+p5.Renderer3D.prototype._getCurColor = function() {
+ //default color: gray
+ if(this.curColor === undefined) {
+ this.curColor = [0.5, 0.5, 0.5, 1.0];
+ }
+ return this.curColor;
+};
+
+//////////////////////////////////////////////
+// HASH | for material and geometry
+//////////////////////////////////////////////
+
+p5.Renderer3D.prototype.initHash = function(){
+ this.gHash = {};
+ this.mHash = {};
+};
+
+p5.Renderer3D.prototype.geometryInHash = function(gId){
+ return this.gHash[gId] !== undefined;
+};
+
+p5.Renderer3D.prototype.materialInHash = function(mId){
+ return this.mHash[mId] !== undefined;
+};
+
+//////////////////////////////////////////////
+// MATRIX
+//////////////////////////////////////////////
+
+p5.Renderer3D.prototype.initMatrix = function(){
+ this.uMVMatrix = new p5.Matrix();
+ this.uPMatrix = new p5.Matrix();
+ this.uNMatrix = new p5.Matrix();
+};
+
+p5.Renderer3D.prototype.resetMatrix = function() {
+ this.uMVMatrix = p5.Matrix.identity();
+ //this.uPMatrix = p5.Matrix.identity();
+};
+
+//detect if user didn't set the camera
+//then call this function below
+p5.Renderer3D.prototype._setDefaultCamera = function(){
+ if(!this._setCamera){
+ var _w = this.width;
+ var _h = this.height;
+ this.uPMatrix = p5.Matrix.identity();
+ this.uPMatrix.perspective(60 / 180 * Math.PI, _w / _h, 0.1, 100);
+ this._setCamera = true;
+ }
+};
+
+/**
+ * [translate description]
+ * @param {[type]} x [description]
+ * @param {[type]} y [description]
+ * @param {[type]} z [description]
+ * @return {[type]} [description]
+ * @todo implement handle for components or vector as args
+ */
+p5.Renderer3D.prototype.translate = function(x, y, z) {
+ //@TODO: figure out how to fit the resolution
+ x = x / RESOLUTION;
+ y = -y / RESOLUTION;
+ z = z / RESOLUTION;
+ this.uMVMatrix.translate([x,y,z]);
+ return this;
+};
+
+/**
+ * Scales the Model View Matrix by a vector
+ * @param {Number | p5.Vector | Array} x [description]
+ * @param {Number} [y] y-axis scalar
+ * @param {Number} [z] z-axis scalar
+ * @return {this} [description]
+ */
+p5.Renderer3D.prototype.scale = function(x,y,z) {
+ this.uMVMatrix.scale([x,y,z]);
+ return this;
+};
+
+/**
+ * [rotate description]
+ * @param {Number} rad angle in radians
+ * @param {p5.Vector | Array} axis axis to rotate around
+ * @return {p5.Renderer3D} [description]
+ */
+p5.Renderer3D.prototype.rotate = function(rad, axis){
+ this.uMVMatrix.rotate(rad, axis);
+ return this;
+};
+
+/**
+ * [rotateX description]
+ * @param {Number} rad radians to rotate
+ * @return {[type]} [description]
+ */
+p5.Renderer3D.prototype.rotateX = function(rad) {
+ this.uMVMatrix.rotateX(rad);
+ return this;
+};
+
+/**
+ * [rotateY description]
+ * @param {Number} rad rad radians to rotate
+ * @return {[type]} [description]
+ */
+p5.Renderer3D.prototype.rotateY = function(rad) {
+ this.uMVMatrix.rotateY(rad);
+ return this;
+};
+
+/**
+ * [rotateZ description]
+ * @param {Number} rad rad radians to rotate
+ * @return {[type]} [description]
+ */
+p5.Renderer3D.prototype.rotateZ = function(rad) {
+ this.uMVMatrix.rotateZ(rad);
+ return this;
+};
+
+/**
+ * pushes a copy of the model view matrix onto the
+ * MV Matrix stack.
+ * NOTE to self: could probably make this more readable
+ * @return {[type]} [description]
+ */
+p5.Renderer3D.prototype.push = function() {
+ uMVMatrixStack.push(this.uMVMatrix.copy());
+};
+
+/**
+ * [pop description]
+ * @return {[type]} [description]
+ */
+p5.Renderer3D.prototype.pop = function() {
+ if (uMVMatrixStack.length === 0) {
+ throw new Error('Invalid popMatrix!');
+ }
+ this.uMVMatrix = uMVMatrixStack.pop();
+};
+
+module.exports = p5.Renderer3D;
+
+},{"../core/core":48,"../core/p5.Renderer":54,"./p5.Matrix":35,"./shader":38}],37:[function(_dereq_,module,exports){
+//retained mode is used by rendering 3d_primitives
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var hashCount = 0;
+
+/**
+ * createBuffer
+ * @param {String} gId key of the geometry object
+ * @param {Array} arr array holding bject containing geometry information
+ */
+p5.Renderer3D.prototype.createBuffer = function(gId, arr) {
+
+ hashCount ++;
+ if(hashCount > 1000){
+ var key = Object.keys(this.gHash)[0];
+ delete this.gHash[key];
+ hashCount --;
+ }
+
+ var gl = this.GL;
+ this.gHash[gId] = {};
+ this.gHash[gId].len = [];
+ this.gHash[gId].vertexBuffer = [];
+ this.gHash[gId].normalBuffer = [];
+ this.gHash[gId].uvBuffer = [];
+ this.gHash[gId].indexBuffer =[];
+
+ arr.forEach(function(obj){
+ this.gHash[gId].len.push(obj.len);
+ this.gHash[gId].vertexBuffer.push(gl.createBuffer());
+ this.gHash[gId].normalBuffer.push(gl.createBuffer());
+ this.gHash[gId].uvBuffer.push(gl.createBuffer());
+ this.gHash[gId].indexBuffer.push(gl.createBuffer());
+ }.bind(this));
+};
+
+/**
+ * initBuffer description
+ * @param {String} gId key of the geometry object
+ * @param {Array} arr array holding bject containing geometry information
+ */
+p5.Renderer3D.prototype.initBuffer = function(gId, arr) {
+ this._setDefaultCamera();
+ var gl = this.GL;
+ this.createBuffer(gId, arr);
+
+ var shaderProgram = this.mHash[this._getCurShaderId()];
+
+ arr.forEach(function(obj, i){
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].vertexBuffer[i]);
+ gl.bufferData(
+ gl.ARRAY_BUFFER, new Float32Array(obj.vertices), gl.STATIC_DRAW);
+ gl.vertexAttribPointer(
+ shaderProgram.vertexPositionAttribute,
+ 3, gl.FLOAT, false, 0, 0);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].normalBuffer[i]);
+ gl.bufferData(
+ gl.ARRAY_BUFFER, new Float32Array(obj.vertexNormals), gl.STATIC_DRAW);
+ gl.vertexAttribPointer(
+ shaderProgram.vertexNormalAttribute,
+ 3, gl.FLOAT, false, 0, 0);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].uvBuffer[i]);
+ gl.bufferData(
+ gl.ARRAY_BUFFER, new Float32Array(obj.uvs), gl.STATIC_DRAW);
+ gl.vertexAttribPointer(
+ shaderProgram.textureCoordAttribute,
+ 2, gl.FLOAT, false, 0, 0);
+
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.gHash[gId].indexBuffer[i]);
+ gl.bufferData
+ (gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(obj.faces), gl.STATIC_DRAW);
+ }.bind(this));
+};
+
+/**
+ * drawBuffer
+ * @param {String} gId key of the geometery object
+ */
+p5.Renderer3D.prototype.drawBuffer = function(gId) {
+ this._setDefaultCamera();
+ var gl = this.GL;
+ var shaderKey = this._getCurShaderId();
+ var shaderProgram = this.mHash[shaderKey];
+
+ this.gHash[gId].len.forEach(function(d, i){
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].vertexBuffer[i]);
+ gl.vertexAttribPointer(
+ shaderProgram.vertexPositionAttribute,
+ 3, gl.FLOAT, false, 0, 0);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].normalBuffer[i]);
+ gl.vertexAttribPointer(
+ shaderProgram.vertexNormalAttribute,
+ 3, gl.FLOAT, false, 0, 0);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.gHash[gId].uvBuffer[i]);
+ gl.vertexAttribPointer(
+ shaderProgram.textureCoordAttribute,
+ 2, gl.FLOAT, false, 0, 0);
+
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.gHash[gId].indexBuffer[i]);
+
+ this.setMatrixUniforms(shaderKey);
+
+ gl.drawElements(
+ gl.TRIANGLES, this.gHash[gId].len[i],
+ gl.UNSIGNED_SHORT, 0);
+ }.bind(this));
+};
+
+module.exports = p5.Renderer3D;
+},{"../core/core":48}],38:[function(_dereq_,module,exports){
+
+
+module.exports = {
+ vertexColorVert:
+ "attribute vec3 aPosition;\nattribute vec4 aVertexColor;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform float uResolution;\n\nvarying vec4 vColor;\n\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition / uResolution * vec3(1.0, -1.0, 1.0), 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vColor = aVertexColor;\n}",
+ vertexColorFrag:
+ "precision mediump float;\nvarying vec4 vColor;\nvoid main(void) {\n gl_FragColor = vColor;\n}",
+ normalVert:
+ "attribute vec3 aPosition;\nattribute vec3 aNormal;\nattribute vec2 aTexCoord;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform mat4 uNormalMatrix;\nuniform float uResolution;\n\nvarying vec3 vVertexNormal;\nvarying highp vec2 vVertTexCoord;\n\nvoid main(void) {\n vec4 positionVec4 = vec4(aPosition / uResolution, 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n vVertexNormal = vec3( uNormalMatrix * vec4( aNormal, 1.0 ) );\n vVertTexCoord = aTexCoord;\n}",
+ normalFrag:
+ "precision mediump float;\nvarying vec3 vVertexNormal;\nvoid main(void) {\n gl_FragColor = vec4(vVertexNormal, 1.0);\n}",
+ basicFrag:
+ "precision mediump float;\nvarying vec3 vVertexNormal;\nuniform vec4 uMaterialColor;\nvoid main(void) {\n gl_FragColor = uMaterialColor;\n}",
+ lightVert:
+ "attribute vec3 aPosition;\nattribute vec3 aNormal;\nattribute vec2 aTexCoord;\n\nuniform mat4 uModelViewMatrix;\nuniform mat4 uProjectionMatrix;\nuniform mat4 uNormalMatrix;\nuniform float uResolution;\nuniform int uAmbientLightCount;\nuniform int uDirectionalLightCount;\nuniform int uPointLightCount;\n\nuniform vec3 uAmbientColor[8];\nuniform vec3 uLightingDirection[8];\nuniform vec3 uDirectionalColor[8];\nuniform vec3 uPointLightLocation[8];\nuniform vec3 uPointLightColor[8];\nuniform bool uSpecular;\n\nvarying vec3 vVertexNormal;\nvarying vec2 vVertTexCoord;\nvarying vec3 vLightWeighting;\n\nvec3 ambientLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 directionalLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 pointLightFactor = vec3(0.0, 0.0, 0.0);\nvec3 pointLightFactor2 = vec3(0.0, 0.0, 0.0);\n\nvoid main(void){\n\n vec4 positionVec4 = vec4(aPosition / uResolution, 1.0);\n gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;\n\n vec3 vertexNormal = vec3( uNormalMatrix * vec4( aNormal, 1.0 ) );\n vVertexNormal = vertexNormal;\n vVertTexCoord = aTexCoord;\n\n vec4 mvPosition = uModelViewMatrix * vec4(aPosition / uResolution, 1.0);\n vec3 eyeDirection = normalize(-mvPosition.xyz);\n\n float shininess = 32.0;\n float specularFactor = 2.0;\n float diffuseFactor = 0.3;\n\n for(int i = 0; i < 8; i++){\n if(uAmbientLightCount == i) break;\n ambientLightFactor += uAmbientColor[i];\n }\n\n for(int j = 0; j < 8; j++){\n if(uDirectionalLightCount == j) break;\n vec3 dir = uLightingDirection[j];\n float directionalLightWeighting = max(dot(vertexNormal, dir), 0.0);\n directionalLightFactor += uDirectionalColor[j] * directionalLightWeighting;\n }\n\n for(int k = 0; k < 8; k++){\n if(uPointLightCount == k) break;\n vec3 loc = uPointLightLocation[k];\n //loc = loc / uResolution;\n vec3 lightDirection = normalize(loc - mvPosition.xyz);\n\n float directionalLightWeighting = max(dot(vertexNormal, lightDirection), 0.0);\n pointLightFactor += uPointLightColor[k] * directionalLightWeighting;\n\n //factor2 for specular\n vec3 reflectionDirection = reflect(-lightDirection, vertexNormal);\n float specularLightWeighting = pow(max(dot(reflectionDirection, eyeDirection), 0.0), shininess);\n\n pointLightFactor2 += uPointLightColor[k] * (specularFactor * specularLightWeighting\n + directionalLightWeighting * diffuseFactor);\n }\n \n if(!uSpecular){\n vLightWeighting = ambientLightFactor + directionalLightFactor + pointLightFactor;\n }else{\n vLightWeighting = ambientLightFactor + directionalLightFactor + pointLightFactor2;\n }\n\n}",
+ lightTextureFrag:
+ "precision mediump float;\n\nuniform vec4 uMaterialColor;\nuniform sampler2D uSampler;\nuniform bool isTexture;\n\nvarying vec3 vLightWeighting;\nvarying highp vec2 vVertTexCoord;\n\nvoid main(void) {\n if(!isTexture){\n gl_FragColor = vec4(vec3(uMaterialColor.rgb * vLightWeighting), uMaterialColor.a);\n }else{\n vec4 textureColor = texture2D(uSampler, vVertTexCoord);\n if(vLightWeighting == vec3(0., 0., 0.)){\n gl_FragColor = textureColor;\n }else{\n gl_FragColor = vec4(vec3(textureColor.rgb * vLightWeighting), textureColor.a); \n }\n }\n}"
+};
+},{}],39:[function(_dereq_,module,exports){
+
+'use strict';
+
+var p5 = _dereq_('./core/core');
+_dereq_('./color/p5.Color');
+_dereq_('./core/p5.Element');
+_dereq_('./typography/p5.Font');
+_dereq_('./core/p5.Graphics');
+_dereq_('./core/p5.Renderer2D');
+
+_dereq_('./image/p5.Image');
+_dereq_('./math/p5.Vector');
+_dereq_('./io/p5.TableRow');
+_dereq_('./io/p5.Table');
+
+_dereq_('./color/creating_reading');
+_dereq_('./color/setting');
+_dereq_('./core/constants');
+_dereq_('./utilities/conversion');
+_dereq_('./utilities/array_functions');
+_dereq_('./utilities/string_functions');
+_dereq_('./core/environment');
+_dereq_('./image/image');
+_dereq_('./image/loading_displaying');
+_dereq_('./image/pixels');
+_dereq_('./io/files');
+_dereq_('./events/keyboard');
+_dereq_('./events/acceleration'); //john
+_dereq_('./events/mouse');
+_dereq_('./utilities/time_date');
+_dereq_('./events/touch');
+_dereq_('./math/math');
+_dereq_('./math/calculation');
+_dereq_('./math/random');
+_dereq_('./math/noise');
+_dereq_('./math/trigonometry');
+_dereq_('./core/rendering');
+_dereq_('./core/2d_primitives');
+
+_dereq_('./core/attributes');
+_dereq_('./core/curves');
+_dereq_('./core/vertex');
+_dereq_('./core/structure');
+_dereq_('./core/transform');
+_dereq_('./typography/attributes');
+_dereq_('./typography/loading_displaying');
+
+_dereq_('./3d/p5.Renderer3D');
+_dereq_('./3d/p5.Geometry3D');
+_dereq_('./3d/retainedMode3D');
+_dereq_('./3d/immediateMode3D');
+_dereq_('./3d/3d_primitives');
+_dereq_('./3d/p5.Matrix');
+_dereq_('./3d/material');
+_dereq_('./3d/light');
+_dereq_('./3d/shader');
+_dereq_('./3d/camera');
+_dereq_('./3d/interaction');
+
+/**
+ * _globalInit
+ *
+ * TODO: ???
+ * if sketch is on window
+ * assume "global" mode
+ * and instantiate p5 automatically
+ * otherwise do nothing
+ *
+ * @return {Undefined}
+ */
+var _globalInit = function() {
+ if (!window.PHANTOMJS && !window.mocha) {
+ // If there is a setup or draw function on the window
+ // then instantiate p5 in "global" mode
+ if((window.setup && typeof window.setup === 'function') ||
+ (window.draw && typeof window.draw === 'function')) {
+ new p5();
+ }
+ }
+};
+
+// TODO: ???
+if (document.readyState === 'complete') {
+ _globalInit();
+} else {
+ window.addEventListener('load', _globalInit , false);
+}
+
+module.exports = p5;
+},{"./3d/3d_primitives":28,"./3d/camera":29,"./3d/immediateMode3D":30,"./3d/interaction":31,"./3d/light":32,"./3d/material":33,"./3d/p5.Geometry3D":34,"./3d/p5.Matrix":35,"./3d/p5.Renderer3D":36,"./3d/retainedMode3D":37,"./3d/shader":38,"./color/creating_reading":41,"./color/p5.Color":42,"./color/setting":43,"./core/2d_primitives":44,"./core/attributes":45,"./core/constants":47,"./core/core":48,"./core/curves":49,"./core/environment":50,"./core/p5.Element":52,"./core/p5.Graphics":53,"./core/p5.Renderer2D":55,"./core/rendering":56,"./core/structure":58,"./core/transform":59,"./core/vertex":60,"./events/acceleration":61,"./events/keyboard":62,"./events/mouse":63,"./events/touch":64,"./image/image":66,"./image/loading_displaying":67,"./image/p5.Image":68,"./image/pixels":69,"./io/files":70,"./io/p5.Table":71,"./io/p5.TableRow":72,"./math/calculation":73,"./math/math":74,"./math/noise":75,"./math/p5.Vector":76,"./math/random":78,"./math/trigonometry":79,"./typography/attributes":80,"./typography/loading_displaying":81,"./typography/p5.Font":82,"./utilities/array_functions":83,"./utilities/conversion":84,"./utilities/string_functions":85,"./utilities/time_date":86}],40:[function(_dereq_,module,exports){
+/**
+ * module Conversion
+ * submodule Color Conversion
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+/**
+ * Conversions adapted from <http://www.easyrgb.com/math.html>.
+ *
+ * In these functions, hue is always in the range [0,1); all other components
+ * are in the range [0,1]. 'Brightness' and 'value' are used interchangeably.
+ */
+
+var p5 = _dereq_('../core/core');
+p5.ColorConversion = {};
+
+/**
+ * Convert an HSBA array to HSLA.
+ */
+p5.ColorConversion._hsbaToHSLA = function(hsba) {
+ var hue = hsba[0];
+ var sat = hsba[1];
+ var val = hsba[2];
+
+ // Calculate lightness.
+ var li = (2 - sat) * val / 2;
+
+ // Convert saturation.
+ if (li !== 0) {
+ if (li === 1) {
+ sat = 0;
+ } else if (li < 0.5) {
+ sat = sat / (2 - sat);
+ } else {
+ sat = sat * val / (2 - li * 2);
+ }
+ }
+
+ // Hue and alpha stay the same.
+ return [hue, sat, li, hsba[3]];
+};
+
+/**
+ * Convert an HSBA array to RGBA.
+ */
+p5.ColorConversion._hsbaToRGBA = function(hsba) {
+ var hue = hsba[0] * 6; // We will split hue into 6 sectors.
+ var sat = hsba[1];
+ var val = hsba[2];
+
+ var RGBA = [];
+
+ if (sat === 0) {
+ RGBA = [val, val, val, hsba[3]]; // Return early if grayscale.
+ } else {
+ var sector = Math.floor(hue);
+ var tint1 = val * (1 - sat);
+ var tint2 = val * (1 - sat * (hue - sector));
+ var tint3 = val * (1 - sat * (1 + sector - hue));
+ var red, green, blue;
+ if (sector === 0) { // Red to yellow.
+ red = val;
+ green = tint3;
+ blue = tint1;
+ } else if (sector === 1) { // Yellow to green.
+ red = tint2;
+ green = val;
+ blue = tint1;
+ } else if (sector === 2) { // Green to cyan.
+ red = tint1;
+ green = val;
+ blue = tint3;
+ } else if (sector === 3) { // Cyan to blue.
+ red = tint1;
+ green = tint2;
+ blue = val;
+ } else if (sector === 4) { // Blue to magenta.
+ red = tint3;
+ green = tint1;
+ blue = val;
+ } else { // Magenta to red.
+ red = val;
+ green = tint1;
+ blue = tint2;
+ }
+ RGBA = [red, green, blue, hsba[3]];
+ }
+
+ return RGBA;
+};
+
+/**
+ * Convert an HSLA array to HSBA.
+ */
+p5.ColorConversion._hslaToHSBA = function(hsla) {
+ var hue = hsla[0];
+ var sat = hsla[1];
+ var li = hsla[2];
+
+ // Calculate brightness.
+ var val;
+ if (li < 0.5) {
+ val = (1 + sat) * li;
+ } else {
+ val = li + sat - li * sat;
+ }
+
+ // Convert saturation.
+ sat = 2 * (val - li) / val;
+
+ // Hue and alpha stay the same.
+ return [hue, sat, val, hsla[3]];
+};
+
+/**
+ * Convert an HSLA array to RGBA.
+ *
+ * We need to change basis from HSLA to something that can be more easily be
+ * projected onto RGBA. We will choose hue and brightness as our first two
+ * components, and pick a convenient third one ('zest') so that we don't need
+ * to calculate formal HSBA saturation.
+ */
+p5.ColorConversion._hslaToRGBA = function(hsla){
+ var hue = hsla[0] * 6; // We will split hue into 6 sectors.
+ var sat = hsla[1];
+ var li = hsla[2];
+
+ var RGBA = [];
+
+ if (sat === 0) {
+ RGBA = [li, li, li, hsla[3]]; // Return early if grayscale.
+ } else {
+
+ // Calculate brightness.
+ var val;
+ if (li < 0.5) {
+ val = (1 + sat) * li;
+ } else {
+ val = li + sat - li * sat;
+ }
+
+ // Define zest.
+ var zest = 2 * li - val;
+
+ // Implement projection (project onto green by default).
+ var hzvToRGB = function(hue, zest, val) {
+ if (hue < 0) { // Hue must wrap to allow projection onto red and blue.
+ hue += 6;
+ } else if (hue >= 6) {
+ hue -= 6;
+ }
+ if (hue < 1) { // Red to yellow (increasing green).
+ return (zest + (val - zest) * hue);
+ } else if (hue < 3) { // Yellow to cyan (greatest green).
+ return val;
+ } else if (hue < 4) { // Cyan to blue (decreasing green).
+ return (zest + (val - zest) * (4 - hue));
+ } else { // Blue to red (least green).
+ return zest;
+ }
+ };
+
+ // Perform projections, offsetting hue as necessary.
+ RGBA = [hzvToRGB(hue + 2, zest, val),
+ hzvToRGB(hue , zest, val),
+ hzvToRGB(hue - 2, zest, val),
+ hsla[3]];
+ }
+
+ return RGBA;
+};
+
+/**
+ * Convert an RGBA array to HSBA.
+ */
+p5.ColorConversion._rgbaToHSBA = function(rgba) {
+ var red = rgba[0];
+ var green = rgba[1];
+ var blue = rgba[2];
+
+ var val = Math.max(red, green, blue);
+ var chroma = val - Math.min(red, green, blue);
+
+ var hue, sat;
+ if (chroma === 0) { // Return early if grayscale.
+ hue = 0;
+ sat = 0;
+ }
+ else {
+ sat = chroma / val;
+ if (red === val) { // Magenta to yellow.
+ hue = (green - blue) / chroma;
+ } else if (green === val) { // Yellow to cyan.
+ hue = 2 + (blue - red) / chroma;
+ } else if (blue === val) { // Cyan to magenta.
+ hue = 4 + (red - green) / chroma;
+ }
+ if (hue < 0) { // Confine hue to the interval [0, 1).
+ hue += 6;
+ } else if (hue >= 6) {
+ hue -= 6;
+ }
+ }
+
+ return [hue / 6, sat, val, rgba[3]];
+};
+
+/**
+ * Convert an RGBA array to HSLA.
+ */
+p5.ColorConversion._rgbaToHSLA = function(rgba) {
+ var red = rgba[0];
+ var green = rgba[1];
+ var blue = rgba[2];
+
+ var val = Math.max(red, green, blue);
+ var min = Math.min(red, green, blue);
+ var li = val + min; // We will halve this later.
+ var chroma = val - min;
+
+ var hue, sat;
+ if (chroma === 0) { // Return early if grayscale.
+ hue = 0;
+ sat = 0;
+ } else {
+ if (li < 1) {
+ sat = chroma / li;
+ } else {
+ sat = chroma / (2 - chroma);
+ }
+ if (red === val) { // Magenta to yellow.
+ hue = (green - blue) / chroma;
+ } else if (green === val) { // Yellow to cyan.
+ hue = 2 + (blue - red) / chroma;
+ } else if (blue === val) { // Cyan to magenta.
+ hue = 4 + (red - green) / chroma;
+ }
+ if (hue < 0) { // Confine hue to the interval [0, 1).
+ hue += 6;
+ } else if (hue >= 6) {
+ hue -= 6;
+ }
+ }
+
+ return [hue / 6, sat, li / 2, rgba[3]];
+};
+
+module.exports = p5.ColorConversion;
+
+},{"../core/core":48}],41:[function(_dereq_,module,exports){
+/**
+ * @module Color
+ * @submodule Creating & Reading
+ * @for p5
+ * @requires core
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var constants = _dereq_('../core/constants');
+_dereq_('./p5.Color');
+
+/**
+ * Extracts the alpha value from a color or pixel array.
+ *
+ * @method alpha
+ * @param {Object} obj p5.Color object or pixel array
+ * @example
+ * <div>
+ * <code>
+ * noStroke();
+ * c = color(0, 126, 255, 102);
+ * fill(c);
+ * rect(15, 15, 35, 70);
+ * value = alpha(c); // Sets 'value' to 102
+ * fill(value);
+ * rect(50, 15, 35, 70);
+ * </code>
+ * </div>
+ */
+p5.prototype.alpha = function(c) {
+ if (c instanceof p5.Color || c instanceof Array) {
+ return this.color(c)._getAlpha();
+ } else {
+ throw new Error('Needs p5.Color or pixel array as argument.');
+ }
+};
+
+/**
+ * Extracts the blue value from a color or pixel array.
+ *
+ * @method blue
+ * @param {Object} obj p5.Color object or pixel array
+ * @example
+ * <div>
+ * <code>
+ * c = color(175, 100, 220); // Define color 'c'
+ * fill(c); // Use color variable 'c' as fill color
+ * rect(15, 20, 35, 60); // Draw left rectangle
+ *
+ * blueValue = blue(c); // Get blue in 'c'
+ * println(blueValue); // Prints "220.0"
+ * fill(0, 0, blueValue); // Use 'blueValue' in new fill
+ * rect(50, 20, 35, 60); // Draw right rectangle
+ * </code>
+ * </div>
+ */
+p5.prototype.blue = function(c) {
+ if (c instanceof p5.Color || c instanceof Array) {
+ return this.color(c)._getBlue();
+ } else {
+ throw new Error('Needs p5.Color or pixel array as argument.');
+ }
+};
+
+/**
+ * Extracts the HSB brightness value from a color or pixel array.
+ *
+ * @method brightness
+ * @param {Object} color p5.Color object or pixel array
+ * @example
+ * <div>
+ * <code>
+ * noStroke();
+ * colorMode(HSB, 255);
+ * c = color(0, 126, 255);
+ * fill(c);
+ * rect(15, 20, 35, 60);
+ * value = brightness(c); // Sets 'value' to 255
+ * fill(value);
+ * rect(50, 20, 35, 60);
+ * </code>
+ * </div>
+ */
+p5.prototype.brightness = function(c) {
+ if (c instanceof p5.Color || c instanceof Array) {
+ return this.color(c)._getBrightness();
+ } else {
+ throw new Error('Needs p5.Color or pixel array as argument.');
+ }
+};
+
+/**
+ * Creates colors for storing in variables of the color datatype. The
+ * parameters are interpreted as RGB or HSB values depending on the
+ * current colorMode(). The default mode is RGB values from 0 to 255
+ * and, therefore, the function call color(255, 204, 0) will return a
+ * bright yellow color.
+ * <br><br>
+ * Note that if only one value is provided to color(), it will be interpreted
+ * as a grayscale value. Add a second value, and it will be used for alpha
+ * transparency. When three values are specified, they are interpreted as
+ * either RGB or HSB values. Adding a fourth value applies alpha
+ * transparency. If a single string parameter is provided it will be
+ * interpreted as a CSS-compatible color string.
+ *
+ * Colors are stored as Numbers or Arrays.
+ *
+ * @method color
+ * @param {Number|String} v1 gray value or red or hue value relative to
+ * the current color range, or a color string
+ * @param {Number} [v2] gray value or green or saturation value
+ * relative to the current color range (or
+ * alpha value if first param is gray value)
+ * @param {Number} [v3] gray value or blue or brightness value
+ * relative to the current color range
+ * @param {Number} [alpha] alpha value relative to current color range
+ * @return {Array} resulting color
+ *
+ * @example
+ * <div>
+ * <code>
+ * var c = color(255, 204, 0); // Define color 'c'
+ * fill(c); // Use color variable 'c' as fill color
+ * noStroke(); // Don't draw a stroke around shapes
+ * rect(30, 20, 55, 55); // Draw rectangle
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var c = color(255, 204, 0); // Define color 'c'
+ * fill(c); // Use color variable 'c' as fill color
+ * noStroke(); // Don't draw a stroke around shapes
+ * ellipse(25, 25, 80, 80); // Draw left circle
+ *
+ * // Using only one value with color()
+ * // generates a grayscale value.
+ * var c = color(65); // Update 'c' with grayscale value
+ * fill(c); // Use updated 'c' as fill color
+ * ellipse(75, 75, 80, 80); // Draw right circle
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // Named SVG & CSS colors may be used,
+ * var c = color('magenta');
+ * fill(c); // Use 'c' as fill color
+ * noStroke(); // Don't draw a stroke around shapes
+ * rect(20, 20, 60, 60); // Draw rectangle
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // as can hex color codes:
+ * noStroke(); // Don't draw a stroke around shapes
+ * var c = color('#0f0');
+ * fill(c); // Use 'c' as fill color
+ * rect(0, 10, 45, 80); // Draw rectangle
+ *
+ * c = color('#00ff00');
+ * fill(c); // Use updated 'c' as fill color
+ * rect(55, 10, 45, 80); // Draw rectangle
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // RGB and RGBA color strings are also supported:
+ * // these all set to the same color (solid blue)
+ * var c;
+ * noStroke(); // Don't draw a stroke around shapes
+ * c = color('rgb(0,0,255)');
+ * fill(c); // Use 'c' as fill color
+ * rect(10, 10, 35, 35); // Draw rectangle
+ *
+ * c = color('rgb(0%, 0%, 100%)');
+ * fill(c); // Use updated 'c' as fill color
+ * rect(55, 10, 35, 35); // Draw rectangle
+ *
+ * c = color('rgba(0, 0, 255, 1)');
+ * fill(c); // Use updated 'c' as fill color
+ * rect(10, 55, 35, 35); // Draw rectangle
+ *
+ * c = color('rgba(0%, 0%, 100%, 1)');
+ * fill(c); // Use updated 'c' as fill color
+ * rect(55, 55, 35, 35); // Draw rectangle
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // HSL color is also supported and can be specified
+ * // by value
+ * var c;
+ * noStroke(); // Don't draw a stroke around shapes
+ * c = color('hsl(160, 100%, 50%)');
+ * fill(c); // Use 'c' as fill color
+ * rect(0, 10, 45, 80); // Draw rectangle
+ *
+ * c = color('hsla(160, 100%, 50%, 0.5)');
+ * fill(c); // Use updated 'c' as fill color
+ * rect(55, 10, 45, 80); // Draw rectangle
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // HSB color is also supported and can be specified
+ * // by value
+ * var c;
+ * noStroke(); // Don't draw a stroke around shapes
+ * c = color('hsb(160, 100%, 50%)');
+ * fill(c); // Use 'c' as fill color
+ * rect(0, 10, 45, 80); // Draw rectangle
+ *
+ * c = color('hsba(160, 100%, 50%, 0.5)');
+ * fill(c); // Use updated 'c' as fill color
+ * rect(55, 10, 45, 80); // Draw rectangle
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var c; // Declare color 'c'
+ * noStroke(); // Don't draw a stroke around shapes
+ *
+ * // If no colorMode is specified, then the
+ * // default of RGB with scale of 0-255 is used.
+ * c = color(50, 55, 100); // Create a color for 'c'
+ * fill(c); // Use color variable 'c' as fill color
+ * rect(0, 10, 45, 80); // Draw left rect
+ *
+ * colorMode(HSB, 100); // Use HSB with scale of 0-100
+ * c = color(50, 55, 100); // Update 'c' with new color
+ * fill(c); // Use updated 'c' as fill color
+ * rect(55, 10, 45, 80); // Draw right rect
+ * </code>
+ * </div>
+ */
+p5.prototype.color = function() {
+ if (arguments[0] instanceof p5.Color) {
+ return arguments[0]; // Do nothing if argument is already a color object.
+ } else if (arguments[0] instanceof Array) {
+ if (this instanceof p5.Renderer) {
+ return new p5.Color(this, arguments[0]);
+ } else {
+ return new p5.Color(this._renderer, arguments[0]);
+ }
+ } else {
+ if (this instanceof p5.Renderer) {
+ return new p5.Color(this, arguments);
+ } else {
+ return new p5.Color(this._renderer, arguments);
+ }
+ }
+};
+
+/**
+ * Extracts the green value from a color or pixel array.
+ *
+ * @method green
+ * @param {Object} color p5.Color object or pixel array
+ * @example
+ * <div>
+ * <code>
+ * c = color(20, 75, 200); // Define color 'c'
+ * fill(c); // Use color variable 'c' as fill color
+ * rect(15, 20, 35, 60); // Draw left rectangle
+ *
+ * greenValue = green(c); // Get green in 'c'
+ * println(greenValue); // Print "75.0"
+ * fill(0, greenValue, 0); // Use 'greenValue' in new fill
+ * rect(50, 20, 35, 60); // Draw right rectangle
+ * </code>
+ * </div>
+ */
+p5.prototype.green = function(c) {
+ if (c instanceof p5.Color || c instanceof Array) {
+ return this.color(c)._getGreen();
+ } else {
+ throw new Error('Needs p5.Color or pixel array as argument.');
+ }
+};
+
+/**
+ * Extracts the hue value from a color or pixel array.
+ *
+ * Hue exists in both HSB and HSL. This function will return the
+ * HSB-normalized hue when supplied with an HSB color object (or when supplied
+ * with a pixel array while the color mode is HSB), but will default to the
+ * HSL-normalized hue otherwise. (The values will only be different if the
+ * maximum hue setting for each system is different.)
+ *
+ * @method hue
+ * @param {Object} color p5.Color object or pixel array
+ * @example
+ * <div>
+ * <code>
+ * noStroke();
+ * colorMode(HSB, 255);
+ * c = color(0, 126, 255);
+ * fill(c);
+ * rect(15, 20, 35, 60);
+ * value = hue(c); // Sets 'value' to "0"
+ * fill(value);
+ * rect(50, 20, 35, 60);
+ * </code>
+ * </div>
+ */
+p5.prototype.hue = function(c) {
+ if (c instanceof p5.Color || c instanceof Array) {
+ return this.color(c)._getHue();
+ } else {
+ throw new Error('Needs p5.Color or pixel array as argument.');
+ }
+};
+
+/**
+ * Blends two colors to find a third color somewhere between them. The amt
+ * parameter is the amount to interpolate between the two values where 0.0
+ * equal to the first color, 0.1 is very near the first color, 0.5 is halfway
+ * in between, etc. An amount below 0 will be treated as 0. Likewise, amounts
+ * above 1 will be capped at 1. This is different from the behavior of lerp(),
+ * but necessary because otherwise numbers outside the range will produce
+ * strange and unexpected colors.
+ * <br><br>
+ * The way that colours are interpolated depends on the current color mode.
+ *
+ * @method lerpColor
+ * @param {Array/Number} c1 interpolate from this color
+ * @param {Array/Number} c2 interpolate to this color
+ * @param {Number} amt number between 0 and 1
+ * @return {Array/Number} interpolated color
+ * @example
+ * <div>
+ * <code>
+ * colorMode(RGB);
+ * stroke(255);
+ * background(51);
+ * from = color(218, 165, 32);
+ * to = color(72, 61, 139);
+ * colorMode(RGB); // Try changing to HSB.
+ * interA = lerpColor(from, to, .33);
+ * interB = lerpColor(from, to, .66);
+ * fill(from);
+ * rect(10, 20, 20, 60);
+ * fill(interA);
+ * rect(30, 20, 20, 60);
+ * fill(interB);
+ * rect(50, 20, 20, 60);
+ * fill(to);
+ * rect(70, 20, 20, 60);
+ * </code>
+ * </div>
+ */
+p5.prototype.lerpColor = function(c1, c2, amt) {
+ var mode = this._renderer._colorMode;
+ var maxes = this._renderer._colorMaxes;
+ var l0, l1, l2, l3;
+ var fromArray, toArray;
+
+ if (mode === constants.RGB) {
+ fromArray = c1.levels.map(function(level) {
+ return level / 255;
+ });
+ toArray = c2.levels.map(function(level) {
+ return level / 255;
+ });
+ } else if (mode === constants.HSB) {
+ c1._getBrightness(); // Cache hsba so it definitely exists.
+ c2._getBrightness();
+ fromArray = c1.hsba;
+ toArray = c2.hsba;
+ } else if (mode === constants.HSL) {
+ c1._getLightness(); // Cache hsla so it definitely exists.
+ c2._getLightness();
+ fromArray = c1.hsla;
+ toArray = c2.hsla;
+ } else {
+ throw new Error (mode + 'cannot be used for interpolation.');
+ }
+
+ // Prevent extrapolation.
+ amt = Math.max(Math.min(amt, 1), 0);
+
+ // Perform interpolation.
+ l0 = this.lerp(fromArray[0], toArray[0], amt);
+ l1 = this.lerp(fromArray[1], toArray[1], amt);
+ l2 = this.lerp(fromArray[2], toArray[2], amt);
+ l3 = this.lerp(fromArray[3], toArray[3], amt);
+
+ // Scale components.
+ l0 *= maxes[mode][0];
+ l1 *= maxes[mode][1];
+ l2 *= maxes[mode][2];
+ l3 *= maxes[mode][3];
+
+ return this.color(l0, l1, l2, l3);
+};
+
+/**
+ * Extracts the HSL lightness value from a color or pixel array.
+ *
+ * @method lightness
+ * @param {Object} color p5.Color object or pixel array
+ * @example
+ * <div>
+ * <code>
+ * noStroke();
+ * colorMode(HSL);
+ * c = color(156, 100, 50, 1);
+ * fill(c);
+ * rect(15, 20, 35, 60);
+ * value = lightness(c); // Sets 'value' to 50
+ * fill(value);
+ * rect(50, 20, 35, 60);
+ * </code>
+ * </div>
+ */
+p5.prototype.lightness = function(c) {
+ if (c instanceof p5.Color || c instanceof Array) {
+ return this.color(c)._getLightness();
+ } else {
+ throw new Error('Needs p5.Color or pixel array as argument.');
+ }
+};
+
+/**
+ * Extracts the red value from a color or pixel array.
+ *
+ * @method red
+ * @param {Object} obj p5.Color object or pixel array
+ * @example
+ * <div>
+ * <code>
+ * c = color(255, 204, 0); // Define color 'c'
+ * fill(c); // Use color variable 'c' as fill color
+ * rect(15, 20, 35, 60); // Draw left rectangle
+ *
+ * redValue = red(c); // Get red in 'c'
+ * println(redValue); // Print "255.0"
+ * fill(redValue, 0, 0); // Use 'redValue' in new fill
+ * rect(50, 20, 35, 60); // Draw right rectangle
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * colorMode(RGB, 255);
+ * var c = color(127, 255, 0);
+ * colorMode(RGB, 1);
+ * var myColor = red(c);
+ * print(myColor);
+ * </code>
+ * </div>
+ */
+p5.prototype.red = function(c) {
+ if (c instanceof p5.Color || c instanceof Array) {
+ return this.color(c)._getRed();
+ } else {
+ throw new Error('Needs p5.Color or pixel array as argument.');
+ }
+};
+
+/**
+ * Extracts the saturation value from a color or pixel array.
+ *
+ * Saturation is scaled differently in HSB and HSL. This function will return
+ * the HSB saturation when supplied with an HSB color object (or when supplied
+ * with a pixel array while the color mode is HSB), but will default to the
+ * HSL saturation otherwise.
+ *
+ * @method saturation
+ * @param {Object} color p5.Color object or pixel array
+ * @example
+ * <div>
+ * <code>
+ * noStroke();
+ * colorMode(HSB, 255);
+ * c = color(0, 126, 255);
+ * fill(c);
+ * rect(15, 20, 35, 60);
+ * value = saturation(c); // Sets 'value' to 126
+ * fill(value);
+ * rect(50, 20, 35, 60);
+ * </code>
+ * </div>
+ */
+p5.prototype.saturation = function(c) {
+ if (c instanceof p5.Color || c instanceof Array) {
+ return this.color(c)._getSaturation();
+ } else {
+ throw new Error('Needs p5.Color or pixel array as argument.');
+ }
+};
+
+module.exports = p5;
+
+},{"../core/constants":47,"../core/core":48,"./p5.Color":42}],42:[function(_dereq_,module,exports){
+/**
+ * @module Color
+ * @submodule Creating & Reading
+ * @for p5
+ * @requires core
+ * @requires constants
+ * @requires color_conversion
+ */
+
+var p5 = _dereq_('../core/core');
+var constants = _dereq_('../core/constants');
+var color_conversion = _dereq_('./color_conversion');
+
+/**
+ * We define colors to be immutable objects. Each color stores the color mode
+ * and level maxes that applied at the time of its construction. These are
+ * used to interpret the input arguments and to format the output e.g. when
+ * saturation() is requested.
+ *
+ * Internally we store an array representing the ideal RGBA values in floating
+ * point form, normalized from 0 to 1. From this we calculate the closest
+ * screen color (RGBA levels from 0 to 255) and expose this to the renderer.
+ *
+ * We also cache normalized, floating point components of the color in various
+ * representations as they are calculated. This is done to prevent repeating a
+ * conversion that has already been performed.
+ *
+ * @class p5.Color
+ * @constructor
+ */
+p5.Color = function(renderer, vals) {
+
+ // Record color mode and maxes at time of construction.
+ this.mode = renderer._colorMode;
+ this.maxes = renderer._colorMaxes;
+
+ // Calculate normalized RGBA values.
+ if (this.mode !== constants.RGB &&
+ this.mode !== constants.HSL &&
+ this.mode !== constants.HSB) {
+ throw new Error(this.mode + ' is an invalid colorMode.');
+ } else {
+ this._array = p5.Color._parseInputs.apply(renderer, vals);
+ }
+
+ // Expose closest screen color.
+ this.levels = this._array.map(function(level) {
+ return Math.round(level * 255);
+ });
+
+ return this;
+};
+
+p5.Color.prototype.toString = function() {
+ var a = this.levels;
+ a[3] = this._array[3]; // String representation uses normalized alpha.
+ return 'rgba('+a[0]+','+a[1]+','+a[2]+','+ a[3] +')';
+};
+
+p5.Color.prototype._getAlpha = function() {
+ return this._array[3] * this.maxes[this.mode][3];
+};
+
+p5.Color.prototype._getBlue = function() {
+ return this._array[2] * this.maxes[constants.RGB][2];
+};
+
+p5.Color.prototype._getBrightness = function() {
+ if (!this.hsba) {
+ this.hsba = color_conversion._rgbaToHSBA(this._array);
+ }
+ return this.hsba[2] * this.maxes[constants.HSB][2];
+};
+
+p5.Color.prototype._getGreen = function() {
+ return this._array[1] * this.maxes[constants.RGB][1];
+};
+
+/**
+ * Hue is the same in HSB and HSL, but the maximum value may be different.
+ * This function will return the HSB-normalized saturation when supplied with
+ * an HSB color object, but will default to the HSL-normalized saturation
+ * otherwise.
+ */
+p5.Color.prototype._getHue = function() {
+ if (this.mode === constants.HSB) {
+ if (!this.hsba) {
+ this.hsba = color_conversion._rgbaToHSBA(this._array);
+ }
+ return this.hsba[0] * this.maxes[constants.HSB][0];
+ } else {
+ if (!this.hsla) {
+ this.hsla = color_conversion._rgbaToHSLA(this._array);
+ }
+ return this.hsla[0] * this.maxes[constants.HSL][0];
+ }
+};
+
+p5.Color.prototype._getLightness = function() {
+ if (!this.hsla) {
+ this.hsla = color_conversion._rgbaToHSLA(this._array);
+ }
+ return this.hsla[2] * this.maxes[constants.HSL][2];
+};
+
+p5.Color.prototype._getRed = function() {
+ return this._array[0] * this.maxes[constants.RGB][0];
+};
+
+/**
+ * Saturation is scaled differently in HSB and HSL. This function will return
+ * the HSB saturation when supplied with an HSB color object, but will default
+ * to the HSL saturation otherwise.
+ */
+p5.Color.prototype._getSaturation = function() {
+ if (this.mode === constants.HSB) {
+ if (!this.hsba) {
+ this.hsba = color_conversion._rgbaToHSBA(this._array);
+ }
+ return this.hsba[1] * this.maxes[constants.HSB][1];
+ } else {
+ if (!this.hsla) {
+ this.hsla = color_conversion._rgbaToHSLA(this._array);
+ }
+ return this.hsla[1] * this.maxes[constants.HSL][1];
+ }
+};
+
+/**
+ * CSS named colors.
+ */
+var namedColors = {
+ aliceblue: '#f0f8ff',
+ antiquewhite: '#faebd7',
+ aqua: '#00ffff',
+ aquamarine: '#7fffd4',
+ azure: '#f0ffff',
+ beige: '#f5f5dc',
+ bisque: '#ffe4c4',
+ black: '#000000',
+ blanchedalmond: '#ffebcd',
+ blue: '#0000ff',
+ blueviolet: '#8a2be2',
+ brown: '#a52a2a',
+ burlywood: '#deb887',
+ cadetblue: '#5f9ea0',
+ chartreuse: '#7fff00',
+ chocolate: '#d2691e',
+ coral: '#ff7f50',
+ cornflowerblue: '#6495ed',
+ cornsilk: '#fff8dc',
+ crimson: '#dc143c',
+ cyan: '#00ffff',
+ darkblue: '#00008b',
+ darkcyan: '#008b8b',
+ darkgoldenrod: '#b8860b',
+ darkgray: '#a9a9a9',
+ darkgreen: '#006400',
+ darkgrey: '#a9a9a9',
+ darkkhaki: '#bdb76b',
+ darkmagenta: '#8b008b',
+ darkolivegreen: '#556b2f',
+ darkorange: '#ff8c00',
+ darkorchid: '#9932cc',
+ darkred: '#8b0000',
+ darksalmon: '#e9967a',
+ darkseagreen: '#8fbc8f',
+ darkslateblue: '#483d8b',
+ darkslategray: '#2f4f4f',
+ darkslategrey: '#2f4f4f',
+ darkturquoise: '#00ced1',
+ darkviolet: '#9400d3',
+ deeppink: '#ff1493',
+ deepskyblue: '#00bfff',
+ dimgray: '#696969',
+ dimgrey: '#696969',
+ dodgerblue: '#1e90ff',
+ firebrick: '#b22222',
+ floralwhite: '#fffaf0',
+ forestgreen: '#228b22',
+ fuchsia: '#ff00ff',
+ gainsboro: '#dcdcdc',
+ ghostwhite: '#f8f8ff',
+ gold: '#ffd700',
+ goldenrod: '#daa520',
+ gray: '#808080',
+ green: '#008000',
+ greenyellow: '#adff2f',
+ grey: '#808080',
+ honeydew: '#f0fff0',
+ hotpink: '#ff69b4',
+ indianred: '#cd5c5c',
+ indigo: '#4b0082',
+ ivory: '#fffff0',
+ khaki: '#f0e68c',
+ lavender: '#e6e6fa',
+ lavenderblush: '#fff0f5',
+ lawngreen: '#7cfc00',
+ lemonchiffon: '#fffacd',
+ lightblue: '#add8e6',
+ lightcoral: '#f08080',
+ lightcyan: '#e0ffff',
+ lightgoldenrodyellow: '#fafad2',
+ lightgray: '#d3d3d3',
+ lightgreen: '#90ee90',
+ lightgrey: '#d3d3d3',
+ lightpink: '#ffb6c1',
+ lightsalmon: '#ffa07a',
+ lightseagreen: '#20b2aa',
+ lightskyblue: '#87cefa',
+ lightslategray: '#778899',
+ lightslategrey: '#778899',
+ lightsteelblue: '#b0c4de',
+ lightyellow: '#ffffe0',
+ lime: '#00ff00',
+ limegreen: '#32cd32',
+ linen: '#faf0e6',
+ magenta: '#ff00ff',
+ maroon: '#800000',
+ mediumaquamarine: '#66cdaa',
+ mediumblue: '#0000cd',
+ mediumorchid: '#ba55d3',
+ mediumpurple: '#9370db',
+ mediumseagreen: '#3cb371',
+ mediumslateblue: '#7b68ee',
+ mediumspringgreen: '#00fa9a',
+ mediumturquoise: '#48d1cc',
+ mediumvioletred: '#c71585',
+ midnightblue: '#191970',
+ mintcream: '#f5fffa',
+ mistyrose: '#ffe4e1',
+ moccasin: '#ffe4b5',
+ navajowhite: '#ffdead',
+ navy: '#000080',
+ oldlace: '#fdf5e6',
+ olive: '#808000',
+ olivedrab: '#6b8e23',
+ orange: '#ffa500',
+ orangered: '#ff4500',
+ orchid: '#da70d6',
+ palegoldenrod: '#eee8aa',
+ palegreen: '#98fb98',
+ paleturquoise: '#afeeee',
+ palevioletred: '#db7093',
+ papayawhip: '#ffefd5',
+ peachpuff: '#ffdab9',
+ peru: '#cd853f',
+ pink: '#ffc0cb',
+ plum: '#dda0dd',
+ powderblue: '#b0e0e6',
+ purple: '#800080',
+ red: '#ff0000',
+ rosybrown: '#bc8f8f',
+ royalblue: '#4169e1',
+ saddlebrown: '#8b4513',
+ salmon: '#fa8072',
+ sandybrown: '#f4a460',
+ seagreen: '#2e8b57',
+ seashell: '#fff5ee',
+ sienna: '#a0522d',
+ silver: '#c0c0c0',
+ skyblue: '#87ceeb',
+ slateblue: '#6a5acd',
+ slategray: '#708090',
+ slategrey: '#708090',
+ snow: '#fffafa',
+ springgreen: '#00ff7f',
+ steelblue: '#4682b4',
+ tan: '#d2b48c',
+ teal: '#008080',
+ thistle: '#d8bfd8',
+ tomato: '#ff6347',
+ turquoise: '#40e0d0',
+ violet: '#ee82ee',
+ wheat: '#f5deb3',
+ white: '#ffffff',
+ whitesmoke: '#f5f5f5',
+ yellow: '#ffff00',
+ yellowgreen: '#9acd32'
+};
+
+/**
+ * These regular expressions are used to build up the patterns for matching
+ * viable CSS color strings: fragmenting the regexes in this way increases the
+ * legibility and comprehensibility of the code.
+ *
+ * Note that RGB values of .9 are not parsed by IE, but are supported here for
+ * color string consistency.
+ */
+var WHITESPACE = /\s*/; // Match zero or more whitespace characters.
+var INTEGER = /(\d{1,3})/; // Match integers: 79, 255, etc.
+var DECIMAL = /((?:\d+(?:\.\d+)?)|(?:\.\d+))/; // Match 129.6, 79, .9, etc.
+var PERCENT = new RegExp(DECIMAL.source + '%'); // Match 12.9%, 79%, .9%, etc.
+
+/**
+ * Full color string patterns. The capture groups are necessary.
+ */
+var colorPatterns = {
+ // Match colors in format #XXX, e.g. #416.
+ HEX3: /^#([a-f0-9])([a-f0-9])([a-f0-9])$/i,
+
+ // Match colors in format #XXXXXX, e.g. #b4d455.
+ HEX6: /^#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i,
+
+ // Match colors in format rgb(R, G, B), e.g. rgb(255, 0, 128).
+ RGB: new RegExp([
+ '^rgb\\(',
+ INTEGER.source,
+ ',',
+ INTEGER.source,
+ ',',
+ INTEGER.source,
+ '\\)$'
+ ].join(WHITESPACE.source), 'i'),
+
+ // Match colors in format rgb(R%, G%, B%), e.g. rgb(100%, 0%, 28.9%).
+ RGB_PERCENT: new RegExp([
+ '^rgb\\(',
+ PERCENT.source,
+ ',',
+ PERCENT.source,
+ ',',
+ PERCENT.source,
+ '\\)$'
+ ].join(WHITESPACE.source), 'i'),
+
+ // Match colors in format rgb(R, G, B, A), e.g. rgb(255, 0, 128, 0.25).
+ RGBA: new RegExp([
+ '^rgba\\(',
+ INTEGER.source,
+ ',',
+ INTEGER.source,
+ ',',
+ INTEGER.source,
+ ',',
+ DECIMAL.source,
+ '\\)$'
+ ].join(WHITESPACE.source), 'i'),
+
+ // Match colors in format rgb(R%, G%, B%, A), e.g. rgb(100%, 0%, 28.9%, 0.5).
+ RGBA_PERCENT: new RegExp([
+ '^rgba\\(',
+ PERCENT.source,
+ ',',
+ PERCENT.source,
+ ',',
+ PERCENT.source,
+ ',',
+ DECIMAL.source,
+ '\\)$'
+ ].join(WHITESPACE.source), 'i'),
+
+ // Match colors in format hsla(H, S%, L%), e.g. hsl(100, 40%, 28.9%).
+ HSL: new RegExp([
+ '^hsl\\(',
+ INTEGER.source,
+ ',',
+ PERCENT.source,
+ ',',
+ PERCENT.source,
+ '\\)$'
+ ].join(WHITESPACE.source), 'i'),
+
+ // Match colors in format hsla(H, S%, L%, A), e.g. hsla(100, 40%, 28.9%, 0.5).
+ HSLA: new RegExp([
+ '^hsla\\(',
+ INTEGER.source,
+ ',',
+ PERCENT.source,
+ ',',
+ PERCENT.source,
+ ',',
+ DECIMAL.source,
+ '\\)$'
+ ].join(WHITESPACE.source), 'i'),
+
+ // Match colors in format hsb(H, S%, B%), e.g. hsb(100, 40%, 28.9%).
+ HSB: new RegExp([
+ '^hsb\\(',
+ INTEGER.source,
+ ',',
+ PERCENT.source,
+ ',',
+ PERCENT.source,
+ '\\)$'
+ ].join(WHITESPACE.source), 'i'),
+
+ // Match colors in format hsba(H, S%, B%, A), e.g. hsba(100, 40%, 28.9%, 0.5).
+ HSBA: new RegExp([
+ '^hsba\\(',
+ INTEGER.source,
+ ',',
+ PERCENT.source,
+ ',',
+ PERCENT.source,
+ ',',
+ DECIMAL.source,
+ '\\)$'
+ ].join(WHITESPACE.source), 'i')
+};
+
+/**
+ * For a number of different inputs, returns a color formatted as [r, g, b, a]
+ * arrays, with each component normalized between 0 and 1.
+ *
+ * @param {Array-like} args An 'array-like' object that represents a list of
+ * arguments
+ * @return {Array} a color formatted as [r, g, b, a]
+ * Example:
+ * input ==> output
+ * g ==> [g, g, g, 255]
+ * g,a ==> [g, g, g, a]
+ * r, g, b ==> [r, g, b, 255]
+ * r, g, b, a ==> [r, g, b, a]
+ * [g] ==> [g, g, g, 255]
+ * [g, a] ==> [g, g, g, a]
+ * [r, g, b] ==> [r, g, b, 255]
+ * [r, g, b, a] ==> [r, g, b, a]
+ * @example
+ * <div>
+ * <code>
+ * // todo
+ * </code>
+ * </div>
+ */
+p5.Color._parseInputs = function() {
+ var numArgs = arguments.length;
+ var mode = this._colorMode;
+ var maxes = this._colorMaxes;
+ var results = [];
+
+ if (numArgs >= 3) { // Argument is a list of component values.
+
+ results[0] = arguments[0] / maxes[mode][0];
+ results[1] = arguments[1] / maxes[mode][1];
+ results[2] = arguments[2] / maxes[mode][2];
+
+ // Alpha may be undefined, so default it to 100%.
+ if (typeof arguments[3] === 'number') {
+ results[3] = arguments[3] / maxes[mode][3];
+ } else {
+ results[3] = 1;
+ }
+
+ // Constrain components to the range [0,1].
+ results = results.map(function(value) {
+ return Math.max(Math.min(value, 1), 0);
+ });
+
+ // Convert to RGBA and return.
+ if (mode === constants.HSL) {
+ return color_conversion._hslaToRGBA(results);
+ } else if (mode === constants.HSB) {
+ return color_conversion._hsbaToRGBA(results);
+ } else {
+ return results;
+ }
+
+ } else if (numArgs === 1 && typeof arguments[0] === 'string') {
+
+ var str = arguments[0].trim().toLowerCase();
+
+ // Return if string is a named colour.
+ if (namedColors[str]) {
+ return p5.Color._parseInputs.apply(this, [namedColors[str]]);
+ }
+
+ // Try RGBA pattern matching.
+ if (colorPatterns.HEX3.test(str)) { // #rgb
+ results = colorPatterns.HEX3.exec(str).slice(1).map(function(color) {
+ return parseInt(color + color, 16) / 255;
+ });
+ results[3] = 1;
+ return results;
+ } else if (colorPatterns.HEX6.test(str)) { // #rrggbb
+ results = colorPatterns.HEX6.exec(str).slice(1).map(function(color) {
+ return parseInt(color, 16) / 255;
+ });
+ results[3] = 1;
+ return results;
+ } else if (colorPatterns.RGB.test(str)) { // rgb(R,G,B)
+ results = colorPatterns.RGB.exec(str).slice(1).map(function(color) {
+ return color / 255;
+ });
+ results[3] = 1;
+ return results;
+ } else if (colorPatterns.RGB_PERCENT.test(str)) { // rgb(R%,G%,B%)
+ results = colorPatterns.RGB_PERCENT.exec(str).slice(1)
+ .map(function(color) {
+ return parseFloat(color) / 100;
+ });
+ results[3] = 1;
+ return results;
+ } else if (colorPatterns.RGBA.test(str)) { // rgba(R,G,B,A)
+ results = colorPatterns.RGBA.exec(str).slice(1)
+ .map(function(color, idx) {
+ if (idx === 3) {
+ return parseFloat(color);
+ }
+ return color / 255;
+ });
+ return results;
+ } else if (colorPatterns.RGBA_PERCENT.test(str)) { // rgba(R%,G%,B%,A%)
+ results = colorPatterns.RGBA_PERCENT.exec(str).slice(1)
+ .map(function(color, idx) {
+ if (idx === 3) {
+ return parseFloat(color);
+ }
+ return parseFloat(color) / 100;
+ });
+ return results;
+ }
+
+ // Try HSLA pattern matching.
+ if (colorPatterns.HSL.test(str)) { // hsl(H,S,L)
+ results = colorPatterns.HSL.exec(str).slice(1)
+ .map(function(color, idx) {
+ if (idx === 0) {
+ return parseInt(color, 10) / 360;
+ }
+ return parseInt(color, 10) / 100;
+ });
+ results[3] = 1;
+ } else if (colorPatterns.HSLA.test(str)) { // hsla(H,S,L,A)
+ results = colorPatterns.HSLA.exec(str).slice(1)
+ .map(function(color, idx) {
+ if (idx === 0) {
+ return parseInt(color, 10) / 360;
+ }
+ else if (idx === 3) {
+ return parseFloat(color);
+ }
+ return parseInt(color, 10) / 100;
+ });
+ }
+ if (results.length) {
+ return color_conversion._hslaToRGBA(results);
+ }
+
+ // Try HSBA pattern matching.
+ if (colorPatterns.HSB.test(str)) { // hsb(H,S,B)
+ results = colorPatterns.HSB.exec(str).slice(1)
+ .map(function(color, idx) {
+ if (idx === 0) {
+ return parseInt(color, 10) / 360;
+ }
+ return parseInt(color, 10) / 100;
+ });
+ results[3] = 1;
+ } else if (colorPatterns.HSBA.test(str)) { // hsba(H,S,B,A)
+ results = colorPatterns.HSBA.exec(str).slice(1)
+ .map(function(color, idx) {
+ if (idx === 0) {
+ return parseInt(color, 10) / 360;
+ }
+ else if (idx === 3) {
+ return parseFloat(color);
+ }
+ return parseInt(color, 10) / 100;
+ });
+ }
+ if (results.length) {
+ return color_conversion._hsbaToRGBA(results);
+ }
+
+ // Input did not match any CSS color pattern: default to white.
+ results = [1, 1, 1, 1];
+
+ } else if ((numArgs === 1 || numArgs === 2) &&
+ typeof arguments[0] === 'number') { // 'Grayscale' mode.
+
+ /**
+ * For HSB and HSL, interpret the gray level as a brightness/lightness
+ * value (they are equivalent when chroma is zero). For RGB, normalize the
+ * gray level according to the blue maximum.
+ */
+ results[0] = arguments[0] / maxes[mode][2];
+ results[1] = arguments[0] / maxes[mode][2];
+ results[2] = arguments[0] / maxes[mode][2];
+
+ // Alpha may be undefined, so default it to 100%.
+ if (typeof arguments[1] === 'number') {
+ results[3] = arguments[1] / maxes[mode][3];
+ } else {
+ results[3] = 1;
+ }
+
+ // Constrain components to the range [0,1].
+ results = results.map(function(value) {
+ return Math.max(Math.min(value, 1), 0);
+ });
+
+ } else {
+ throw new Error (arguments + 'is not a valid color representation.');
+ }
+
+ return results;
+};
+
+module.exports = p5.Color;
+
+},{"../core/constants":47,"../core/core":48,"./color_conversion":40}],43:[function(_dereq_,module,exports){
+/**
+ * @module Color
+ * @submodule Setting
+ * @for p5
+ * @requires core
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var constants = _dereq_('../core/constants');
+_dereq_('./p5.Color');
+
+/**
+ * The background() function sets the color used for the background of the
+ * p5.js canvas. The default background is light gray. This function is
+ * typically used within draw() to clear the display window at the beginning
+ * of each frame, but it can be used inside setup() to set the background on
+ * the first frame of animation or if the background need only be set once.
+ *
+ * @method background
+ * @param {Number|String|p5.Color|p5.Image} v1 gray value, red or hue value
+ * (depending on the current
+ * color mode), color string,
+ * p5.Color, or p5.Image
+ * @param {Number} [v2] green or saturation value
+ * (depending on the current
+ * color mode)
+ * @param {Number} [v3] blue or brightness value
+ * (depending on the current
+ * color mode)
+ * @param {Number} [a] opacity of the background
+ *
+ * @example
+ * <div>
+ * <code>
+ * // Grayscale integer value
+ * background(51);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // R, G & B integer values
+ * background(255, 204, 0);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // H, S & B integer values
+ * colorMode(HSB);
+ * background(255, 204, 100);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // Named SVG/CSS color string
+ * background('red');
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // three-digit hexadecimal RGB notation
+ * background('#fae');
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // six-digit hexadecimal RGB notation
+ * background('#222222');
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // integer RGB notation
+ * background('rgb(0,255,0)');
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // integer RGBA notation
+ * background('rgba(0,255,0, 0.25)');
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // percentage RGB notation
+ * background('rgb(100%,0%,10%)');
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // percentage RGBA notation
+ * background('rgba(100%,0%,100%,0.5)');
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // p5 Color object
+ * background(color(0, 0, 255));
+ * </code>
+ * </div>
+ */
+p5.prototype.background = function() {
+ if (arguments[0] instanceof p5.Image) {
+ this.image(arguments[0], 0, 0, this.width, this.height);
+ } else {
+ this._renderer.background.apply(this._renderer, arguments);
+ }
+ return this;
+};
+
+/**
+ * Clears the pixels within a buffer. This function only works on p5.Canvas
+ * objects created with the createCanvas() function; it won't work with the
+ * main display window. Unlike the main graphics context, pixels in
+ * additional graphics areas created with createGraphics() can be entirely
+ * or partially transparent. This function clears everything to make all of
+ * the pixels 100% transparent.
+ *
+ * @method clear
+ * @example
+ * <div>
+ * <code>
+ * // Clear the screen on mouse press.
+ * function setup() {
+ * createCanvas(100, 100);
+ * }
+ *
+ * function draw() {
+ * ellipse(mouseX, mouseY, 20, 20);
+ * }
+ *
+ * function mousePressed() {
+ * clear();
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.clear = function() {
+ this._renderer.clear();
+ return this;
+};
+
+/**
+ * colorMode() changes the way p5.js interprets color data. By default, the
+ * parameters for fill(), stroke(), background(), and color() are defined by
+ * values between 0 and 255 using the RGB color model. This is equivalent to
+ * setting colorMode(RGB, 255). Setting colorMode(HSB) lets you use the HSB
+ * system instead. By default, this is colorMode(HSB, 360, 100, 100, 1). You
+ * can also use HSL.
+ * <br><br>
+ * Note: existing color objects remember the mode that they were created in,
+ * so you can change modes as you like without affecting their appearance.
+ *
+ * @method colorMode
+ * @param {Number|Constant} mode either RGB or HSB, corresponding to
+ * Red/Green/Blue and Hue/Saturation/Brightness
+ * (or Lightness)
+ * @param {Number|Constant} [max1] range for the red or hue depending on the
+ * current color mode, or range for all values
+ * @param {Number|Constant} [max2] range for the green or saturation depending
+ * on the current color mode
+ * @param {Number|Constant} [max3] range for the blue or brightness/lighntess
+ * depending on the current color mode
+ * @param {Number|Constant} [maxA] range for the alpha
+ * @example
+ * <div>
+ * <code>
+ * noStroke();
+ * colorMode(RGB, 100);
+ * for (i = 0; i < 100; i++) {
+ * for (j = 0; j < 100; j++) {
+ * stroke(i, j, 0);
+ * point(i, j);
+ * }
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * noStroke();
+ * colorMode(HSB, 100);
+ * for (i = 0; i < 100; i++) {
+ * for (j = 0; j < 100; j++) {
+ * stroke(i, j, 100);
+ * point(i, j);
+ * }
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * colorMode(RGB, 255);
+ * var c = color(127, 255, 0);
+ *
+ * colorMode(RGB, 1);
+ * var myColor = c._getRed();
+ * text(myColor, 10, 10, 80, 80);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * noFill();
+ * colorMode(RGB, 255, 255, 255, 1);
+ * background(255);
+ *
+ * strokeWeight(4);
+ * stroke(255, 0 , 10, 0.3);
+ * ellipse(40, 40, 50, 50);
+ * ellipse(50, 50, 40, 40);
+ * </code>
+ * </div>
+ */
+p5.prototype.colorMode = function() {
+ if (arguments[0] === constants.RGB ||
+ arguments[0] === constants.HSB ||
+ arguments[0] === constants.HSL) {
+
+ // Set color mode.
+ this._renderer._colorMode = arguments[0];
+
+ // Set color maxes.
+ var maxes = this._renderer._colorMaxes[this._renderer._colorMode];
+ if (arguments.length === 2) {
+ maxes[0] = arguments[1]; // Red
+ maxes[1] = arguments[1]; // Green
+ maxes[2] = arguments[1]; // Blue
+ maxes[3] = arguments[1]; // Alpha
+ } else if (arguments.length === 4) {
+ maxes[0] = arguments[1]; // Red
+ maxes[1] = arguments[2]; // Green
+ maxes[2] = arguments[3]; // Blue
+ } else if (arguments.length === 5) {
+ maxes[0] = arguments[1]; // Red
+ maxes[1] = arguments[2]; // Green
+ maxes[2] = arguments[3]; // Blue
+ maxes[3] = arguments[4]; // Alpha
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Sets the color used to fill shapes. For example, if you run
+ * fill(204, 102, 0), all subsequent shapes will be filled with orange. This
+ * color is either specified in terms of the RGB or HSB color depending on
+ * the current colorMode(). (The default color space is RGB, with each value
+ * in the range from 0 to 255).
+ * <br><br>
+ * If a single string argument is provided, RGB, RGBA and Hex CSS color strings
+ * and all named color strings are supported. A p5 Color object can also be
+ * provided to set the fill color.
+ *
+ * @method fill
+ * @param {Number|Array|String|p5.Color} v1 gray value, red or hue value
+ * (depending on the current color
+ * mode), or color Array, or CSS
+ * color string
+ * @param {Number} [v2] green or saturation value
+ * (depending on the current
+ * color mode)
+ * @param {Number} [v3] blue or brightness value
+ * (depending on the current
+ * color mode)
+ * @param {Number} [a] opacity of the background
+ *
+ * @example
+ * <div>
+ * <code>
+ * // Grayscale integer value
+ * fill(51);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // R, G & B integer values
+ * fill(255, 204, 0);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // H, S & B integer values
+ * colorMode(HSB);
+ * fill(255, 204, 100);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // Named SVG/CSS color string
+ * fill('red');
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // three-digit hexadecimal RGB notation
+ * fill('#fae');
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // six-digit hexadecimal RGB notation
+ * fill('#222222');
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // integer RGB notation
+ * fill('rgb(0,255,0)');
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // integer RGBA notation
+ * fill('rgba(0,255,0, 0.25)');
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // percentage RGB notation
+ * fill('rgb(100%,0%,10%)');
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // percentage RGBA notation
+ * fill('rgba(100%,0%,100%,0.5)');
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // p5 Color object
+ * fill(color(0, 0, 255));
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ */
+p5.prototype.fill = function() {
+ this._renderer._setProperty('_fillSet', true);
+ this._renderer._setProperty('_doFill', true);
+ this._renderer.fill.apply(this._renderer, arguments);
+ return this;
+};
+
+/**
+ * Disables filling geometry. If both noStroke() and noFill() are called,
+ * nothing will be drawn to the screen.
+ *
+ * @method noFill
+ * @example
+ * <div>
+ * <code>
+ * rect(15, 10, 55, 55);
+ * noFill();
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ */
+p5.prototype.noFill = function() {
+ this._renderer._setProperty('_doFill', false);
+ return this;
+};
+
+/**
+ * Disables drawing the stroke (outline). If both noStroke() and noFill()
+ * are called, nothing will be drawn to the screen.
+ *
+ * @method noStroke
+ * @example
+ * <div>
+ * <code>
+ * noStroke();
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ */
+p5.prototype.noStroke = function() {
+ this._renderer._setProperty('_doStroke', false);
+ return this;
+};
+
+/**
+ * Sets the color used to draw lines and borders around shapes. This color
+ * is either specified in terms of the RGB or HSB color depending on the
+ * current colorMode() (the default color space is RGB, with each value in
+ * the range from 0 to 255).
+ * <br><br>
+ * If a single string argument is provided, RGB, RGBA and Hex CSS color
+ * strings and all named color strings are supported. A p5 Color object
+ * can also be provided to set the stroke color.
+ *
+ * @method stroke
+ * @param {Number|Array|String|p5.Color} v1 gray value, red or hue value
+ * (depending on the current color
+ * mode), or color Array, or CSS
+ * color string
+ * @param {Number} [v2] green or saturation value
+ * (depending on the current
+ * color mode)
+ * @param {Number} [v3] blue or brightness value
+ * (depending on the current
+ * color mode)
+ * @param {Number} [a] opacity of the background
+ *
+ * @example
+ * <div>
+ * <code>
+ * // Grayscale integer value
+ * strokeWeight(4);
+ * stroke(51);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // R, G & B integer values
+ * stroke(255, 204, 0);
+ * strokeWeight(4);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // H, S & B integer values
+ * colorMode(HSB);
+ * strokeWeight(4);
+ * stroke(255, 204, 100);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // Named SVG/CSS color string
+ * stroke('red');
+ * strokeWeight(4);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // three-digit hexadecimal RGB notation
+ * stroke('#fae');
+ * strokeWeight(4);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // six-digit hexadecimal RGB notation
+ * stroke('#222222');
+ * strokeWeight(4);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // integer RGB notation
+ * stroke('rgb(0,255,0)');
+ * strokeWeight(4);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // integer RGBA notation
+ * stroke('rgba(0,255,0,0.25)');
+ * strokeWeight(4);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // percentage RGB notation
+ * stroke('rgb(100%,0%,10%)');
+ * strokeWeight(4);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // percentage RGBA notation
+ * stroke('rgba(100%,0%,100%,0.5)');
+ * strokeWeight(4);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // p5 Color object
+ * stroke(color(0, 0, 255));
+ * strokeWeight(4);
+ * rect(20, 20, 60, 60);
+ * </code>
+ * </div>
+ */
+p5.prototype.stroke = function() {
+ this._renderer._setProperty('_strokeSet', true);
+ this._renderer._setProperty('_doStroke', true);
+ this._renderer.stroke.apply(this._renderer, arguments);
+ return this;
+};
+
+module.exports = p5;
+
+},{"../core/constants":47,"../core/core":48,"./p5.Color":42}],44:[function(_dereq_,module,exports){
+/**
+ * @module Shape
+ * @submodule 2D Primitives
+ * @for p5
+ * @requires core
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('./core');
+var constants = _dereq_('./constants');
+
+_dereq_('./error_helpers');
+
+/**
+ * Draw an arc to the screen. If called with only a, b, c, d, start, and
+ * stop, the arc will be drawn as an open pie. If mode is provided, the arc
+ * will be drawn either open, as a chord, or as a pie as specified. The
+ * origin may be changed with the ellipseMode() function.<br><br>
+ * Note that drawing a full circle (ex: 0 to TWO_PI) will appear blank
+ * because 0 and TWO_PI are the same position on the unit circle. The
+ * best way to handle this is by using the ellipse() function instead
+ * to create a closed ellipse, and to use the arc() function
+ * only to draw parts of an ellipse.
+ *
+ * @method arc
+ * @param {Number} a x-coordinate of the arc's ellipse
+ * @param {Number} b y-coordinate of the arc's ellipse
+ * @param {Number} c width of the arc's ellipse by default
+ * @param {Number} d height of the arc's ellipse by default
+ * @param {Number} start angle to start the arc, specified in radians
+ * @param {Number} stop angle to stop the arc, specified in radians
+ * @param {String} [mode] optional parameter to determine the way of drawing
+ * the arc
+ * @return {Object} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * arc(50, 55, 50, 50, 0, HALF_PI);
+ * noFill();
+ * arc(50, 55, 60, 60, HALF_PI, PI);
+ * arc(50, 55, 70, 70, PI, PI+QUARTER_PI);
+ * arc(50, 55, 80, 80, PI+QUARTER_PI, TWO_PI);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * arc(50, 50, 80, 80, 0, PI+QUARTER_PI, OPEN);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * arc(50, 50, 80, 80, 0, PI+QUARTER_PI, CHORD);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * arc(50, 50, 80, 80, 0, PI+QUARTER_PI, PIE);
+ * </code>
+ * </div>
+ */
+p5.prototype.arc = function(x, y, w, h, start, stop, mode) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ this._validateParameters(
+ 'arc',
+ args,
+ [
+ ['Number', 'Number', 'Number', 'Number', 'Number', 'Number'],
+ [ 'Number', 'Number', 'Number', 'Number',
+ 'Number', 'Number', 'String' ]
+ ]
+ );
+
+ if (!this._renderer._doStroke && !this._renderer._doFill) {
+ return this;
+ }
+ if (this._angleMode === constants.DEGREES) {
+ start = this.radians(start);
+ stop = this.radians(stop);
+ }
+
+ // Make all angles positive...
+ while (start < 0) {
+ start += constants.TWO_PI;
+ }
+ while (stop < 0) {
+ stop += constants.TWO_PI;
+ }
+ // ...and confine them to the interval [0,TWO_PI).
+ start %= constants.TWO_PI;
+ stop %= constants.TWO_PI;
+
+ // Adjust angles to counter linear scaling.
+ if (start <= constants.HALF_PI) {
+ start = Math.atan(w / h * Math.tan(start));
+ } else if (start > constants.HALF_PI && start <= 3 * constants.HALF_PI) {
+ start = Math.atan(w / h * Math.tan(start)) + constants.PI;
+ } else {
+ start = Math.atan(w / h * Math.tan(start)) + constants.TWO_PI;
+ }
+ if (stop <= constants.HALF_PI) {
+ stop = Math.atan(w / h * Math.tan(stop));
+ } else if (stop > constants.HALF_PI && stop <= 3 * constants.HALF_PI) {
+ stop = Math.atan(w / h * Math.tan(stop)) + constants.PI;
+ } else {
+ stop = Math.atan(w / h * Math.tan(stop)) + constants.TWO_PI;
+ }
+
+ // Exceed the interval if necessary in order to preserve the size and
+ // orientation of the arc.
+ if (start > stop) {
+ stop += constants.TWO_PI;
+ }
+ // p5 supports negative width and heights for ellipses
+ w = Math.abs(w);
+ h = Math.abs(h);
+ this._renderer.arc(x, y, w, h, start, stop, mode);
+ return this;
+};
+
+/**
+ * Draws an ellipse (oval) to the screen. An ellipse with equal width and
+ * height is a circle. By default, the first two parameters set the location,
+ * and the third and fourth parameters set the shape's width and height. The
+ * origin may be changed with the ellipseMode() function.
+ *
+ * @method ellipse
+ * @param {Number} a x-coordinate of the ellipse.
+ * @param {Number} b y-coordinate of the ellipse.
+ * @param {Number} c width of the ellipse.
+ * @param {Number} d height of the ellipse.
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * ellipse(56, 46, 55, 55);
+ * </code>
+ * </div>
+ */
+p5.prototype.ellipse = function(x, y, w, h) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ this._validateParameters(
+ 'ellipse',
+ args,
+ ['Number', 'Number', 'Number', 'Number']
+ );
+
+ if (!this._renderer._doStroke && !this._renderer._doFill) {
+ return this;
+ }
+ // p5 supports negative width and heights for ellipses
+ w = Math.abs(w);
+ h = Math.abs(h);
+ //@TODO add catch block here if this._renderer
+ //doesn't have the method implemented yet
+ this._renderer.ellipse(x, y, w, h);
+ return this;
+};
+/**
+ * Draws a line (a direct path between two points) to the screen. The version
+ * of line() with four parameters draws the line in 2D. To color a line, use
+ * the stroke() function. A line cannot be filled, therefore the fill()
+ * function will not affect the color of a line. 2D lines are drawn with a
+ * width of one pixel by default, but this can be changed with the
+ * strokeWeight() function.
+ *
+ * @method line
+ * @param {Number} x1 the x-coordinate of the first point
+ * @param {Number} y1 the y-coordinate of the first point
+ * @param {Number} x2 the x-coordinate of the second point
+ * @param {Number} y2 the y-coordinate of the second point
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * line(30, 20, 85, 75);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * line(30, 20, 85, 20);
+ * stroke(126);
+ * line(85, 20, 85, 75);
+ * stroke(255);
+ * line(85, 75, 30, 75);
+ * </code>
+ * </div>
+ */
+////commented out original
+// p5.prototype.line = function(x1, y1, x2, y2) {
+// if (!this._renderer._doStroke) {
+// return this;
+// }
+// if(this._renderer.isP3D){
+// } else {
+// this._renderer.line(x1, y1, x2, y2);
+// }
+// };
+p5.prototype.line = function() {
+ if (!this._renderer._doStroke) {
+ return this;
+ }
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ //check whether we should draw a 3d line or 2d
+ if(this._renderer.isP3D){
+ this._validateParameters(
+ 'line',
+ args,
+ [
+ ['Number', 'Number', 'Number', 'Number', 'Number', 'Number']
+ ]
+ );
+ this._renderer.line(
+ args[0],
+ args[1],
+ args[2],
+ args[3],
+ args[4],
+ args[5]);
+ } else {
+ this._validateParameters(
+ 'line',
+ args,
+ [
+ ['Number', 'Number', 'Number', 'Number'],
+ ]
+ );
+ this._renderer.line(
+ args[0],
+ args[1],
+ args[2],
+ args[3]);
+ }
+ return this;
+};
+
+/**
+ * Draws a point, a coordinate in space at the dimension of one pixel.
+ * The first parameter is the horizontal value for the point, the second
+ * value is the vertical value for the point. The color of the point is
+ * determined by the current stroke.
+ *
+ * @method point
+ * @param {Number} x the x-coordinate
+ * @param {Number} y the y-coordinate
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * point(30, 20);
+ * point(85, 20);
+ * point(85, 75);
+ * point(30, 75);
+ * </code>
+ * </div>
+ */
+p5.prototype.point = function() {
+ if (!this._renderer._doStroke) {
+ return this;
+ }
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ //check whether we should draw a 3d line or 2d
+ if(this._renderer.isP3D){
+ this._validateParameters(
+ 'point',
+ args,
+ [
+ ['Number', 'Number', 'Number']
+ ]
+ );
+ this._renderer.point(
+ args[0],
+ args[1],
+ args[2]
+ );
+ } else {
+ this._validateParameters(
+ 'point',
+ args,
+ [
+ ['Number', 'Number']
+ ]
+ );
+ this._renderer.point(
+ args[0],
+ args[1]
+ );
+ }
+ return this;
+};
+
+
+/**
+ * Draw a quad. A quad is a quadrilateral, a four sided polygon. It is
+ * similar to a rectangle, but the angles between its edges are not
+ * constrained to ninety degrees. The first pair of parameters (x1,y1)
+ * sets the first vertex and the subsequent pairs should proceed
+ * clockwise or counter-clockwise around the defined shape.
+ *
+ * @method quad
+ * @param {Number} x1 the x-coordinate of the first point
+ * @param {Number} y1 the y-coordinate of the first point
+ * @param {Number} x2 the x-coordinate of the second point
+ * @param {Number} y2 the y-coordinate of the second point
+ * @param {Number} x3 the x-coordinate of the third point
+ * @param {Number} y3 the y-coordinate of the third point
+ * @param {Number} x4 the x-coordinate of the fourth point
+ * @param {Number} y4 the y-coordinate of the fourth point
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * quad(38, 31, 86, 20, 69, 63, 30, 76);
+ * </code>
+ * </div>
+ */
+p5.prototype.quad = function() {
+ if (!this._renderer._doStroke && !this._renderer._doFill) {
+ return this;
+ }
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ if(this._renderer.isP3D){
+ this._validateParameters(
+ 'quad',
+ args,
+ [
+ [ 'Number', 'Number', 'Number',
+ 'Number', 'Number', 'Number',
+ 'Number', 'Number', 'Number',
+ 'Number', 'Number', 'Number']
+ ]
+ );
+ this._renderer.quad(
+ args[0],
+ args[1],
+ args[2],
+ args[3],
+ args[4],
+ args[5],
+ args[6],
+ args[7],
+ args[8],
+ args[9],
+ args[10],
+ args[11]
+ );
+ } else {
+ this._validateParameters(
+ 'quad',
+ args,
+ [
+ [ 'Number', 'Number', 'Number', 'Number',
+ 'Number', 'Number', 'Number', 'Number' ]
+ ]
+ );
+ this._renderer.quad(
+ args[0],
+ args[1],
+ args[2],
+ args[3],
+ args[4],
+ args[5],
+ args[6],
+ args[7]
+ );
+ }
+ return this;
+};
+
+/**
+* Draws a rectangle to the screen. A rectangle is a four-sided shape with
+* every angle at ninety degrees. By default, the first two parameters set
+* the location of the upper-left corner, the third sets the width, and the
+* fourth sets the height. The way these parameters are interpreted, however,
+* may be changed with the rectMode() function.
+* <br><br>
+* The fifth, sixth, seventh and eighth parameters, if specified,
+* determine corner radius for the top-right, top-left, lower-right and
+* lower-left corners, respectively. An omitted corner radius parameter is set
+* to the value of the previously specified radius value in the parameter list.
+*
+* @method rect
+* @param {Number} x x-coordinate of the rectangle.
+* @param {Number} y y-coordinate of the rectangle.
+* @param {Number} w width of the rectangle.
+* @param {Number} h height of the rectangle.
+* @param {Number} [tl] optional radius of top-left corner.
+* @param {Number} [tr] optional radius of top-right corner.
+* @param {Number} [br] optional radius of bottom-right corner.
+* @param {Number} [bl] optional radius of bottom-left corner.
+* @return {p5} the p5 object.
+* @example
+* <div>
+* <code>
+* // Draw a rectangle at location (30, 20) with a width and height of 55.
+* rect(30, 20, 55, 55);
+* </code>
+* </div>
+*
+* <div>
+* <code>
+* // Draw a rectangle with rounded corners, each having a radius of 20.
+* rect(30, 20, 55, 55, 20);
+* </code>
+* </div>
+*
+* <div>
+* <code>
+* // Draw a rectangle with rounded corners having the following radii:
+* // top-left = 20, top-right = 15, bottom-right = 10, bottom-left = 5.
+* rect(30, 20, 55, 55, 20, 15, 10, 5)
+* </code>
+* </div>
+*/
+p5.prototype.rect = function (x, y, w, h, tl, tr, br, bl) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ this._validateParameters(
+ 'rect',
+ args,
+ [
+ ['Number', 'Number', 'Number', 'Number'],
+ ['Number', 'Number', 'Number', 'Number', 'Number'],
+ ['Number', 'Number', 'Number', 'Number',
+ 'Number', 'Number', 'Number', 'Number']
+ ]
+ );
+
+ if (!this._renderer._doStroke && !this._renderer._doFill) {
+ return;
+ }
+ this._renderer.rect(x, y, w, h, tl, tr, br, bl);
+ return this;
+};
+
+/**
+* A triangle is a plane created by connecting three points. The first two
+* arguments specify the first point, the middle two arguments specify the
+* second point, and the last two arguments specify the third point.
+*
+* @method triangle
+* @param {Number} x1 x-coordinate of the first point
+* @param {Number} y1 y-coordinate of the first point
+* @param {Number} x2 x-coordinate of the second point
+* @param {Number} y2 y-coordinate of the second point
+* @param {Number} x3 x-coordinate of the third point
+* @param {Number} y3 y-coordinate of the third point
+* @return {p5} the p5 object
+* @example
+* <div>
+* <code>
+* triangle(30, 75, 58, 20, 86, 75);
+* </code>
+* </div>
+*/
+p5.prototype.triangle = function() {
+
+ if (!this._renderer._doStroke && !this._renderer._doFill) {
+ return this;
+ }
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ if(this._renderer.isP3D){
+ this._validateParameters(
+ 'triangle',
+ args,
+ [
+ ['Number', 'Number', 'Number', 'Number', 'Number', 'Number',
+ 'Number', 'Number', 'Number']
+ ]
+ );
+ this._renderer.triangle(
+ args[0],
+ args[1],
+ args[2],
+ args[3],
+ args[4],
+ args[5],
+ args[6],
+ args[7],
+ args[8]
+ );
+ } else {
+ this._validateParameters(
+ 'triangle',
+ args,
+ [
+ ['Number', 'Number', 'Number', 'Number', 'Number', 'Number']
+ ]
+ );
+ this._renderer.triangle(
+ args[0],
+ args[1],
+ args[2],
+ args[3],
+ args[4],
+ args[5]
+ );
+ }
+ return this;
+};
+
+module.exports = p5;
+
+},{"./constants":47,"./core":48,"./error_helpers":51}],45:[function(_dereq_,module,exports){
+/**
+ * @module Shape
+ * @submodule Attributes
+ * @for p5
+ * @requires core
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('./core');
+var constants = _dereq_('./constants');
+
+/**
+ * Modifies the location from which ellipses are drawn by changing the way
+ * in which parameters given to ellipse() are interpreted.
+ * <br><br>
+ * The default mode is ellipseMode(CENTER), which interprets the first two
+ * parameters of ellipse() as the shape's center point, while the third and
+ * fourth parameters are its width and height.
+ * <br><br>
+ * ellipseMode(RADIUS) also uses the first two parameters of ellipse() as
+ * the shape's center point, but uses the third and fourth parameters to
+ * specify half of the shapes's width and height.
+ * <br><br>
+ * ellipseMode(CORNER) interprets the first two parameters of ellipse() as
+ * the upper-left corner of the shape, while the third and fourth parameters
+ * are its width and height.
+ * <br><br>
+ * ellipseMode(CORNERS) interprets the first two parameters of ellipse() as
+ * the location of one corner of the ellipse's bounding box, and the third
+ * and fourth parameters as the location of the opposite corner.
+ * <br><br>
+ * The parameter must be written in ALL CAPS because Javascript is a
+ * case-sensitive language.
+ *
+ * @method ellipseMode
+ * @param {Number/Constant} mode either CENTER, RADIUS, CORNER, or CORNERS
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * ellipseMode(RADIUS); // Set ellipseMode to RADIUS
+ * fill(255); // Set fill to white
+ * ellipse(50, 50, 30, 30); // Draw white ellipse using RADIUS mode
+ *
+ * ellipseMode(CENTER); // Set ellipseMode to CENTER
+ * fill(100); // Set fill to gray
+ * ellipse(50, 50, 30, 30); // Draw gray ellipse using CENTER mode
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * ellipseMode(CORNER); // Set ellipseMode is CORNER
+ * fill(255); // Set fill to white
+ * ellipse(25, 25, 50, 50); // Draw white ellipse using CORNER mode
+ *
+ * ellipseMode(CORNERS); // Set ellipseMode to CORNERS
+ * fill(100); // Set fill to gray
+ * ellipse(25, 25, 50, 50); // Draw gray ellipse using CORNERS mode
+ * </code>
+ * </div>
+ */
+p5.prototype.ellipseMode = function(m) {
+ if (m === constants.CORNER ||
+ m === constants.CORNERS ||
+ m === constants.RADIUS ||
+ m === constants.CENTER) {
+ this._renderer._ellipseMode = m;
+ }
+ return this;
+};
+
+/**
+ * Draws all geometry with jagged (aliased) edges. Note that smooth() is
+ * active by default, so it is necessary to call noSmooth() to disable
+ * smoothing of geometry, images, and fonts.
+ *
+ * @method noSmooth
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * background(0);
+ * noStroke();
+ * smooth();
+ * ellipse(30, 48, 36, 36);
+ * noSmooth();
+ * ellipse(70, 48, 36, 36);
+ * </code>
+ * </div>
+ */
+p5.prototype.noSmooth = function() {
+ this._renderer.noSmooth();
+ return this;
+};
+
+/**
+ * Modifies the location from which rectangles are drawn by changing the way
+ * in which parameters given to rect() are interpreted.
+ * <br><br>
+ * The default mode is rectMode(CORNER), which interprets the first two
+ * parameters of rect() as the upper-left corner of the shape, while the
+ * third and fourth parameters are its width and height.
+ * <br><br>
+ * rectMode(CORNERS) interprets the first two parameters of rect() as the
+ * location of one corner, and the third and fourth parameters as the
+ * location of the opposite corner.
+ * <br><br>
+ * rectMode(CENTER) interprets the first two parameters of rect() as the
+ * shape's center point, while the third and fourth parameters are its
+ * width and height.
+ * <br><br>
+ * rectMode(RADIUS) also uses the first two parameters of rect() as the
+ * shape's center point, but uses the third and fourth parameters to specify
+ * half of the shapes's width and height.
+ * <br><br>
+ * The parameter must be written in ALL CAPS because Javascript is a
+ * case-sensitive language.
+ *
+ * @method rectMode
+ * @param {Number/Constant} mode either CORNER, CORNERS, CENTER, or RADIUS
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * rectMode(CORNER); // Default rectMode is CORNER
+ * fill(255); // Set fill to white
+ * rect(25, 25, 50, 50); // Draw white rect using CORNER mode
+ *
+ * rectMode(CORNERS); // Set rectMode to CORNERS
+ * fill(100); // Set fill to gray
+ * rect(25, 25, 50, 50); // Draw gray rect using CORNERS mode
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * rectMode(RADIUS); // Set rectMode to RADIUS
+ * fill(255); // Set fill to white
+ * rect(50, 50, 30, 30); // Draw white rect using RADIUS mode
+ *
+ * rectMode(CENTER); // Set rectMode to CENTER
+ * fill(100); // Set fill to gray
+ * rect(50, 50, 30, 30); // Draw gray rect using CENTER mode
+ * </code>
+ * </div>
+ */
+p5.prototype.rectMode = function(m) {
+ if (m === constants.CORNER ||
+ m === constants.CORNERS ||
+ m === constants.RADIUS ||
+ m === constants.CENTER) {
+ this._renderer._rectMode = m;
+ }
+ return this;
+};
+
+/**
+ * Draws all geometry with smooth (anti-aliased) edges. smooth() will also
+ * improve image quality of resized images. Note that smooth() is active by
+ * default; noSmooth() can be used to disable smoothing of geometry,
+ * images, and fonts.
+ *
+ * @method smooth
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * background(0);
+ * noStroke();
+ * smooth();
+ * ellipse(30, 48, 36, 36);
+ * noSmooth();
+ * ellipse(70, 48, 36, 36);
+ * </code>
+ * </div>
+ */
+p5.prototype.smooth = function() {
+ this._renderer.smooth();
+ return this;
+};
+
+/**
+ * Sets the style for rendering line endings. These ends are either squared,
+ * extended, or rounded, each of which specified with the corresponding
+ * parameters: SQUARE, PROJECT, and ROUND. The default cap is ROUND.
+ *
+ * @method strokeCap
+ * @param {Number/Constant} cap either SQUARE, PROJECT, or ROUND
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * strokeWeight(12.0);
+ * strokeCap(ROUND);
+ * line(20, 30, 80, 30);
+ * strokeCap(SQUARE);
+ * line(20, 50, 80, 50);
+ * strokeCap(PROJECT);
+ * line(20, 70, 80, 70);
+ * </code>
+ * </div>
+ */
+p5.prototype.strokeCap = function(cap) {
+ if (cap === constants.ROUND ||
+ cap === constants.SQUARE ||
+ cap === constants.PROJECT) {
+ this._renderer.strokeCap(cap);
+ }
+ return this;
+};
+
+/**
+ * Sets the style of the joints which connect line segments. These joints
+ * are either mitered, beveled, or rounded and specified with the
+ * corresponding parameters MITER, BEVEL, and ROUND. The default joint is
+ * MITER.
+ *
+ * @method strokeJoin
+ * @param {Number/Constant} join either MITER, BEVEL, ROUND
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ * strokeWeight(10.0);
+ * strokeJoin(MITER);
+ * beginShape();
+ * vertex(35, 20);
+ * vertex(65, 50);
+ * vertex(35, 80);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * noFill();
+ * strokeWeight(10.0);
+ * strokeJoin(BEVEL);
+ * beginShape();
+ * vertex(35, 20);
+ * vertex(65, 50);
+ * vertex(35, 80);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * noFill();
+ * strokeWeight(10.0);
+ * strokeJoin(ROUND);
+ * beginShape();
+ * vertex(35, 20);
+ * vertex(65, 50);
+ * vertex(35, 80);
+ * endShape();
+ * </code>
+ * </div>
+ */
+p5.prototype.strokeJoin = function(join) {
+ if (join === constants.ROUND ||
+ join === constants.BEVEL ||
+ join === constants.MITER) {
+ this._renderer.strokeJoin(join);
+ }
+ return this;
+};
+
+/**
+ * Sets the width of the stroke used for lines, points, and the border
+ * around shapes. All widths are set in units of pixels.
+ *
+ * @method strokeWeight
+ * @param {Number} weight the weight (in pixels) of the stroke
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * strokeWeight(1); // Default
+ * line(20, 20, 80, 20);
+ * strokeWeight(4); // Thicker
+ * line(20, 40, 80, 40);
+ * strokeWeight(10); // Beastly
+ * line(20, 70, 80, 70);
+ * </code>
+ * </div>
+ */
+p5.prototype.strokeWeight = function(w) {
+ this._renderer.strokeWeight(w);
+ return this;
+};
+
+module.exports = p5;
+
+},{"./constants":47,"./core":48}],46:[function(_dereq_,module,exports){
+/**
+ * @requires constants
+ */
+
+var constants = _dereq_('./constants');
+
+module.exports = {
+
+ modeAdjust: function(a, b, c, d, mode) {
+ if (mode === constants.CORNER) {
+ return { x: a, y: b, w: c, h: d };
+ } else if (mode === constants.CORNERS) {
+ return { x: a, y: b, w: c-a, h: d-b };
+ } else if (mode === constants.RADIUS) {
+ return { x: a-c, y: b-d, w: 2*c, h: 2*d };
+ } else if (mode === constants.CENTER) {
+ return { x: a-c*0.5, y: b-d*0.5, w: c, h: d };
+ }
+ },
+
+ arcModeAdjust: function(a, b, c, d, mode) {
+ if (mode === constants.CORNER) {
+ return { x: a+c*0.5, y: b+d*0.5, w: c, h: d };
+ } else if (mode === constants.CORNERS) {
+ return { x: a, y: b, w: c+a, h: d+b };
+ } else if (mode === constants.RADIUS) {
+ return { x: a, y: b, w: 2*c, h: 2*d };
+ } else if (mode === constants.CENTER) {
+ return { x: a, y: b, w: c, h: d };
+ }
+ }
+
+};
+
+
+},{"./constants":47}],47:[function(_dereq_,module,exports){
+/**
+ * @module Constants
+ * @submodule Constants
+ * @for p5
+ */
+
+var PI = Math.PI;
+
+module.exports = {
+
+ // GRAPHICS RENDERER
+ P2D: 'p2d',
+ WEBGL: 'webgl',
+
+ // ENVIRONMENT
+ ARROW: 'default',
+ CROSS: 'crosshair',
+ HAND: 'pointer',
+ MOVE: 'move',
+ TEXT: 'text',
+ WAIT: 'wait',
+
+ // TRIGONOMETRY
+
+ /**
+ * HALF_PI is a mathematical constant with the value
+ * 1.57079632679489661923. It is half the ratio of the
+ * circumference of a circle to its diameter. It is useful in
+ * combination with the trigonometric functions sin() and cos().
+ *
+ * @property HALF_PI
+ *
+ * @example
+ * <div><code>
+ * arc(50, 50, 80, 80, 0, HALF_PI);
+ * </code></div>
+ *
+ */
+ HALF_PI: PI / 2,
+ /**
+ * PI is a mathematical constant with the value
+ * 3.14159265358979323846. It is the ratio of the circumference
+ * of a circle to its diameter. It is useful in combination with
+ * the trigonometric functions sin() and cos().
+ *
+ * @property PI
+ *
+ * @example
+ * <div><code>
+ * arc(50, 50, 80, 80, 0, PI);
+ * </code></div>
+ */
+ PI: PI,
+ /**
+ * QUARTER_PI is a mathematical constant with the value 0.7853982.
+ * It is one quarter the ratio of the circumference of a circle to
+ * its diameter. It is useful in combination with the trigonometric
+ * functions sin() and cos().
+ *
+ * @property QUARTER_PI
+ *
+ * @example
+ * <div><code>
+ * arc(50, 50, 80, 80, 0, QUARTER_PI);
+ * </code></div>
+ *
+ */
+ QUARTER_PI: PI / 4,
+ /**
+ * TAU is an alias for TWO_PI, a mathematical constant with the
+ * value 6.28318530717958647693. It is twice the ratio of the
+ * circumference of a circle to its diameter. It is useful in
+ * combination with the trigonometric functions sin() and cos().
+ *
+ * @property TAU
+ *
+ * @example
+ * <div><code>
+ * arc(50, 50, 80, 80, 0, TAU);
+ * </code></div>
+ *
+ */
+ TAU: PI * 2,
+ /**
+ * TWO_PI is a mathematical constant with the value
+ * 6.28318530717958647693. It is twice the ratio of the
+ * circumference of a circle to its diameter. It is useful in
+ * combination with the trigonometric functions sin() and cos().
+ *
+ * @property TWO_PI
+ *
+ * @example
+ * <div><code>
+ * arc(50, 50, 80, 80, 0, TWO_PI);
+ * </code></div>
+ *
+ */
+ TWO_PI: PI * 2,
+ DEGREES: 'degrees',
+ RADIANS: 'radians',
+
+ // SHAPE
+ CORNER: 'corner',
+ CORNERS: 'corners',
+ RADIUS: 'radius',
+ RIGHT: 'right',
+ LEFT: 'left',
+ CENTER: 'center',
+ TOP: 'top',
+ BOTTOM: 'bottom',
+ BASELINE: 'alphabetic',
+ POINTS: 'points',
+ LINES: 'lines',
+ TRIANGLES: 'triangles',
+ TRIANGLE_FAN: 'triangles_fan',
+ TRIANGLE_STRIP: 'triangles_strip',
+ QUADS: 'quads',
+ QUAD_STRIP: 'quad_strip',
+ CLOSE: 'close',
+ OPEN: 'open',
+ CHORD: 'chord',
+ PIE: 'pie',
+ PROJECT: 'square', // PEND: careful this is counterintuitive
+ SQUARE: 'butt',
+ ROUND: 'round',
+ BEVEL: 'bevel',
+ MITER: 'miter',
+
+ // COLOR
+ RGB: 'rgb',
+ HSB: 'hsb',
+ HSL: 'hsl',
+
+ // DOM EXTENSION
+ AUTO: 'auto',
+
+ // INPUT
+ ALT: 18,
+ BACKSPACE: 8,
+ CONTROL: 17,
+ DELETE: 46,
+ DOWN_ARROW: 40,
+ ENTER: 13,
+ ESCAPE: 27,
+ LEFT_ARROW: 37,
+ OPTION: 18,
+ RETURN: 13,
+ RIGHT_ARROW: 39,
+ SHIFT: 16,
+ TAB: 9,
+ UP_ARROW: 38,
+
+ // RENDERING
+ BLEND: 'normal',
+ ADD: 'lighter',
+ //ADD: 'add', //
+ //SUBTRACT: 'subtract', //
+ DARKEST: 'darken',
+ LIGHTEST: 'lighten',
+ DIFFERENCE: 'difference',
+ EXCLUSION: 'exclusion',
+ MULTIPLY: 'multiply',
+ SCREEN: 'screen',
+ REPLACE: 'source-over',
+ OVERLAY: 'overlay',
+ HARD_LIGHT: 'hard-light',
+ SOFT_LIGHT: 'soft-light',
+ DODGE: 'color-dodge',
+ BURN: 'color-burn',
+
+ // FILTERS
+ THRESHOLD: 'threshold',
+ GRAY: 'gray',
+ OPAQUE: 'opaque',
+ INVERT: 'invert',
+ POSTERIZE: 'posterize',
+ DILATE: 'dilate',
+ ERODE: 'erode',
+ BLUR: 'blur',
+
+ // TYPOGRAPHY
+ NORMAL: 'normal',
+ ITALIC: 'italic',
+ BOLD: 'bold',
+
+ // TYPOGRAPHY-INTERNAL
+ _DEFAULT_TEXT_FILL: '#000000',
+ _DEFAULT_LEADMULT: 1.25,
+ _CTX_MIDDLE: 'middle',
+
+ // VERTICES
+ LINEAR: 'linear',
+ QUADRATIC: 'quadratic',
+ BEZIER: 'bezier',
+ CURVE: 'curve',
+
+ // DEFAULTS
+ _DEFAULT_STROKE: '#000000',
+ _DEFAULT_FILL: '#FFFFFF'
+
+};
+
+},{}],48:[function(_dereq_,module,exports){
+/**
+ * @module Structure
+ * @submodule Structure
+ * @for p5
+ * @requires constants
+ */
+
+'use strict';
+
+_dereq_('./shim');
+
+// Core needs the PVariables object
+var constants = _dereq_('./constants');
+
+/**
+ * This is the p5 instance constructor.
+ *
+ * A p5 instance holds all the properties and methods related to
+ * a p5 sketch. It expects an incoming sketch closure and it can also
+ * take an optional node parameter for attaching the generated p5 canvas
+ * to a node. The sketch closure takes the newly created p5 instance as
+ * its sole argument and may optionally set preload(), setup(), and/or
+ * draw() properties on it for running a sketch.
+ *
+ * A p5 sketch can run in "global" or "instance" mode:
+ * "global" - all properties and methods are attached to the window
+ * "instance" - all properties and methods are bound to this p5 object
+ *
+ * @param {Function} sketch a closure that can set optional preload(),
+ * setup(), and/or draw() properties on the
+ * given p5 instance
+ * @param {HTMLElement|boolean} node element to attach canvas to, if a
+ * boolean is passed in use it as sync
+ * @param {boolean} [sync] start synchronously (optional)
+ * @return {p5} a p5 instance
+ */
+var p5 = function(sketch, node, sync) {
+
+ if (arguments.length === 2 && typeof node === 'boolean') {
+ sync = node;
+ node = undefined;
+ }
+
+ //////////////////////////////////////////////
+ // PUBLIC p5 PROPERTIES AND METHODS
+ //////////////////////////////////////////////
+
+
+ /**
+ * Called directly before setup(), the preload() function is used to handle
+ * asynchronous loading of external files. If a preload function is
+ * defined, setup() will wait until any load calls within have finished.
+ * Nothing besides load calls should be inside preload (loadImage,
+ * loadJSON, loadFont, loadStrings, etc).
+ *
+ * @method preload
+ * @example
+ * <div><code>
+ * var img;
+ * var c;
+ * function preload() { // preload() runs once
+ * img = loadImage('assets/laDefense.jpg');
+ * }
+ *
+ * function setup() { // setup() waits until preload() is done
+ * img.loadPixels();
+ * // get color of middle pixel
+ * c = img.get(img.width/2, img.height/2);
+ * }
+ *
+ * function draw() {
+ * background(c);
+ * image(img, 25, 25, 50, 50);
+ * }
+ * </code></div>
+ */
+
+ /**
+ * The setup() function is called once when the program starts. It's used to
+ * define initial environment properties such as screen size and background
+ * color and to load media such as images and fonts as the program starts.
+ * There can only be one setup() function for each program and it shouldn't
+ * be called again after its initial execution.
+ * <br><br>
+ * Note: Variables declared within setup() are not accessible within other
+ * functions, including draw().
+ *
+ * @method setup
+ * @example
+ * <div><code>
+ * var a = 0;
+ *
+ * function setup() {
+ * background(0);
+ * noStroke();
+ * fill(102);
+ * }
+ *
+ * function draw() {
+ * rect(a++%width, 10, 2, 80);
+ * }
+ * </code></div>
+ */
+
+ /**
+ * Called directly after setup(), the draw() function continuously executes
+ * the lines of code contained inside its block until the program is stopped
+ * or noLoop() is called. draw() is called automatically and should never be
+ * called explicitly.
+ * <br><br>
+ * It should always be controlled with noLoop(), redraw() and loop(). After
+ * noLoop() stops the code in draw() from executing, redraw() causes the
+ * code inside draw() to execute once, and loop() will cause the code
+ * inside draw() to resume executing continuously.
+ * <br><br>
+ * The number of times draw() executes in each second may be controlled with
+ * the frameRate() function.
+ * <br><br>
+ * There can only be one draw() function for each sketch, and draw() must
+ * exist if you want the code to run continuously, or to process events such
+ * as mousePressed(). Sometimes, you might have an empty call to draw() in
+ * your program, as shown in the above example.
+ *
+ * @method draw
+ * @example
+ * <div><code>
+ * var yPos = 0;
+ * function setup() { // setup() runs once
+ * frameRate(30);
+ * }
+ * function draw() { // draw() loops forever, until stopped
+ * background(204);
+ * yPos = yPos - 1;
+ * if (yPos < 0) {
+ * yPos = height;
+ * }
+ * line(0, yPos, width, yPos);
+ * }
+ * </code></div>
+ */
+
+
+ //////////////////////////////////////////////
+ // PRIVATE p5 PROPERTIES AND METHODS
+ //////////////////////////////////////////////
+
+ this._setupDone = false;
+ // for handling hidpi
+ this._pixelDensity = Math.ceil(window.devicePixelRatio) || 1;
+ this._userNode = node;
+ this._curElement = null;
+ this._elements = [];
+ this._requestAnimId = 0;
+ this._preloadCount = 0;
+ this._isGlobal = false;
+ this._loop = true;
+ this._styles = [];
+ this._defaultCanvasSize = {
+ width: 100,
+ height: 100
+ };
+ this._events = { // keep track of user-events for unregistering later
+ 'mousemove': null,
+ 'mousedown': null,
+ 'mouseup': null,
+ 'dragend': null,
+ 'dragover': null,
+ 'click': null,
+ 'mouseover': null,
+ 'mouseout': null,
+ 'keydown': null,
+ 'keyup': null,
+ 'keypress': null,
+ 'touchstart': null,
+ 'touchmove': null,
+ 'touchend': null,
+ 'resize': null,
+ 'blur': null
+ };
+
+ if (window.DeviceOrientationEvent) {
+ this._events.deviceorientation = null;
+ }
+ if (window.DeviceMotionEvent && !window._isNodeWebkit) {
+ this._events.devicemotion = null;
+ }
+
+ this._events.wheel = null;
+
+
+ this._loadingScreenId = 'p5_loading';
+
+ this._start = function () {
+ // Find node if id given
+ if (this._userNode) {
+ if (typeof this._userNode === 'string') {
+ this._userNode = document.getElementById(this._userNode);
+ }
+ }
+
+ // Always create a default canvas.
+ // Later on if the user calls createCanvas, this default one
+ // will be replaced
+ this.createCanvas(
+ this._defaultCanvasSize.width,
+ this._defaultCanvasSize.height,
+ 'p2d',
+ true
+ );
+
+ var userPreload = this.preload || window.preload; // look for "preload"
+ if (userPreload) {
+
+ // Setup loading screen
+ // Set loading scfeen into dom if not present
+ // Otherwise displays and removes user provided loading screen
+ var loadingScreen = document.getElementById(this._loadingScreenId);
+ if(!loadingScreen){
+ loadingScreen = document.createElement('div');
+ loadingScreen.innerHTML = 'Loading...';
+ loadingScreen.style.position = 'absolute';
+ loadingScreen.id = this._loadingScreenId;
+ var node = this._userNode || document.body;
+ node.appendChild(loadingScreen);
+ }
+ // var methods = this._preloadMethods;
+ for (var method in this._preloadMethods){
+ // default to p5 if no object defined
+ this._preloadMethods[method] = this._preloadMethods[method] || p5;
+ var obj = this._preloadMethods[method];
+ //it's p5, check if it's global or instance
+ if (obj === p5.prototype || obj === p5){
+ obj = this._isGlobal ? window : this;
+ }
+ this._registeredPreloadMethods[method] = obj[method];
+ obj[method] = this._wrapPreload(obj, method);
+ }
+
+ userPreload();
+ this._runIfPreloadsAreDone();
+ } else {
+ this._setup();
+ this._runFrames();
+ this._draw();
+ }
+ }.bind(this);
+
+ this._runIfPreloadsAreDone = function(){
+ var context = this._isGlobal ? window : this;
+ if (context._preloadCount === 0) {
+ var loadingScreen = document.getElementById(context._loadingScreenId);
+ if (loadingScreen) {
+ loadingScreen.parentNode.removeChild(loadingScreen);
+ }
+ context._setup();
+ context._runFrames();
+ context._draw();
+ }
+ };
+
+ this._decrementPreload = function(){
+ var context = this._isGlobal ? window : this;
+ context._setProperty('_preloadCount', context._preloadCount - 1);
+ context._runIfPreloadsAreDone();
+ };
+
+ this._wrapPreload = function(obj, fnName){
+ return function(){
+ //increment counter
+ this._incrementPreload();
+ //call original function
+ var args = Array.prototype.slice.call(arguments);
+ args.push(this._decrementPreload.bind(this));
+ return this._registeredPreloadMethods[fnName].apply(obj, args);
+ }.bind(this);
+ };
+
+ this._incrementPreload = function(){
+ var context = this._isGlobal ? window : this;
+ context._setProperty('_preloadCount', context._preloadCount + 1);
+ };
+
+ this._setup = function() {
+
+ // return preload functions to their normal vals if switched by preload
+ var context = this._isGlobal ? window : this;
+ if (typeof context.preload === 'function') {
+ for (var f in this._preloadMethods) {
+ context[f] = this._preloadMethods[f][f];
+ if (context[f] && this) {
+ context[f] = context[f].bind(this);
+ }
+ }
+ }
+
+ // Short-circuit on this, in case someone used the library in "global"
+ // mode earlier
+ if (typeof context.setup === 'function') {
+ context.setup();
+ }
+
+ // // unhide hidden canvas that was created
+ // this.canvas.style.visibility = '';
+ // this.canvas.className = this.canvas.className.replace('p5_hidden', '');
+
+ // unhide any hidden canvases that were created
+ var reg = new RegExp(/(^|\s)p5_hidden(?!\S)/g);
+ var canvases = document.getElementsByClassName('p5_hidden');
+ for (var i = 0; i < canvases.length; i++) {
+ var k = canvases[i];
+ k.style.visibility = '';
+ k.className = k.className.replace(reg, '');
+ }
+ this._setupDone = true;
+
+ }.bind(this);
+
+ this._draw = function () {
+ var now = window.performance.now();
+ var time_since_last = now - this._lastFrameTime;
+ var target_time_between_frames = 1000 / this._targetFrameRate;
+
+ // only draw if we really need to; don't overextend the browser.
+ // draw if we're within 5ms of when our next frame should paint
+ // (this will prevent us from giving up opportunities to draw
+ // again when it's really about time for us to do so). fixes an
+ // issue where the frameRate is too low if our refresh loop isn't
+ // in sync with the browser. note that we have to draw once even
+ // if looping is off, so we bypass the time delay if that
+ // is the case.
+ var epsilon = 5;
+ if (!this._loop ||
+ time_since_last >= target_time_between_frames - epsilon) {
+
+ //mandatory update values(matrixs and stack) for 3d
+ if(this._renderer.isP3D){
+ this._renderer._update();
+ }
+
+ this._setProperty('frameCount', this.frameCount + 1);
+ this._updateMouseCoords();
+ this._updateTouchCoords();
+ this.redraw();
+ this._frameRate = 1000.0/(now - this._lastFrameTime);
+ this._lastFrameTime = now;
+ }
+
+ // get notified the next time the browser gives us
+ // an opportunity to draw.
+ if (this._loop) {
+ this._requestAnimId = window.requestAnimationFrame(this._draw);
+ }
+ }.bind(this);
+
+ this._runFrames = function() {
+ if (this._updateInterval) {
+ clearInterval(this._updateInterval);
+ }
+ }.bind(this);
+
+ this._setProperty = function(prop, value) {
+ this[prop] = value;
+ if (this._isGlobal) {
+ window[prop] = value;
+ }
+ }.bind(this);
+
+ /**
+ * Removes the entire p5 sketch. This will remove the canvas and any
+ * elements created by p5.js. It will also stop the draw loop and unbind
+ * any properties or methods from the window global scope. It will
+ * leave a variable p5 in case you wanted to create a new p5 sketch.
+ * If you like, you can set p5 = null to erase it.
+ * @method remove
+ * @example
+ * <div class='norender'><code>
+ * function draw() {
+ * ellipse(50, 50, 10, 10);
+ * }
+ *
+ * function mousePressed() {
+ * remove(); // remove whole sketch on mouse press
+ * }
+ * </code></div>
+ */
+ this.remove = function() {
+ if (this._curElement) {
+
+ // stop draw
+ this._loop = false;
+ if (this._requestAnimId) {
+ window.cancelAnimationFrame(this._requestAnimId);
+ }
+
+ // unregister events sketch-wide
+ for (var ev in this._events) {
+ window.removeEventListener(ev, this._events[ev]);
+ }
+
+ // remove DOM elements created by p5, and listeners
+ for (var i=0; i<this._elements.length; i++) {
+ var e = this._elements[i];
+ if (e.elt.parentNode) {
+ e.elt.parentNode.removeChild(e.elt);
+ }
+ for (var elt_ev in e._events) {
+ e.elt.removeEventListener(elt_ev, e._events[elt_ev]);
+ }
+ }
+
+ // call any registered remove functions
+ var self = this;
+ this._registeredMethods.remove.forEach(function (f) {
+ if (typeof(f) !== 'undefined') {
+ f.call(self);
+ }
+ });
+
+ // remove window bound properties and methods
+ if (this._isGlobal) {
+ for (var p in p5.prototype) {
+ try {
+ delete window[p];
+ } catch (x) {
+ window[p] = undefined;
+ }
+ }
+ for (var p2 in this) {
+ if (this.hasOwnProperty(p2)) {
+ try {
+ delete window[p2];
+ } catch (x) {
+ window[p2] = undefined;
+ }
+ }
+ }
+ }
+ }
+ // window.p5 = undefined;
+ }.bind(this);
+
+
+ // attach constants to p5 instance
+ for (var k in constants) {
+ p5.prototype[k] = constants[k];
+ }
+
+ // If the user has created a global setup or draw function,
+ // assume "global" mode and make everything global (i.e. on the window)
+ if (!sketch) {
+ this._isGlobal = true;
+ // Loop through methods on the prototype and attach them to the window
+ for (var p in p5.prototype) {
+ if(typeof p5.prototype[p] === 'function') {
+ var ev = p.substring(2);
+ if (!this._events.hasOwnProperty(ev)) {
+ window[p] = p5.prototype[p].bind(this);
+ }
+ } else {
+ window[p] = p5.prototype[p];
+ }
+ }
+ // Attach its properties to the window
+ for (var p2 in this) {
+ if (this.hasOwnProperty(p2)) {
+ window[p2] = this[p2];
+ }
+ }
+
+ } else {
+ // Else, the user has passed in a sketch closure that may set
+ // user-provided 'setup', 'draw', etc. properties on this instance of p5
+ sketch(this);
+ }
+
+ // Bind events to window (not using container div bc key events don't work)
+
+ for (var e in this._events) {
+ var f = this['_on'+e];
+ if (f) {
+ var m = f.bind(this);
+ window.addEventListener(e, m);
+ this._events[e] = m;
+ }
+ }
+
+ var focusHandler = function() {
+ this._setProperty('focused', true);
+ }.bind(this);
+ var blurHandler = function() {
+ this._setProperty('focused', false);
+ }.bind(this);
+ window.addEventListener('focus', focusHandler);
+ window.addEventListener('blur', blurHandler);
+ this.registerMethod('remove', function() {
+ window.removeEventListener('focus', focusHandler);
+ window.removeEventListener('blur', blurHandler);
+ });
+
+ // TODO: ???
+
+ if (sync) {
+ this._start();
+ } else {
+ if (document.readyState === 'complete') {
+ this._start();
+ } else {
+ window.addEventListener('load', this._start.bind(this), false);
+ }
+ }
+};
+
+
+// functions that cause preload to wait
+// more can be added by using registerPreloadMethod(func)
+p5.prototype._preloadMethods = {
+ loadJSON: p5.prototype,
+ loadImage: p5.prototype,
+ loadStrings: p5.prototype,
+ loadXML: p5.prototype,
+ loadShape: p5.prototype,
+ loadTable: p5.prototype,
+ loadFont: p5.prototype
+};
+
+p5.prototype._registeredMethods = { pre: [], post: [], remove: [] };
+
+p5.prototype._registeredPreloadMethods = {};
+
+p5.prototype.registerPreloadMethod = function(fnString, obj) {
+ // obj = obj || p5.prototype;
+ if (!p5.prototype._preloadMethods.hasOwnProperty(fnString)) {
+ p5.prototype._preloadMethods[fnString] = obj;
+ }
+};
+
+p5.prototype.registerMethod = function(name, m) {
+ if (!p5.prototype._registeredMethods.hasOwnProperty(name)) {
+ p5.prototype._registeredMethods[name] = [];
+ }
+ p5.prototype._registeredMethods[name].push(m);
+};
+
+module.exports = p5;
+
+},{"./constants":47,"./shim":57}],49:[function(_dereq_,module,exports){
+/**
+ * @module Shape
+ * @submodule Curves
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('./core');
+
+_dereq_('./error_helpers');
+
+var bezierDetail = 20;
+var curveDetail = 20;
+
+/**
+ * Draws a cubic Bezier curve on the screen. These curves are defined by a
+ * series of anchor and control points. The first two parameters specify
+ * the first anchor point and the last two parameters specify the other
+ * anchor point, which become the first and last points on the curve. The
+ * middle parameters specify the two control points which define the shape
+ * of the curve. Approximately speaking, control points "pull" the curve
+ * towards them.<br /><br />Bezier curves were developed by French
+ * automotive engineer Pierre Bezier, and are commonly used in computer
+ * graphics to define gently sloping curves. See also curve().
+ *
+ * @method bezier
+ * @param {Number} x1 x-coordinate for the first anchor point
+ * @param {Number} y1 y-coordinate for the first anchor point
+ * @param {Number} x2 x-coordinate for the first control point
+ * @param {Number} y2 y-coordinate for the first control point
+ * @param {Number} x3 x-coordinate for the second control point
+ * @param {Number} y3 y-coordinate for the second control point
+ * @param {Number} x4 x-coordinate for the second anchor point
+ * @param {Number} y4 y-coordinate for the second anchor point
+ * @return {Object} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ * stroke(255, 102, 0);
+ * line(85, 20, 10, 10);
+ * line(90, 90, 15, 80);
+ * stroke(0, 0, 0);
+ * bezier(85, 20, 10, 10, 90, 90, 15, 80);
+ * </code>
+ * </div>
+ */
+p5.prototype.bezier = function(x1, y1, x2, y2, x3, y3, x4, y4) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ this._validateParameters(
+ 'bezier',
+ args,
+ [ 'Number', 'Number', 'Number', 'Number',
+ 'Number', 'Number', 'Number', 'Number' ]
+ );
+
+ if (!this._renderer._doStroke) {
+ return this;
+ }
+ this._renderer.bezier(x1, y1, x2, y2, x3, y3, x4, y4);
+ return this;
+};
+
+/**
+ * Sets the resolution at which Beziers display.
+ *
+ * The default value is 20.
+ *
+ * @param {Number} detail resolution of the curves
+ * @return {Object} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * background(204);
+ * bezierDetail(50);
+ * bezier(85, 20, 10, 10, 90, 90, 15, 80);
+ * </code>
+ * </div>
+ */
+p5.prototype.bezierDetail = function(d) {
+ bezierDetail = d;
+ return this;
+};
+
+/**
+ * Evaluates the Bezier at position t for points a, b, c, d.
+ * The parameters a and d are the first and last points
+ * on the curve, and b and c are the control points.
+ * The final parameter t varies between 0 and 1.
+ * This can be done once with the x coordinates and a second time
+ * with the y coordinates to get the location of a bezier curve at t.
+ *
+ * @method bezierPoint
+ * @param {Number} a coordinate of first point on the curve
+ * @param {Number} b coordinate of first control point
+ * @param {Number} c coordinate of second control point
+ * @param {Number} d coordinate of second point on the curve
+ * @param {Number} t value between 0 and 1
+ * @return {Number} the value of the Bezier at position t
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ * x1 = 85, x2 = 10, x3 = 90, x4 = 15;
+ * y1 = 20, y2 = 10, y3 = 90, y4 = 80;
+ * bezier(x1, y1, x2, y2, x3, y3, x4, y4);
+ * fill(255);
+ * steps = 10;
+ * for (i = 0; i <= steps; i++) {
+ * t = i / steps;
+ * x = bezierPoint(x1, x2, x3, x4, t);
+ * y = bezierPoint(y1, y2, y3, y4, t);
+ * ellipse(x, y, 5, 5);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.bezierPoint = function(a, b, c, d, t) {
+ var adjustedT = 1-t;
+ return Math.pow(adjustedT,3)*a +
+ 3*(Math.pow(adjustedT,2))*t*b +
+ 3*adjustedT*Math.pow(t,2)*c +
+ Math.pow(t,3)*d;
+};
+
+/**
+ * Evaluates the tangent to the Bezier at position t for points a, b, c, d.
+ * The parameters a and d are the first and last points
+ * on the curve, and b and c are the control points.
+ * The final parameter t varies between 0 and 1.
+ *
+ * @method bezierTangent
+ * @param {Number} a coordinate of first point on the curve
+ * @param {Number} b coordinate of first control point
+ * @param {Number} c coordinate of second control point
+ * @param {Number} d coordinate of second point on the curve
+ * @param {Number} t value between 0 and 1
+ * @return {Number} the tangent at position t
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ * bezier(85, 20, 10, 10, 90, 90, 15, 80);
+ * steps = 6;
+ * fill(255);
+ * for (i = 0; i <= steps; i++) {
+ * t = i / steps;
+ * // Get the location of the point
+ * x = bezierPoint(85, 10, 90, 15, t);
+ * y = bezierPoint(20, 10, 90, 80, t);
+ * // Get the tangent points
+ * tx = bezierTangent(85, 10, 90, 15, t);
+ * ty = bezierTangent(20, 10, 90, 80, t);
+ * // Calculate an angle from the tangent points
+ * a = atan2(ty, tx);
+ * a += PI;
+ * stroke(255, 102, 0);
+ * line(x, y, cos(a)*30 + x, sin(a)*30 + y);
+ * // The following line of code makes a line
+ * // inverse of the above line
+ * //line(x, y, cos(a)*-30 + x, sin(a)*-30 + y);
+ * stroke(0);
+ * ellipse(x, y, 5, 5);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * noFill();
+ * bezier(85, 20, 10, 10, 90, 90, 15, 80);
+ * stroke(255, 102, 0);
+ * steps = 16;
+ * for (i = 0; i <= steps; i++) {
+ * t = i / steps;
+ * x = bezierPoint(85, 10, 90, 15, t);
+ * y = bezierPoint(20, 10, 90, 80, t);
+ * tx = bezierTangent(85, 10, 90, 15, t);
+ * ty = bezierTangent(20, 10, 90, 80, t);
+ * a = atan2(ty, tx);
+ * a -= HALF_PI;
+ * line(x, y, cos(a)*8 + x, sin(a)*8 + y);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.bezierTangent = function(a, b, c, d, t) {
+ var adjustedT = 1-t;
+ return 3*d*Math.pow(t,2) -
+ 3*c*Math.pow(t,2) +
+ 6*c*adjustedT*t -
+ 6*b*adjustedT*t +
+ 3*b*Math.pow(adjustedT,2) -
+ 3*a*Math.pow(adjustedT,2);
+};
+
+/**
+ * Draws a curved line on the screen between two points, given as the
+ * middle four parameters. The first two parameters are a control point, as
+ * if the curve came from this point even though it's not drawn. The last
+ * two parameters similarly describe the other control point. <br /><br />
+ * Longer curves can be created by putting a series of curve() functions
+ * together or using curveVertex(). An additional function called
+ * curveTightness() provides control for the visual quality of the curve.
+ * The curve() function is an implementation of Catmull-Rom splines.
+ *
+ * @method curve
+ * @param {Number} x1 x-coordinate for the beginning control point
+ * @param {Number} y1 y-coordinate for the beginning control point
+ * @param {Number} x2 x-coordinate for the first point
+ * @param {Number} y2 y-coordinate for the first point
+ * @param {Number} x3 x-coordinate for the second point
+ * @param {Number} y3 y-coordinate for the second point
+ * @param {Number} x4 x-coordinate for the ending control point
+ * @param {Number} y4 y-coordinate for the ending control point
+ * @return {Object} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ * stroke(255, 102, 0);
+ * curve(5, 26, 5, 26, 73, 24, 73, 61);
+ * stroke(0);
+ * curve(5, 26, 73, 24, 73, 61, 15, 65);
+ * stroke(255, 102, 0);
+ * curve(73, 24, 73, 61, 15, 65, 15, 65);
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * // Define the curve points as JavaScript objects
+ * p1 = {x: 5, y: 26}, p2 = {x: 73, y: 24}
+ * p3 = {x: 73, y: 61}, p4 = {x: 15, y: 65}
+ * noFill();
+ * stroke(255, 102, 0);
+ * curve(p1.x, p1.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y)
+ * stroke(0);
+ * curve(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y)
+ * stroke(255, 102, 0);
+ * curve(p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, p4.x, p4.y)
+ * </code>
+ * </div>
+ */
+p5.prototype.curve = function(x1, y1, x2, y2, x3, y3, x4, y4) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ this._validateParameters(
+ 'curve',
+ args,
+ [ 'Number', 'Number', 'Number', 'Number',
+ 'Number', 'Number', 'Number', 'Number' ]
+ );
+
+ if (!this._renderer._doStroke) {
+ return;
+ }
+ this._renderer.curve(x1, y1, x2, y2, x3, y3, x4, y4);
+ return this;
+};
+
+/**
+ * Sets the resolution at which curves display.
+ *
+ * The default value is 20.
+ *
+ * @param {Number} resolution of the curves
+ * @return {Object} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * background(204);
+ * curveDetail(20);
+ * curve(5, 26, 5, 26, 73, 24, 73, 61);
+ * </code>
+ * </div>
+ */
+p5.prototype.curveDetail = function(d) {
+ curveDetail = d;
+ return this;
+};
+
+/**
+ * Modifies the quality of forms created with curve() and curveVertex().
+ * The parameter tightness determines how the curve fits to the vertex
+ * points. The value 0.0 is the default value for tightness (this value
+ * defines the curves to be Catmull-Rom splines) and the value 1.0 connects
+ * all the points with straight lines. Values within the range -5.0 and 5.0
+ * will deform the curves but will leave them recognizable and as values
+ * increase in magnitude, they will continue to deform.
+ *
+ * @method curveTightness
+ * @param {Number} amount of deformation from the original vertices
+ * @return {Object} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * // Move the mouse left and right to see the curve change
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ * noFill();
+ * }
+ *
+ * function draw() {
+ * background(204);
+ * var t = map(mouseX, 0, width, -5, 5);
+ * curveTightness(t);
+ * beginShape();
+ * curveVertex(10, 26);
+ * curveVertex(10, 26);
+ * curveVertex(83, 24);
+ * curveVertex(83, 61);
+ * curveVertex(25, 65);
+ * curveVertex(25, 65);
+ * endShape();
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.curveTightness = function (t) {
+ this._renderer._curveTightness = t;
+};
+
+/**
+ * Evaluates the curve at position t for points a, b, c, d.
+ * The parameter t varies between 0 and 1, a and d are points
+ * on the curve, and b and c are the control points.
+ * This can be done once with the x coordinates and a second time
+ * with the y coordinates to get the location of a curve at t.
+ *
+ * @method curvePoint
+ * @param {Number} a coordinate of first point on the curve
+ * @param {Number} b coordinate of first control point
+ * @param {Number} c coordinate of second control point
+ * @param {Number} d coordinate of second point on the curve
+ * @param {Number} t value between 0 and 1
+ * @return {Number} bezier value at position t
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ * curve(5, 26, 5, 26, 73, 24, 73, 61);
+ * curve(5, 26, 73, 24, 73, 61, 15, 65);
+ * fill(255);
+ * ellipseMode(CENTER);
+ * steps = 6;
+ * for (i = 0; i <= steps; i++) {
+ * t = i / steps;
+ * x = curvePoint(5, 5, 73, 73, t);
+ * y = curvePoint(26, 26, 24, 61, t);
+ * ellipse(x, y, 5, 5);
+ * x = curvePoint(5, 73, 73, 15, t);
+ * y = curvePoint(26, 24, 61, 65, t);
+ * ellipse(x, y, 5, 5);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.curvePoint = function(a, b, c, d, t) {
+ var t3 = t*t*t,
+ t2 = t*t,
+ f1 = -0.5 * t3 + t2 - 0.5 * t,
+ f2 = 1.5 * t3 - 2.5 * t2 + 1.0,
+ f3 = -1.5 * t3 + 2.0 * t2 + 0.5 * t,
+ f4 = 0.5 * t3 - 0.5 * t2;
+ return a*f1 + b*f2 + c*f3 + d*f4;
+};
+
+/**
+ * Evaluates the tangent to the curve at position t for points a, b, c, d.
+ * The parameter t varies between 0 and 1, a and d are points on the curve,
+ * and b and c are the control points.
+ *
+ * @method curveTangent
+ * @param {Number} a coordinate of first point on the curve
+ * @param {Number} b coordinate of first control point
+ * @param {Number} c coordinate of second control point
+ * @param {Number} d coordinate of second point on the curve
+ * @param {Number} t value between 0 and 1
+ * @return {Number} the tangent at position t
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ * curve(5, 26, 73, 24, 73, 61, 15, 65);
+ * steps = 6;
+ * for (i = 0; i <= steps; i++) {
+ * t = i / steps;
+ * x = curvePoint(5, 73, 73, 15, t);
+ * y = curvePoint(26, 24, 61, 65, t);
+ * //ellipse(x, y, 5, 5);
+ * tx = curveTangent(5, 73, 73, 15, t);
+ * ty = curveTangent(26, 24, 61, 65, t);
+ * a = atan2(ty, tx);
+ * a -= PI/2.0;
+ * line(x, y, cos(a)*8 + x, sin(a)*8 + y);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.curveTangent = function(a, b,c, d, t) {
+ var t2 = t*t,
+ f1 = (-3*t2)/2 + 2*t - 0.5,
+ f2 = (9*t2)/2 - 5*t,
+ f3 = (-9*t2)/2 + 4*t + 0.5,
+ f4 = (3*t2)/2 - t;
+ return a*f1 + b*f2 + c*f3 + d*f4;
+};
+
+module.exports = p5;
+
+},{"./core":48,"./error_helpers":51}],50:[function(_dereq_,module,exports){
+/**
+ * @module Environment
+ * @submodule Environment
+ * @for p5
+ * @requires core
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('./core');
+var C = _dereq_('./constants');
+
+var standardCursors = [C.ARROW, C.CROSS, C.HAND, C.MOVE, C.TEXT, C.WAIT];
+
+p5.prototype._frameRate = 0;
+p5.prototype._lastFrameTime = window.performance.now();
+p5.prototype._targetFrameRate = 60;
+
+
+if (window.console && console.log) {
+ /**
+ * The print() function writes to the console area of your browser.
+ * This function is often helpful for looking at the data a program is
+ * producing. This function creates a new line of text for each call to
+ * the function. Individual elements can be
+ * separated with quotes ("") and joined with the addition operator (+).
+ * <br><br>
+ * While print() is similar to console.log(), it does not directly map to
+ * it in order to simulate easier to understand behavior than
+ * console.log(). Due to this, it is slower. For fastest results, use
+ * console.log().
+ *
+ * @method print
+ * @param {Any} contents any combination of Number, String, Object, Boolean,
+ * Array to print
+ * @example
+ * <div><code class='norender'>
+ * var x = 10;
+ * print("The value of x is " + x);
+ * // prints "The value of x is 10"
+ * </code></div>
+ */
+ // Converts passed args into a string and then parses that string to
+ // simulate synchronous behavior. This is a hack and is gross.
+ // Since this will not work on all objects, particularly circular
+ // structures, simply console.log() on error.
+ p5.prototype.print = function(args) {
+ try {
+ var newArgs = JSON.parse(JSON.stringify(args));
+ console.log(newArgs);
+ } catch(err) {
+ console.log(args);
+ }
+ };
+} else {
+ p5.prototype.print = function() {};
+}
+
+p5.prototype.println = p5.prototype.print;
+
+/**
+ * The system variable frameCount contains the number of frames that have
+ * been displayed since the program started. Inside setup() the value is 0,
+ * after the first iteration of draw it is 1, etc.
+ *
+ * @property frameCount
+ * @example
+ * <div><code>
+ * function setup() {
+ * frameRate(30);
+ * textSize(20);
+ * textSize(30);
+ * textAlign(CENTER);
+ * }
+ *
+ * function draw() {
+ * background(200);
+ * text(frameCount, width/2, height/2);
+ * }
+ * </code></div>
+ */
+p5.prototype.frameCount = 0;
+
+/**
+ * Confirms if the window a p5.js program is in is "focused," meaning that
+ * the sketch will accept mouse or keyboard input. This variable is
+ * "true" if the window is focused and "false" if not.
+ *
+ * @property focused
+ * @example
+ * <div><code>
+ * // To demonstrate, put two windows side by side.
+ * // Click on the window that the p5 sketch isn't in!
+ * function draw() {
+ * if (focused) { // or "if (focused === true)"
+ * noStroke();
+ * fill(0, 200, 0);
+ * ellipse(25, 25, 50, 50);
+ * } else {
+ * stroke(200,0,0);
+ * line(0, 0, 100, 100);
+ * line(100, 0, 0, 100);
+ * }
+ * }
+ *
+ * </code></div>
+ */
+p5.prototype.focused = (document.hasFocus());
+
+/**
+ * Sets the cursor to a predefined symbol or an image, or makes it visible
+ * if already hidden. If you are trying to set an image as the cursor, the
+ * recommended size is 16x16 or 32x32 pixels. It is not possible to load an
+ * image as the cursor if you are exporting your program for the Web, and not
+ * all MODES work with all browsers. The values for parameters x and y must
+ * be less than the dimensions of the image.
+ *
+ * @method cursor
+ * @param {Number/Constant} type either ARROW, CROSS, HAND, MOVE, TEXT, or
+ * WAIT, or path for image
+ * @param {Number} [x] the horizontal active spot of the cursor
+ * @param {Number} [y] the vertical active spot of the cursor
+ * @example
+ * <div><code>
+ * // Move the mouse left and right across the image
+ * // to see the cursor change from a cross to a hand
+ * function draw() {
+ * line(width/2, 0, width/2, height);
+ * if (mouseX < 50) {
+ * cursor(CROSS);
+ * } else {
+ * cursor(HAND);
+ * }
+ * }
+ * </code></div>
+ */
+p5.prototype.cursor = function(type, x, y) {
+ var cursor = 'auto';
+ var canvas = this._curElement.elt;
+ if (standardCursors.indexOf(type) > -1) {
+ // Standard css cursor
+ cursor = type;
+ } else if (typeof type === 'string') {
+ var coords = '';
+ if (x && y && (typeof x === 'number' && typeof y === 'number')) {
+ // Note that x and y values must be unit-less positive integers < 32
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/cursor
+ coords = x + ' ' + y;
+ }
+ if (type.substring(0, 6) !== 'http://') {
+ // Image (absolute url)
+ cursor = 'url(' + type + ') ' + coords + ', auto';
+ } else if (/\.(cur|jpg|jpeg|gif|png|CUR|JPG|JPEG|GIF|PNG)$/.test(type)) {
+ // Image file (relative path) - Separated for performance reasons
+ cursor = 'url(' + type + ') ' + coords + ', auto';
+ } else {
+ // Any valid string for the css cursor property
+ cursor = type;
+ }
+ }
+ canvas.style.cursor = cursor;
+};
+
+/**
+ * Specifies the number of frames to be displayed every second. For example,
+ * the function call frameRate(30) will attempt to refresh 30 times a second.
+ * If the processor is not fast enough to maintain the specified rate, the
+ * frame rate will not be achieved. Setting the frame rate within setup() is
+ * recommended. The default rate is 60 frames per second. This is the same as
+ * setFrameRate(val).
+ * <br><br>
+ * Calling frameRate() with no arguments returns the current framerate. This
+ * is the same as getFrameRate().
+ * <br><br>
+ * Calling frameRate() with arguments that are not of the type numbers
+ * or are non positive also returns current framerate.
+ *
+ * @method frameRate
+ * @param {Number} [fps] number of frames to be displayed every second
+ * @return {Number} current frameRate
+ * @example
+ *
+ * <div><code>
+ * var rectX = 0;
+ * var fr = 30; //starting FPS
+ * var clr;
+ *
+ * function setup() {
+ * background(200);
+ * frameRate(fr); // Attempt to refresh at starting FPS
+ * clr = color(255,0,0);
+ * }
+ *
+ * function draw() {
+ * background(200);
+ * rectX = rectX += 1; // Move Rectangle
+ *
+ * if (rectX >= width) { // If you go off screen.
+ * if (fr == 30) {
+ * clr = color(0,0,255);
+ * fr = 10;
+ * frameRate(fr); // make frameRate 10 FPS
+ * } else {
+ * clr = color(255,0,0);
+ * fr = 30;
+ * frameRate(fr); // make frameRate 30 FPS
+ * }
+ * rectX = 0;
+ * }
+ * fill(clr);
+ * rect(rectX, 40, 20,20);
+ * }
+ * </div></code>
+ *
+ */
+p5.prototype.frameRate = function(fps) {
+ if (typeof fps !== 'number' || fps <= 0) {
+ return this._frameRate;
+ } else {
+ this._setProperty('_targetFrameRate', fps);
+ this._runFrames();
+ return this;
+ }
+};
+/**
+ * Returns the current framerate.
+ *
+ * @return {Number} current frameRate
+ */
+p5.prototype.getFrameRate = function() {
+ return this.frameRate();
+};
+
+/**
+ * Specifies the number of frames to be displayed every second. For example,
+ * the function call frameRate(30) will attempt to refresh 30 times a second.
+ * If the processor is not fast enough to maintain the specified rate, the
+ * frame rate will not be achieved. Setting the frame rate within setup() is
+ * recommended. The default rate is 60 frames per second.
+ *
+ * Calling frameRate() with no arguments returns the current framerate.
+ *
+ * @param {Number} [fps] number of frames to be displayed every second
+ */
+p5.prototype.setFrameRate = function(fps) {
+ return this.frameRate(fps);
+};
+
+/**
+ * Hides the cursor from view.
+ *
+ * @method noCursor
+ * @example
+ * <div><code>
+ * function setup() {
+ * noCursor();
+ * }
+ *
+ * function draw() {
+ * background(200);
+ * ellipse(mouseX, mouseY, 10, 10);
+ * }
+ * </code></div>
+ */
+p5.prototype.noCursor = function() {
+ this._curElement.elt.style.cursor = 'none';
+};
+
+
+/**
+ * System variable that stores the width of the entire screen display. This
+ * is used to run a full-screen program on any display size.
+ *
+ * @property displayWidth
+ * @example
+ * <div class="norender"><code>
+ * createCanvas(displayWidth, displayHeight);
+ * </code></div>
+ */
+p5.prototype.displayWidth = screen.width;
+
+/**
+ * System variable that stores the height of the entire screen display. This
+ * is used to run a full-screen program on any display size.
+ *
+ * @property displayHeight
+ * @example
+ * <div class="norender"><code>
+ * createCanvas(displayWidth, displayHeight);
+ * </code></div>
+ */
+p5.prototype.displayHeight = screen.height;
+
+/**
+ * System variable that stores the width of the inner window, it maps to
+ * window.innerWidth.
+ *
+ * @property windowWidth
+ * @example
+ * <div class="norender"><code>
+ * createCanvas(windowWidth, windowHeight);
+ * </code></div>
+ */
+p5.prototype.windowWidth = window.innerWidth;
+/**
+ * System variable that stores the height of the inner window, it maps to
+ * window.innerHeight.
+ *
+ * @property windowHeight
+ * @example
+ * <div class="norender"><code>
+ * createCanvas(windowWidth, windowHeight);
+ * </code></div>
+ */
+p5.prototype.windowHeight = window.innerHeight;
+
+/**
+ * The windowResized() function is called once every time the browser window
+ * is resized. This is a good place to resize the canvas or do any other
+ * adjustements to accomodate the new window size.
+ *
+ * @method windowResized
+ * @example
+ * <div class="norender"><code>
+ * function setup() {
+ * createCanvas(windowWidth, windowHeight);
+ * }
+ *
+ * function draw() {
+ * background(0, 100, 200);
+ * }
+ *
+ * function windowResized() {
+ * resizeCanvas(windowWidth, windowHeight);
+ * }
+ * </code></div>
+ */
+p5.prototype._onresize = function(e){
+ this._setProperty('windowWidth', window.innerWidth);
+ this._setProperty('windowHeight', window.innerHeight);
+ var context = this._isGlobal ? window : this;
+ var executeDefault;
+ if (typeof context.windowResized === 'function') {
+ executeDefault = context.windowResized(e);
+ if (executeDefault !== undefined && !executeDefault) {
+ e.preventDefault();
+ }
+ }
+};
+
+/**
+ * System variable that stores the width of the drawing canvas. This value
+ * is set by the first parameter of the createCanvas() function.
+ * For example, the function call createCanvas(320, 240) sets the width
+ * variable to the value 320. The value of width defaults to 100 if
+ * createCanvas() is not used in a program.
+ *
+ * @property width
+ */
+p5.prototype.width = 0;
+
+/**
+ * System variable that stores the height of the drawing canvas. This value
+ * is set by the second parameter of the createCanvas() function. For
+ * example, the function call createCanvas(320, 240) sets the height
+ * variable to the value 240. The value of height defaults to 100 if
+ * createCanvas() is not used in a program.
+ *
+ * @property height
+ */
+p5.prototype.height = 0;
+
+/**
+ * If argument is given, sets the sketch to fullscreen or not based on the
+ * value of the argument. If no argument is given, returns the current
+ * fullscreen state. Note that due to browser restrictions this can only
+ * be called on user input, for example, on mouse press like the example
+ * below.
+ *
+ * @method fullscreen
+ * @param {Boolean} [val] whether the sketch should be in fullscreen mode
+ * or not
+ * @return {Boolean} current fullscreen state
+ * @example
+ * <div>
+ * <code>
+ * // Clicking in the box toggles fullscreen on and off.
+ * function setup() {
+ * background(200);
+ * }
+ * function mousePressed() {
+ * if (mouseX > 0 && mouseX < 100 && mouseY > 0 && mouseY < 100) {
+ * var fs = fullscreen();
+ * fullscreen(!fs);
+ * }
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.fullscreen = function(val) {
+ // no arguments, return fullscreen or not
+ if (typeof val === 'undefined') {
+ return document.fullscreenElement ||
+ document.webkitFullscreenElement ||
+ document.mozFullScreenElement ||
+ document.msFullscreenElement;
+ } else { // otherwise set to fullscreen or not
+ if (val) {
+ launchFullscreen(document.documentElement);
+ } else {
+ exitFullscreen();
+ }
+ }
+};
+
+/**
+ * Sets the pixel scaling for high pixel density displays. By default
+ * pixel density is set to match display density, call pixelDensity(1)
+ * to turn this off. Calling pixelDensity() with no arguments returns
+ * the current pixel density of the sketch.
+ *
+ *
+ * @method pixelDensity
+ * @param {Number} [val] whether or how much the sketch should scale
+ * @returns {Number} current pixel density of the sketch
+ * @example
+ * <div>
+ * <code>
+ * function setup() {
+ * pixelDensity(1);
+ * createCanvas(100, 100);
+ * background(200);
+ * ellipse(width/2, height/2, 50, 50);
+ * }
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * function setup() {
+ * pixelDensity(3.0);
+ * createCanvas(100, 100);
+ * background(200);
+ * ellipse(width/2, height/2, 50, 50);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.pixelDensity = function(val) {
+ if (typeof val === 'number') {
+ this._pixelDensity = val;
+ } else {
+ return this._pixelDensity;
+ }
+ this.resizeCanvas(this.width, this.height, true);
+};
+
+/**
+ * Returns the pixel density of the current display the sketch is running on.
+ *
+ * @method displayDensity
+ * @returns {Number} current pixel density of the display
+ * @example
+ * <div>
+ * <code>
+ * function setup() {
+ * var density = displayDensity();
+ * pixelDensity(density);
+ * createCanvas(100, 100);
+ * background(200);
+ * ellipse(width/2, height/2, 50, 50);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.displayDensity = function() {
+ return window.devicePixelRatio;
+};
+
+function launchFullscreen(element) {
+ var enabled = document.fullscreenEnabled ||
+ document.webkitFullscreenEnabled ||
+ document.mozFullScreenEnabled ||
+ document.msFullscreenEnabled;
+ if (!enabled) {
+ throw new Error('Fullscreen not enabled in this browser.');
+ }
+ if(element.requestFullscreen) {
+ element.requestFullscreen();
+ } else if(element.mozRequestFullScreen) {
+ element.mozRequestFullScreen();
+ } else if(element.webkitRequestFullscreen) {
+ element.webkitRequestFullscreen();
+ } else if(element.msRequestFullscreen) {
+ element.msRequestFullscreen();
+ }
+}
+
+function exitFullscreen() {
+ if(document.exitFullscreen) {
+ document.exitFullscreen();
+ } else if(document.mozCancelFullScreen) {
+ document.mozCancelFullScreen();
+ } else if(document.webkitExitFullscreen) {
+ document.webkitExitFullscreen();
+ } else if (document.msExitFullscreen) {
+ document.msExitFullscreen();
+ }
+}
+
+
+/**
+ * Gets the current URL.
+ * @method getURL
+ * @return {String} url
+ * @example
+ * <div>
+ * <code>
+ * var url;
+ * var x = 100;
+ *
+ * function setup() {
+ * fill(0);
+ * noStroke();
+ * url = getURL();
+ * }
+ *
+ * function draw() {
+ * background(200);
+ * text(url, x, height/2);
+ * x--;
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.getURL = function() {
+ return location.href;
+};
+/**
+ * Gets the current URL path as an array.
+ * @method getURLPath
+ * @return {Array} path components
+ * @example
+ * <div class='norender'><code>
+ * function setup() {
+ * var urlPath = getURLPath();
+ * for (var i=0; i&lt;urlPath.length; i++) {
+ * text(urlPath[i], 10, i*20+20);
+ * }
+ * }
+ * </code></div>
+ */
+p5.prototype.getURLPath = function() {
+ return location.pathname.split('/').filter(function(v){return v!=='';});
+};
+/**
+ * Gets the current URL params as an Object.
+ * @method getURLParams
+ * @return {Object} URL params
+ * @example
+ * <div class='norender'>
+ * <code>
+ * // Example: http://p5js.org?year=2014&month=May&day=15
+ *
+ * function setup() {
+ * var params = getURLParams();
+ * text(params.day, 10, 20);
+ * text(params.month, 10, 40);
+ * text(params.year, 10, 60);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.getURLParams = function() {
+ var re = /[?&]([^&=]+)(?:[&=])([^&=]+)/gim;
+ var m;
+ var v={};
+ while ((m = re.exec(location.search)) != null) {
+ if (m.index === re.lastIndex) {
+ re.lastIndex++;
+ }
+ v[m[1]]=m[2];
+ }
+ return v;
+};
+
+module.exports = p5;
+
+},{"./constants":47,"./core":48}],51:[function(_dereq_,module,exports){
+/**
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('./core');
+var doFriendlyWelcome = false; // TEMP until we get it all working LM
+
+// -- Borrowed from jQuery 1.11.3 --
+var class2type = {};
+var toString = class2type.toString;
+var names = ['Boolean', 'Number', 'String', 'Function',
+ 'Array', 'Date', 'RegExp', 'Object', 'Error'];
+for (var n=0; n<names.length; n++) {
+ class2type[ '[object ' + names[n] + ']' ] = names[n].toLowerCase();
+}
+var getType = function( obj ) {
+ if ( obj == null ) {
+ return obj + '';
+ }
+ return typeof obj === 'object' || typeof obj === 'function' ?
+ class2type[ toString.call(obj) ] || 'object' :
+ typeof obj;
+};
+var isArray = Array.isArray || function( obj ) {
+ return getType(obj) === 'array';
+};
+var isNumeric =function( obj ) {
+ // parseFloat NaNs numeric-cast false positives (null|true|false|"")
+ // ...but misinterprets leading-number strings, particularly hex literals
+ // subtraction forces infinities to NaN
+ // adding 1 corrects loss of precision from parseFloat (#15100)
+ return !isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0;
+};
+// -- End borrow --
+
+/**
+ * Checks the definition type against the argument type
+ * If any of these passes (in order), it matches:
+ *
+ * - p5.* definitions are checked with instanceof
+ * - Booleans are let through (everything is truthy or falsey)
+ * - Lowercase of the definition is checked against the js type
+ * - Number types are checked to see if they are numerically castable
+ */
+var numberTypes = ['Number', 'Integer', 'Number/Constant'];
+function typeMatches(defType, argType, arg) {
+ if(defType.match(/^p5\./)) {
+ var parts = defType.split('.');
+ return arg instanceof p5[parts[1]];
+ }
+ return defType === 'Boolean' || // Anything is truthy, cover in Debug Guide
+ (defType.toLowerCase() === argType) ||
+ (numberTypes.indexOf(defType) > -1 && isNumeric(arg));
+}
+
+/**
+ * Prints out a fancy, colorful message to the console log
+ *
+ * @param {String} message the words to be said
+ * @param {String} func the name of the function to link
+ * @param {Integer/Color String} color CSS color string or error type
+ *
+ * @return console logs
+ */
+// Wrong number of params, undefined param, wrong type
+var PARAM_COUNT = 0;
+var EMPTY_VAR = 1;
+var WRONG_TYPE = 2;
+var FILE_LOAD = 3;
+// p5.js blue, p5.js orange, auto dark green; fallback p5.js darkened magenta
+// See testColors below for all the color codes and names
+var typeColors = ['#2D7BB6', '#EE9900', '#4DB200', '#C83C00'];
+function report(message, func, color) {
+ if(doFriendlyWelcome){
+ friendlyWelcome();
+ doFriendlyWelcome =false;
+ }
+ if ('undefined' === getType(color)) {
+ color = '#B40033'; // dark magenta
+ } else if (getType(color) === 'number') { // Type to color
+ color = typeColors[color];
+ }
+ // LM TEMP commenting this out until we get the whole system working
+ // if (func.substring(0,4) === 'load'){
+ // console.log(
+ // '%c> p5.js says: '+message+'%c'+
+ // '[https://github.com/processing/p5.js/wiki/Local-server]',
+ // 'background-color:' + color + ';color:#FFF;',
+ // 'background-color:transparent;color:' + color +';',
+ // 'background-color:' + color + ';color:#FFF;',
+ // 'background-color:transparent;color:' + color +';'
+ // );
+ // }
+ // else{
+ // console.log(
+ // '%c> p5.js says: '+message+'%c [http://p5js.org/reference/#p5/'+func+
+ // ']', 'background-color:' + color + ';color:#FFF;',
+ // 'background-color:transparent;color:' + color +';'
+ // );
+ // }
+}
+
+/**
+ * Validate all the parameters of a function for number and type
+ * NOTE THIS FUNCTION IS TEMPORARILY DISABLED UNTIL FURTHER WORK
+ * AND UPDATES ARE IMPLEMENTED. -LMCCART
+ *
+ * @param {String} func name of function we're checking
+ * @param {Array} args pass of the JS default arguments array
+ * @param {Array} types List of types accepted ['Number', 'String, ...] OR
+ * a list of lists for each format: [
+ * ['String', 'Number', 'Number'],
+ * ['String', 'Number', 'Number', 'Number', 'Number'
+ * ]
+ *
+ * @return console logs
+ */
+p5.prototype._validateParameters = function(func, args, types) {
+ if (!isArray(types[0])) {
+ types = [types];
+ }
+ // Check number of parameters
+ // Example: "You wrote ellipse(X,X,X). ellipse was expecting 4
+ // parameters. Try ellipse(X,X,X,X)."
+ var diff = Math.abs(args.length-types[0].length);
+ var message, tindex = 0;
+ for (var i=1, len=types.length; i<len; i++) {
+ var d = Math.abs(args.length-types[i].length);
+ if (d <= diff) {
+ tindex = i;
+ diff = d;
+ }
+ }
+ var symbol = 'X'; // Parameter placeholder
+ if(diff > 0) {
+ message = 'You wrote ' + func + '(';
+ // Concat an appropriate number of placeholders for call
+ if (args.length > 0) {
+ message += symbol + Array(args.length).join(',' + symbol);
+ }
+ message += '). ' + func + ' was expecting ' + types[tindex].length +
+ ' parameters. Try ' + func + '(';
+ // Concat an appropriate number of placeholders for definition
+ if (types[tindex].length > 0) {
+ message += symbol + Array(types[tindex].length).join(',' + symbol);
+ }
+ message += ').';
+ // If multiple definitions
+ if (types.length > 1) {
+ message += ' ' + func + ' takes different numbers of parameters ' +
+ 'depending on what you want to do. Click this link to learn more: ';
+ }
+ report(message, func, PARAM_COUNT);
+ }
+ // Type checking
+ // Example: "It looks like ellipse received an empty variable in spot #2."
+ // Example: "ellipse was expecting a number for parameter #1,
+ // received "foo" instead."
+ for (var format=0; format<types.length; format++) {
+ for (var p=0; p < types[format].length && p < args.length; p++) {
+ var defType = types[format][p];
+ var argType = getType(args[p]);
+ if ('undefined' === argType || null === argType) {
+ report('It looks like ' + func +
+ ' received an empty variable in spot #' + (p+1) +
+ '. If not intentional, this is often a problem with scope: ' +
+ '[link to scope].', func, EMPTY_VAR);
+ } else if (defType !== '*' && !typeMatches(defType, argType, args[p])) {
+ message = func + ' was expecting a ' + defType.toLowerCase() +
+ ' for parameter #' + (p+1) + ', received ';
+ // Wrap strings in quotes
+ message += 'string' === argType ? '"' + args[p] + '"' : args[p];
+ message += ' instead.';
+ // If multiple definitions
+ if (types.length > 1) {
+ message += ' ' + func + ' takes different numbers of parameters ' +
+ 'depending on what you want to do. ' +
+ 'Click this link to learn more:';
+ }
+ report(message, func, WRONG_TYPE);
+ }
+ }
+ }
+};
+/*
+ * NOTE THIS FUNCTION IS TEMPORARILY DISABLED UNTIL FURTHER WORK
+ * AND UPDATES ARE IMPLEMENTED. -LMCCART
+ */
+p5.prototype._validateParameters = function() {
+ return true;
+};
+
+var errorCases = {
+ '0': {
+ fileType: 'image',
+ method: 'loadImage',
+ message: ' hosting the image online,'
+ },
+ '1': {
+ fileType: 'XML file',
+ method: 'loadXML'
+ },
+ '2': {
+ fileType: 'table file',
+ method: 'loadTable'
+ },
+ '3': {
+ fileType: 'text file',
+ method: 'loadStrings'
+ }
+};
+p5._friendlyFileLoadError = function (errorType, filePath) {
+ var errorInfo = errorCases[ errorType ];
+ var message = 'It looks like there was a problem' +
+ ' loading your ' + errorInfo.fileType + '.' +
+ ' Try checking if the file path%c [' + filePath + '] %cis correct,' +
+ (errorInfo.message || '') + ' or running a local server.';
+ report(message, errorInfo.method, FILE_LOAD);
+};
+
+function friendlyWelcome() {
+ // p5.js brand - magenta: #ED225D
+ var astrixBgColor = 'transparent';
+ var astrixTxtColor = '#ED225D';
+ var welcomeBgColor = '#ED225D';
+ var welcomeTextColor = 'white';
+ console.log(
+ '%c _ \n'+
+ ' /\\| |/\\ \n'+
+ ' \\ ` \' / \n'+
+ ' / , . \\ \n'+
+ ' \\/|_|\\/ '+
+ '\n\n%c> p5.js says: Welcome! '+
+ 'This is your friendly debugger. ' +
+ 'To turn me off switch to using “p5.min.js”.',
+ 'background-color:'+astrixBgColor+';color:' + astrixTxtColor +';',
+ 'background-color:'+welcomeBgColor+';color:' + welcomeTextColor +';'
+ );
+}
+
+/**
+ * Prints out all the colors in the color pallete with white text.
+ * For color blindness testing.
+ */
+/* function testColors() {
+ var str = 'A box of biscuits, a box of mixed biscuits and a biscuit mixer';
+ report(str, 'println', '#ED225D'); // p5.js magenta
+ report(str, 'println', '#2D7BB6'); // p5.js blue
+ report(str, 'println', '#EE9900'); // p5.js orange
+ report(str, 'println', '#A67F59'); // p5.js light brown
+ report(str, 'println', '#704F21'); // p5.js gold
+ report(str, 'println', '#1CC581'); // auto cyan
+ report(str, 'println', '#FF6625'); // auto orange
+ report(str, 'println', '#79EB22'); // auto green
+ report(str, 'println', '#B40033'); // p5.js darkened magenta
+ report(str, 'println', '#084B7F'); // p5.js darkened blue
+ report(str, 'println', '#945F00'); // p5.js darkened orange
+ report(str, 'println', '#6B441D'); // p5.js darkened brown
+ report(str, 'println', '#2E1B00'); // p5.js darkened gold
+ report(str, 'println', '#008851'); // auto dark cyan
+ report(str, 'println', '#C83C00'); // auto dark orange
+ report(str, 'println', '#4DB200'); // auto dark green
+} */
+
+// This is a lazily-defined list of p5 symbols that may be
+// misused by beginners at top-level code, outside of setup/draw. We'd like
+// to detect these errors and help the user by suggesting they move them
+// into setup/draw.
+//
+// For more details, see https://github.com/processing/p5.js/issues/1121.
+var misusedAtTopLevelCode = null;
+var FAQ_URL = 'https://github.com/processing/p5.js/wiki/' +
+ 'Frequently-Asked-Questions' +
+ '#why-cant-i-assign-variables-using-p5-functions-and-' +
+ 'variables-before-setup';
+
+function defineMisusedAtTopLevelCode() {
+ var uniqueNamesFound = {};
+
+ var getSymbols = function(obj) {
+ return Object.getOwnPropertyNames(obj).filter(function(name) {
+ if (name[0] === '_') {
+ return false;
+ }
+ if (name in uniqueNamesFound) {
+ return false;
+ }
+
+ uniqueNamesFound[name] = true;
+
+ return true;
+ }).map(function(name) {
+ var type;
+
+ if (typeof(obj[name]) === 'function') {
+ type = 'function';
+ } else if (name === name.toUpperCase()) {
+ type = 'constant';
+ } else {
+ type = 'variable';
+ }
+
+ return {name: name, type: type};
+ });
+ };
+
+ misusedAtTopLevelCode = [].concat(
+ getSymbols(p5.prototype),
+ // At present, p5 only adds its constants to p5.prototype during
+ // construction, which may not have happened at the time a
+ // ReferenceError is thrown, so we'll manually add them to our list.
+ getSymbols(_dereq_('./constants'))
+ );
+
+ // This will ultimately ensure that we report the most specific error
+ // possible to the user, e.g. advising them about HALF_PI instead of PI
+ // when their code misuses the former.
+ misusedAtTopLevelCode.sort(function(a, b) {
+ return b.name.length - a.name.length;
+ });
+}
+
+function helpForMisusedAtTopLevelCode(e, log) {
+ if (!log) {
+ log = console.log.bind(console);
+ }
+
+ if (!misusedAtTopLevelCode) {
+ defineMisusedAtTopLevelCode();
+ }
+
+ // If we find that we're logging lots of false positives, we can
+ // uncomment the following code to avoid displaying anything if the
+ // user's code isn't likely to be using p5's global mode. (Note that
+ // setup/draw are more likely to be defined due to JS function hoisting.)
+ //
+ //if (!('setup' in window || 'draw' in window)) {
+ // return;
+ //}
+
+ misusedAtTopLevelCode.some(function(symbol) {
+ // Note that while just checking for the occurrence of the
+ // symbol name in the error message could result in false positives,
+ // a more rigorous test is difficult because different browsers
+ // log different messages, and the format of those messages may
+ // change over time.
+ //
+ // For example, if the user uses 'PI' in their code, it may result
+ // in any one of the following messages:
+ //
+ // * 'PI' is undefined (Microsoft Edge)
+ // * ReferenceError: PI is undefined (Firefox)
+ // * Uncaught ReferenceError: PI is not defined (Chrome)
+
+ if (e.message && e.message.indexOf(symbol.name) !== -1) {
+ log('%cDid you just try to use p5.js\'s ' + symbol.name +
+ (symbol.type === 'function' ? '() ' : ' ') + symbol.type +
+ '? If so, you may want to ' +
+ 'move it into your sketch\'s setup() function.\n\n' +
+ 'For more details, see: ' + FAQ_URL,
+ 'color: #B40033' /* Dark magenta */);
+ return true;
+ }
+ });
+}
+
+// Exposing this primarily for unit testing.
+p5.prototype._helpForMisusedAtTopLevelCode = helpForMisusedAtTopLevelCode;
+
+if (document.readyState !== 'complete') {
+ window.addEventListener('error', helpForMisusedAtTopLevelCode, false);
+
+ // Our job is only to catch ReferenceErrors that are thrown when
+ // global (non-instance mode) p5 APIs are used at the top-level
+ // scope of a file, so we'll unbind our error listener now to make
+ // sure we don't log false positives later.
+ window.addEventListener('load', function() {
+ window.removeEventListener('error', helpForMisusedAtTopLevelCode, false);
+ });
+}
+
+module.exports = p5;
+
+},{"./constants":47,"./core":48}],52:[function(_dereq_,module,exports){
+/**
+ * @module DOM
+ * @submodule DOM
+ * @for p5.Element
+ */
+
+var p5 = _dereq_('./core');
+
+/**
+ * Base class for all elements added to a sketch, including canvas,
+ * graphics buffers, and other HTML elements. Methods in blue are
+ * included in the core functionality, methods in brown are added
+ * with the <a href="http://p5js.org/libraries/">p5.dom library</a>.
+ * It is not called directly, but p5.Element
+ * objects are created by calling createCanvas, createGraphics,
+ * or in the p5.dom library, createDiv, createImg, createInput, etc.
+ *
+ * @class p5.Element
+ * @constructor
+ * @param {String} elt DOM node that is wrapped
+ * @param {Object} [pInst] pointer to p5 instance
+ */
+p5.Element = function(elt, pInst) {
+ /**
+ * Underlying HTML element. All normal HTML methods can be called on this.
+ *
+ * @property elt
+ */
+ this.elt = elt;
+ this._pInst = pInst;
+ this._events = {};
+ this.width = this.elt.offsetWidth;
+ this.height = this.elt.offsetHeight;
+};
+
+/**
+ *
+ * Attaches the element to the parent specified. A way of setting
+ * the container for the element. Accepts either a string ID, DOM
+ * node, or p5.Element. If no arguments given, parent node is returned.
+ *
+ * @method parent
+ * @param {String|Object} parent the ID, DOM node, or p5.Element
+ * of desired parent element
+ * @return {p5.Element}
+ * @example
+ * <div class="norender"><code>
+ * // in the html file:
+ * &lt;div id="myContainer">&lt;/div>
+ * // in the js file:
+ * var cnv = createCanvas(100, 100);
+ * cnv.parent("myContainer");
+ * </code></div>
+ * <div class='norender'><code>
+ * var div0 = createDiv('this is the parent');
+ * var div1 = createDiv('this is the child');
+ * div1.parent(div0); // use p5.Element
+ * </code></div>
+ * <div class='norender'><code>
+ * var div0 = createDiv('this is the parent');
+ * div0.id('apples');
+ * var div1 = createDiv('this is the child');
+ * div1.parent('apples'); // use id
+ * </code></div>
+ * <div class='norender'><code>
+ * var elt = document.getElementById('myParentDiv');
+ * var div1 = createDiv('this is the child');
+ * div1.parent(elt); // use element from page
+ * </code></div>
+ */
+p5.Element.prototype.parent = function(p) {
+ if (arguments.length === 0){
+ return this.elt.parentNode;
+ } else {
+ if (typeof p === 'string') {
+ if (p[0] === '#') {
+ p = p.substring(1);
+ }
+ p = document.getElementById(p);
+ } else if (p instanceof p5.Element) {
+ p = p.elt;
+ }
+ p.appendChild(this.elt);
+ return this;
+ }
+};
+
+/**
+ *
+ * Sets the ID of the element
+ *
+ * @method id
+ * @param {String} id ID of the element
+ * @return {p5.Element}
+ */
+p5.Element.prototype.id = function(id) {
+ if (arguments.length === 0) {
+ return this.elt.id;
+ } else {
+ this.elt.id = id;
+ this.width = this.elt.offsetWidth;
+ this.height = this.elt.offsetHeight;
+ return this;
+ }
+};
+
+/**
+ *
+ * Adds given class to the element
+ *
+ * @method class
+ * @param {String} class class to add
+ * @return {p5.Element}
+ */
+p5.Element.prototype.class = function(c) {
+ if (arguments.length === 0) {
+ return this.elt.className;
+ } else {
+ this.elt.className = c;
+ this.width = this.elt.offsetWidth;
+ this.height = this.elt.offsetHeight;
+ return this;
+ }
+};
+
+/**
+ * The .mousePressed() function is called once after every time a
+ * mouse button is pressed over the element. This can be used to
+ * attach element specific event listeners.
+ *
+ * @method mousePressed
+ * @param {Function} fxn function to be fired when mouse is
+ * pressed over the element.
+ * @return {p5.Element}
+ * @example
+ * <div class='norender'><code>
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ * cnv = createCanvas(100, 100);
+ * cnv.mousePressed(changeGray); // attach listener for
+ * // canvas click only
+ * d = 10;
+ * g = 100;
+ * }
+ *
+ * function draw() {
+ * background(g);
+ * ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * // this function fires with any click anywhere
+ * function mousePressed() {
+ * d = d + 10;
+ * }
+ *
+ * // this function fires only when cnv is clicked
+ * function changeGray() {
+ * g = random(0, 255);
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.mousePressed = function (fxn) {
+ attachListener('mousedown', fxn, this);
+ attachListener('touchstart', fxn, this);
+ return this;
+};
+
+/**
+ * The .mouseWheel() function is called once after every time a
+ * mouse wheel is scrolled over the element. This can be used to
+ * attach element specific event listeners.<br><br>
+ * The event.wheelDelta or event.detail property returns negative values if
+ * the mouse wheel if rotated up or away from the user and positive in the
+ * other direction. On OS X with "natural" scrolling enabled, the values are
+ * opposite.
+ *
+ * @method mouseWheel
+ * @param {Function} fxn function to be fired when mouse wheel is
+ * scrolled over the element.
+ * @return {p5.Element}
+ * @example
+ * <div class='norender'><code>
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ * cnv = createCanvas(100, 100);
+ * cnv.mouseWheel(changeSize); // attach listener for
+ * // activity on canvas only
+ * d = 10;
+ * g = 100;
+ * }
+ *
+ * function draw() {
+ * background(g);
+ * ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * // this function fires with mousewheel movement
+ * // anywhere on screen
+ * function mouseWheel() {
+ * g = g + 10;
+ * }
+ *
+ * // this function fires with mousewheel movement
+ * // over canvas only
+ * function changeSize() {
+ * if (event.wheelDelta > 0) {
+ * d = d + 10;
+ * } else {
+ * d = d - 10;
+ * }
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.mouseWheel = function (fxn) {
+ attachListener('wheel', fxn, this);
+ return this;
+};
+
+/**
+ * The .mouseReleased() function is called once after every time a
+ * mouse button is released over the element. This can be used to
+ * attach element specific event listeners.
+ *
+ * @method mouseReleased
+ * @param {Function} fxn function to be fired when mouse is
+ * released over the element.
+ * @return {p5.Element}
+ * @example
+ * <div class='norender'><code>
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ * cnv = createCanvas(100, 100);
+ * cnv.mouseReleased(changeGray); // attach listener for
+ * // activity on canvas only
+ * d = 10;
+ * g = 100;
+ * }
+ *
+ * function draw() {
+ * background(g);
+ * ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * // this function fires after the mouse has been
+ * // released
+ * function mouseReleased() {
+ * d = d + 10;
+ * }
+ *
+ * // this function fires after the mouse has been
+ * // released while on canvas
+ * function changeGray() {
+ * g = random(0, 255);
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.mouseReleased = function (fxn) {
+ attachListener('mouseup', fxn, this);
+ attachListener('touchend', fxn, this);
+ return this;
+};
+
+
+/**
+ * The .mouseClicked() function is called once after a mouse button is
+ * pressed and released over the element. This can be used to
+ * attach element specific event listeners.
+ *
+ * @method mouseClicked
+ * @param {Function} fxn function to be fired when mouse is
+ * clicked over the element.
+ * @return {p5.Element}
+ * @example
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ * cnv = createCanvas(100, 100);
+ * cnv.mouseClicked(changeGray); // attach listener for
+ * // activity on canvas only
+ * d = 10;
+ * g = 100;
+ * }
+ *
+ * function draw() {
+ * background(g);
+ * ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * // this function fires after the mouse has been
+ * // clicked anywhere
+ * function mouseClicked() {
+ * d = d + 10;
+ * }
+ *
+ * // this function fires after the mouse has been
+ * // clicked on canvas
+ * function changeGray() {
+ * g = random(0, 255);
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.mouseClicked = function (fxn) {
+ attachListener('click', fxn, this);
+ return this;
+};
+
+/**
+ * The .mouseMoved() function is called once every time a
+ * mouse moves over the element. This can be used to attach an
+ * element specific event listener.
+ *
+ * @method mouseMoved
+ * @param {Function} fxn function to be fired when mouse is
+ * moved over the element.
+ * @return {p5.Element}
+ * @example
+ * <div class='norender'><code>
+ * var cnv;
+ * var d = 30;
+ * var g;
+ * function setup() {
+ * cnv = createCanvas(100, 100);
+ * cnv.mouseMoved(changeSize); // attach listener for
+ * // activity on canvas only
+ * d = 10;
+ * g = 100;
+ * }
+ *
+ * function draw() {
+ * background(g);
+ * fill(200);
+ * ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * // this function fires when mouse moves anywhere on
+ * // page
+ * function mouseMoved() {
+ * g = g + 5;
+ * if (g > 255) {
+ * g = 0;
+ * }
+ * }
+ *
+ * // this function fires when mouse moves over canvas
+ * function changeSize() {
+ * d = d + 2;
+ * if (d > 100) {
+ * d = 0;
+ * }
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.mouseMoved = function (fxn) {
+ attachListener('mousemove', fxn, this);
+ attachListener('touchmove', fxn, this);
+ return this;
+};
+
+/**
+ * The .mouseOver() function is called once after every time a
+ * mouse moves onto the element. This can be used to attach an
+ * element specific event listener.
+ *
+ * @method mouseOver
+ * @param {Function} fxn function to be fired when mouse is
+ * moved over the element.
+ * @return {p5.Element}
+ * @example
+ * <div class='norender'><code>
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ * cnv = createCanvas(100, 100);
+ * cnv.mouseOver(changeGray);
+ * d = 10;
+ * }
+ *
+ * function draw() {
+ * ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * function changeGray() {
+ * d = d + 10;
+ * if (d > 100) {
+ * d = 0;
+ * }
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.mouseOver = function (fxn) {
+ attachListener('mouseover', fxn, this);
+ return this;
+};
+
+
+/**
+ * The .changed() function is called when the value of an
+ * element is changed.
+ * This can be used to attach an element specific event listener.
+ *
+ * @method changed
+ * @param {Function} fxn function to be fired when the value of an
+ * element changes.
+ * @return {p5.Element}
+ * @example
+ * <div><code>
+ * var sel;
+ *
+ * function setup() {
+ * textAlign(CENTER);
+ * background(200);
+ * sel = createSelect();
+ * sel.position(10, 10);
+ * sel.option('pear');
+ * sel.option('kiwi');
+ * sel.option('grape');
+ * sel.changed(mySelectEvent);
+ * }
+ *
+ * function mySelectEvent() {
+ * var item = sel.value();
+ * background(200);
+ * text("it's a "+item+"!", 50, 50);
+ * }
+ * </code></div>
+ * <div><code>
+ * var checkbox;
+ * var cnv;
+ *
+ * function setup() {
+ * checkbox = createCheckbox(" fill");
+ * checkbox.changed(changeFill);
+ * cnv = createCanvas(100, 100);
+ * cnv.position(0, 30);
+ * noFill();
+ * }
+ *
+ * function draw() {
+ * background(200);
+ * ellipse(50, 50, 50, 50);
+ * }
+ *
+ * function changeFill() {
+ * if (checkbox.checked()) {
+ * fill(0);
+ * } else {
+ * noFill();
+ * }
+ * }
+ * </code></div>
+ */
+p5.Element.prototype.changed = function (fxn) {
+ attachListener('change', fxn, this);
+ return this;
+};
+
+/**
+ * The .input() function is called when any user input is
+ * detected with an element. The input event is often used
+ * to detect keystrokes in a input element, or changes on a
+ * slider element. This can be used to attach an element specific
+ * event listener.
+ *
+ * @method input
+ * @param {Function} fxn function to be fired on user input.
+ * @return {p5.Element}
+ * @example
+ * <div class='norender'><code>
+ * // Open your console to see the output
+ * function setup() {
+ * var inp = createInput('');
+ * inp.input(myInputEvent);
+ * }
+ *
+ * function myInputEvent() {
+ * console.log('you are typing: ', this.value());
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.input = function (fxn) {
+ attachListener('input', fxn, this);
+ return this;
+};
+
+/**
+ * The .mouseOut() function is called once after every time a
+ * mouse moves off the element. This can be used to attach an
+ * element specific event listener.
+ *
+ * @method mouseOut
+ * @param {Function} fxn function to be fired when mouse is
+ * moved off the element.
+ * @return {p5.Element}
+ * @example
+ * <div class='norender'><code>
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ * cnv = createCanvas(100, 100);
+ * cnv.mouseOut(changeGray);
+ * d = 10;
+ * }
+ *
+ * function draw() {
+ * ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * function changeGray() {
+ * d = d + 10;
+ * if (d > 100) {
+ * d = 0;
+ * }
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.mouseOut = function (fxn) {
+ attachListener('mouseout', fxn, this);
+ return this;
+};
+
+/**
+ * The .touchStarted() function is called once after every time a touch is
+ * registered. This can be used to attach element specific event listeners.
+ *
+ * @method touchStarted
+ * @param {Function} fxn function to be fired when touch is
+ * started over the element.
+ * @return {p5.Element}
+ * @example
+ * <div class='norender'><code>
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ * cnv = createCanvas(100, 100);
+ * cnv.touchStarted(changeGray); // attach listener for
+ * // canvas click only
+ * d = 10;
+ * g = 100;
+ * }
+ *
+ * function draw() {
+ * background(g);
+ * ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * // this function fires with any touch anywhere
+ * function touchStarted() {
+ * d = d + 10;
+ * }
+ *
+ * // this function fires only when cnv is clicked
+ * function changeGray() {
+ * g = random(0, 255);
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.touchStarted = function (fxn) {
+ attachListener('touchstart', fxn, this);
+ attachListener('mousedown', fxn, this);
+ return this;
+};
+
+/**
+ * The .touchMoved() function is called once after every time a touch move is
+ * registered. This can be used to attach element specific event listeners.
+ *
+ * @method touchMoved
+ * @param {Function} fxn function to be fired when touch is moved
+ * over the element.
+ * @return {p5.Element}
+ * @example
+ * <div class='norender'><code>
+ * var cnv;
+ * var g;
+ * function setup() {
+ * cnv = createCanvas(100, 100);
+ * cnv.touchMoved(changeGray); // attach listener for
+ * // canvas click only
+ * g = 100;
+ * }
+ *
+ * function draw() {
+ * background(g);
+ * }
+ *
+ * // this function fires only when cnv is clicked
+ * function changeGray() {
+ * g = random(0, 255);
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.touchMoved = function (fxn) {
+ attachListener('touchmove', fxn, this);
+ attachListener('mousemove', fxn, this);
+ return this;
+};
+
+/**
+ * The .touchEnded() function is called once after every time a touch is
+ * registered. This can be used to attach element specific event listeners.
+ *
+ * @method touchEnded
+ * @param {Function} fxn function to be fired when touch is
+ * ended over the element.
+ * @return {p5.Element}
+ * @example
+ * <div class='norender'><code>
+ * var cnv;
+ * var d;
+ * var g;
+ * function setup() {
+ * cnv = createCanvas(100, 100);
+ * cnv.touchEnded(changeGray); // attach listener for
+ * // canvas click only
+ * d = 10;
+ * g = 100;
+ * }
+ *
+ * function draw() {
+ * background(g);
+ * ellipse(width/2, height/2, d, d);
+ * }
+ *
+ * // this function fires with any touch anywhere
+ * function touchEnded() {
+ * d = d + 10;
+ * }
+ *
+ * // this function fires only when cnv is clicked
+ * function changeGray() {
+ * g = random(0, 255);
+ * }
+ * </code></div>
+ *
+ */
+p5.Element.prototype.touchEnded = function (fxn) {
+ attachListener('touchend', fxn, this);
+ attachListener('mouseup', fxn, this);
+ return this;
+};
+
+
+
+/**
+ * The .dragOver() function is called once after every time a
+ * file is dragged over the element. This can be used to attach an
+ * element specific event listener.
+ *
+ * @method dragOver
+ * @param {Function} fxn function to be fired when mouse is
+ * dragged over the element.
+ * @return {p5.Element}
+ */
+p5.Element.prototype.dragOver = function (fxn) {
+ attachListener('dragover', fxn, this);
+ return this;
+};
+
+/**
+ * The .dragLeave() function is called once after every time a
+ * dragged file leaves the element area. This can be used to attach an
+ * element specific event listener.
+ *
+ * @method dragLeave
+ * @param {Function} fxn function to be fired when mouse is
+ * dragged over the element.
+ * @return {p5.Element}
+ */
+p5.Element.prototype.dragLeave = function (fxn) {
+ attachListener('dragleave', fxn, this);
+ return this;
+};
+
+/**
+ * The .drop() function is called for each file dropped on the element.
+ * It requires a callback that is passed a p5.File object. You can
+ * optionally pass two callbacks, the first one (required) is triggered
+ * for each file dropped when the file is loaded. The second (optional)
+ * is triggered just once when a file (or files) are dropped.
+ *
+ * @method drop
+ * @param {Function} callback triggered when files are dropped.
+ * @param {Function} callback to receive loaded file.
+ * @return {p5.Element}
+ */
+p5.Element.prototype.drop = function (callback, fxn) {
+ // Make a file loader callback and trigger user's callback
+ function makeLoader(theFile) {
+ // Making a p5.File object
+ var p5file = new p5.File(theFile);
+ return function(e) {
+ p5file.data = e.target.result;
+ callback(p5file);
+ };
+ }
+
+ // Is the file stuff supported?
+ if (window.File && window.FileReader && window.FileList && window.Blob) {
+
+ // If you want to be able to drop you've got to turn off
+ // a lot of default behavior
+ attachListener('dragover',function(evt) {
+ evt.stopPropagation();
+ evt.preventDefault();
+ },this);
+
+ // If this is a drag area we need to turn off the default behavior
+ attachListener('dragleave',function(evt) {
+ evt.stopPropagation();
+ evt.preventDefault();
+ },this);
+
+ // If just one argument it's the callback for the files
+ if (arguments.length > 1) {
+ attachListener('drop', fxn, this);
+ }
+
+ // Deal with the files
+ attachListener('drop', function(evt) {
+
+ evt.stopPropagation();
+ evt.preventDefault();
+
+ // A FileList
+ var files = evt.dataTransfer.files;
+
+ // Load each one and trigger the callback
+ for (var i = 0; i < files.length; i++) {
+ var f = files[i];
+ var reader = new FileReader();
+ reader.onload = makeLoader(f);
+
+
+ // Text or data?
+ // This should likely be improved
+ if (f.type.indexOf('text') > -1) {
+ reader.readAsText(f);
+ } else {
+ reader.readAsDataURL(f);
+ }
+ }
+ }, this);
+ } else {
+ console.log('The File APIs are not fully supported in this browser.');
+ }
+
+ return this;
+};
+
+
+
+
+function attachListener(ev, fxn, ctx) {
+ // LM removing, not sure why we had this?
+ // var _this = ctx;
+ // var f = function (e) { fxn(e, _this); };
+ var f = fxn.bind(ctx);
+ ctx.elt.addEventListener(ev, f, false);
+ ctx._events[ev] = f;
+}
+
+/**
+ * Helper fxn for sharing pixel methods
+ *
+ */
+p5.Element.prototype._setProperty = function (prop, value) {
+ this[prop] = value;
+};
+
+
+module.exports = p5.Element;
+
+},{"./core":48}],53:[function(_dereq_,module,exports){
+/**
+ * @module Rendering
+ * @submodule Rendering
+ * @for p5
+ */
+
+var p5 = _dereq_('./core');
+var constants = _dereq_('./constants');
+
+/**
+ * Thin wrapper around a renderer, to be used for creating a
+ * graphics buffer object. Use this class if you need
+ * to draw into an off-screen graphics buffer. The two parameters define the
+ * width and height in pixels. The fields and methods for this class are
+ * extensive, but mirror the normal drawing API for p5.
+ *
+ * @class p5.Graphics
+ * @constructor
+ * @extends p5.Element
+ * @param {String} elt DOM node that is wrapped
+ * @param {Object} [pInst] pointer to p5 instance
+ * @param {Boolean} whether we're using it as main canvas
+ */
+p5.Graphics = function(w, h, renderer, pInst) {
+
+ var r = renderer || constants.P2D;
+
+ var c = document.createElement('canvas');
+ var node = this._userNode || document.body;
+ node.appendChild(c);
+
+ p5.Element.call(this, c, pInst, false);
+ this._styles = [];
+ this.width = w;
+ this.height = h;
+ this._pixelDensity = pInst._pixelDensity;
+
+ if (r === constants.WEBGL) {
+ this._renderer = new p5.Renderer3D(c, pInst, false);
+ } else {
+ this._renderer = new p5.Renderer2D(c, pInst, false);
+ }
+
+ this._renderer.resize(w, h);
+ this._renderer._applyDefaults();
+
+ pInst._elements.push(this);
+
+ // bind methods and props of p5 to the new object
+ for (var p in p5.prototype) {
+ if (!this[p]) {
+ if (typeof p5.prototype[p] === 'function') {
+ this[p] = p5.prototype[p].bind(this);
+ } else {
+ this[p] = p5.prototype[p];
+ }
+ }
+ }
+
+ return this;
+};
+
+p5.Graphics.prototype = Object.create(p5.Element.prototype);
+
+module.exports = p5.Graphics;
+
+},{"./constants":47,"./core":48}],54:[function(_dereq_,module,exports){
+/**
+ * @module Rendering
+ * @submodule Rendering
+ * @for p5
+ */
+
+var p5 = _dereq_('./core');
+var constants = _dereq_('../core/constants');
+
+/**
+ * Main graphics and rendering context, as well as the base API
+ * implementation for p5.js "core". To be used as the superclass for
+ * Renderer2D and Renderer3D classes, respecitvely.
+ *
+ * @class p5.Renderer
+ * @constructor
+ * @extends p5.Element
+ * @param {String} elt DOM node that is wrapped
+ * @param {Object} [pInst] pointer to p5 instance
+ * @param {Boolean} whether we're using it as main canvas
+ */
+p5.Renderer = function(elt, pInst, isMainCanvas) {
+ p5.Element.call(this, elt, pInst);
+ this.canvas = elt;
+ this._pInst = pInst;
+ if (isMainCanvas) {
+ this._isMainCanvas = true;
+ // for pixel method sharing with pimage
+ this._pInst._setProperty('_curElement', this);
+ this._pInst._setProperty('canvas', this.canvas);
+ this._pInst._setProperty('width', this.width);
+ this._pInst._setProperty('height', this.height);
+ } else { // hide if offscreen buffer by default
+ this.canvas.style.display = 'none';
+ this._styles = []; // non-main elt styles stored in p5.Renderer
+ }
+
+
+ this._textSize = 12;
+ this._textLeading = 15;
+ this._textFont = 'sans-serif';
+ this._textStyle = constants.NORMAL;
+ this._textAscent = null;
+ this._textDescent = null;
+
+
+ this._rectMode = constants.CORNER;
+ this._ellipseMode = constants.CENTER;
+ this._curveTightness = 0;
+ this._imageMode = constants.CORNER;
+
+ this._tint = null;
+ this._doStroke = true;
+ this._doFill = true;
+ this._strokeSet = false;
+ this._fillSet = false;
+ this._colorMode = constants.RGB;
+ this._colorMaxes = {
+ rgb: [255, 255, 255, 255],
+ hsb: [360, 100, 100, 1],
+ hsl: [360, 100, 100, 1]
+ };
+
+};
+
+p5.Renderer.prototype = Object.create(p5.Element.prototype);
+
+
+
+
+/**
+ * Resize our canvas element.
+ */
+p5.Renderer.prototype.resize = function(w, h) {
+ this.width = w;
+ this.height = h;
+ this.elt.width = w * this._pInst._pixelDensity;
+ this.elt.height = h * this._pInst._pixelDensity;
+ this.elt.style.width = w +'px';
+ this.elt.style.height = h + 'px';
+ if (this._isMainCanvas) {
+ this._pInst._setProperty('width', this.width);
+ this._pInst._setProperty('height', this.height);
+ }
+};
+
+p5.Renderer.prototype.textLeading = function(l) {
+
+ if (arguments.length && arguments[0]) {
+
+ this._setProperty('_textLeading', l);
+ return this;
+ }
+
+ return this._textLeading;
+};
+
+p5.Renderer.prototype.textSize = function(s) {
+
+ if (arguments.length && arguments[0]) {
+
+ this._setProperty('_textSize', s);
+ this._setProperty('_textLeading', s * constants._DEFAULT_LEADMULT);
+ return this._applyTextProperties();
+ }
+
+ return this._textSize;
+};
+
+p5.Renderer.prototype.textStyle = function(s) {
+
+ if (arguments.length && arguments[0]) {
+
+ if (s === constants.NORMAL ||
+ s === constants.ITALIC ||
+ s === constants.BOLD) {
+ this._setProperty('_textStyle', s);
+ }
+
+ return this._applyTextProperties();
+ }
+
+ return this._textStyle;
+};
+
+p5.Renderer.prototype.textAscent = function() {
+ if (this._textAscent === null) {
+ this._updateTextMetrics();
+ }
+ return this._textAscent;
+};
+
+p5.Renderer.prototype.textDescent = function() {
+
+ if (this._textDescent === null) {
+ this._updateTextMetrics();
+ }
+ return this._textDescent;
+};
+
+/**
+ * Helper fxn to check font type (system or otf)
+ */
+p5.Renderer.prototype._isOpenType = function(f) {
+
+ f = f || this._textFont;
+ return (typeof f === 'object' && f.font && f.font.supported);
+};
+
+p5.Renderer.prototype._updateTextMetrics = function() {
+
+ if (this._isOpenType()) {
+
+ this._setProperty('_textAscent', this._textFont._textAscent());
+ this._setProperty('_textDescent', this._textFont._textDescent());
+ return this;
+ }
+
+ // Adapted from http://stackoverflow.com/a/25355178
+ var text = document.createElement('span');
+ text.style.fontFamily = this._textFont;
+ text.style.fontSize = this._textSize + 'px';
+ text.innerHTML = 'ABCjgq|';
+
+ var block = document.createElement('div');
+ block.style.display = 'inline-block';
+ block.style.width = '1px';
+ block.style.height = '0px';
+
+ var container = document.createElement('div');
+ container.appendChild(text);
+ container.appendChild(block);
+
+ container.style.height = '0px';
+ container.style.overflow = 'hidden';
+ document.body.appendChild(container);
+
+ block.style.verticalAlign = 'baseline';
+ var blockOffset = calculateOffset(block);
+ var textOffset = calculateOffset(text);
+ var ascent = blockOffset[1] - textOffset[1];
+
+ block.style.verticalAlign = 'bottom';
+ blockOffset = calculateOffset(block);
+ textOffset = calculateOffset(text);
+ var height = blockOffset[1] - textOffset[1];
+ var descent = height - ascent;
+
+ document.body.removeChild(container);
+
+ this._setProperty('_textAscent', ascent);
+ this._setProperty('_textDescent', descent);
+
+ return this;
+};
+
+/**
+ * Helper fxn to measure ascent and descent.
+ * Adapted from http://stackoverflow.com/a/25355178
+ */
+function calculateOffset(object) {
+ var currentLeft = 0,
+ currentTop = 0;
+ if (object.offsetParent) {
+ do {
+ currentLeft += object.offsetLeft;
+ currentTop += object.offsetTop;
+ } while (object = object.offsetParent);
+ } else {
+ currentLeft += object.offsetLeft;
+ currentTop += object.offsetTop;
+ }
+ return [currentLeft, currentTop];
+}
+
+module.exports = p5.Renderer;
+
+},{"../core/constants":47,"./core":48}],55:[function(_dereq_,module,exports){
+
+var p5 = _dereq_('./core');
+var canvas = _dereq_('./canvas');
+var constants = _dereq_('./constants');
+var filters = _dereq_('../image/filters');
+
+_dereq_('./p5.Renderer');
+
+/**
+ * p5.Renderer2D
+ * The 2D graphics canvas renderer class.
+ * extends p5.Renderer
+ */
+var styleEmpty = 'rgba(0,0,0,0)';
+// var alphaThreshold = 0.00125; // minimum visible
+
+p5.Renderer2D = function(elt, pInst, isMainCanvas){
+ p5.Renderer.call(this, elt, pInst, isMainCanvas);
+ this.drawingContext = this.canvas.getContext('2d');
+ this._pInst._setProperty('drawingContext', this.drawingContext);
+ return this;
+};
+
+p5.Renderer2D.prototype = Object.create(p5.Renderer.prototype);
+
+p5.Renderer2D.prototype._applyDefaults = function() {
+ this.drawingContext.fillStyle = constants._DEFAULT_FILL;
+ this.drawingContext.strokeStyle = constants._DEFAULT_STROKE;
+ this.drawingContext.lineCap = constants.ROUND;
+ this.drawingContext.font = 'normal 12px sans-serif';
+};
+
+p5.Renderer2D.prototype.resize = function(w,h) {
+ p5.Renderer.prototype.resize.call(this, w,h);
+ this.drawingContext.scale(this._pInst._pixelDensity,
+ this._pInst._pixelDensity);
+};
+
+//////////////////////////////////////////////
+// COLOR | Setting
+//////////////////////////////////////////////
+
+p5.Renderer2D.prototype.background = function() {
+ this.drawingContext.save();
+ this.drawingContext.setTransform(1, 0, 0, 1, 0, 0);
+ this.drawingContext.scale(this._pInst._pixelDensity,
+ this._pInst._pixelDensity);
+
+ if (arguments[0] instanceof p5.Image) {
+ this._pInst.image(arguments[0], 0, 0, this.width, this.height);
+ } else {
+ var curFill = this.drawingContext.fillStyle;
+ // create background rect
+ var color = this._pInst.color.apply(this, arguments);
+ var newFill = color.toString();
+ this.drawingContext.fillStyle = newFill;
+ this.drawingContext.fillRect(0, 0, this.width, this.height);
+ // reset fill
+ this.drawingContext.fillStyle = curFill;
+ }
+ this.drawingContext.restore();
+};
+
+p5.Renderer2D.prototype.clear = function() {
+ this.drawingContext.clearRect(0, 0, this.width, this.height);
+};
+
+p5.Renderer2D.prototype.fill = function() {
+
+ var ctx = this.drawingContext;
+ var color = this._pInst.color.apply(this, arguments);
+ ctx.fillStyle = color.toString();
+};
+
+p5.Renderer2D.prototype.stroke = function() {
+ var ctx = this.drawingContext;
+ var color = this._pInst.color.apply(this, arguments);
+ ctx.strokeStyle = color.toString();
+};
+
+//////////////////////////////////////////////
+// IMAGE | Loading & Displaying
+//////////////////////////////////////////////
+
+p5.Renderer2D.prototype.image =
+ function (img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) {
+ var cnv;
+ try {
+ if (this._tint) {
+ if (p5.MediaElement && img instanceof p5.MediaElement) {
+ img.loadPixels();
+ }
+ if (img.canvas) {
+ cnv = this._getTintedImageCanvas(img);
+ }
+ }
+ if (!cnv) {
+ cnv = img.canvas || img.elt;
+ }
+ this.drawingContext.drawImage(cnv, sx, sy, sWidth, sHeight, dx, dy,
+ dWidth, dHeight);
+ } catch (e) {
+ if (e.name !== 'NS_ERROR_NOT_AVAILABLE') {
+ throw e;
+ }
+ }
+};
+
+p5.Renderer2D.prototype._getTintedImageCanvas = function (img) {
+ if (!img.canvas) {
+ return img;
+ }
+ var pixels = filters._toPixels(img.canvas);
+ var tmpCanvas = document.createElement('canvas');
+ tmpCanvas.width = img.canvas.width;
+ tmpCanvas.height = img.canvas.height;
+ var tmpCtx = tmpCanvas.getContext('2d');
+ var id = tmpCtx.createImageData(img.canvas.width, img.canvas.height);
+ var newPixels = id.data;
+ for (var i = 0; i < pixels.length; i += 4) {
+ var r = pixels[i];
+ var g = pixels[i + 1];
+ var b = pixels[i + 2];
+ var a = pixels[i + 3];
+ newPixels[i] = r * this._tint[0] / 255;
+ newPixels[i + 1] = g * this._tint[1] / 255;
+ newPixels[i + 2] = b * this._tint[2] / 255;
+ newPixels[i + 3] = a * this._tint[3] / 255;
+ }
+ tmpCtx.putImageData(id, 0, 0);
+ return tmpCanvas;
+};
+
+
+//////////////////////////////////////////////
+// IMAGE | Pixels
+//////////////////////////////////////////////
+
+p5.Renderer2D.prototype.blendMode = function(mode) {
+ this.drawingContext.globalCompositeOperation = mode;
+};
+p5.Renderer2D.prototype.blend = function() {
+ var currBlend = this.drawingContext.globalCompositeOperation;
+ var blendMode = arguments[arguments.length - 1];
+
+ var copyArgs = Array.prototype.slice.call(
+ arguments,
+ 0,
+ arguments.length - 1
+ );
+
+ this.drawingContext.globalCompositeOperation = blendMode;
+ this._pInst.copy.apply(this._pInst, copyArgs);
+ this.drawingContext.globalCompositeOperation = currBlend;
+};
+
+p5.Renderer2D.prototype.copy = function () {
+ var srcImage, sx, sy, sw, sh, dx, dy, dw, dh;
+ if (arguments.length === 9) {
+ srcImage = arguments[0];
+ sx = arguments[1];
+ sy = arguments[2];
+ sw = arguments[3];
+ sh = arguments[4];
+ dx = arguments[5];
+ dy = arguments[6];
+ dw = arguments[7];
+ dh = arguments[8];
+ } else if (arguments.length === 8) {
+ srcImage = this._pInst;
+ sx = arguments[0];
+ sy = arguments[1];
+ sw = arguments[2];
+ sh = arguments[3];
+ dx = arguments[4];
+ dy = arguments[5];
+ dw = arguments[6];
+ dh = arguments[7];
+ } else {
+ throw new Error('Signature not supported');
+ }
+ p5.Renderer2D._copyHelper(srcImage, sx, sy, sw, sh, dx, dy, dw, dh);
+};
+
+p5.Renderer2D._copyHelper =
+function (srcImage, sx, sy, sw, sh, dx, dy, dw, dh) {
+ var s = srcImage.canvas.width / srcImage.width;
+ this.drawingContext.drawImage(srcImage.canvas,
+ s * sx, s * sy, s * sw, s * sh, dx, dy, dw, dh);
+};
+
+p5.Renderer2D.prototype.get = function(x, y, w, h) {
+ if (x === undefined && y === undefined &&
+ w === undefined && h === undefined){
+ x = 0;
+ y = 0;
+ w = this.width;
+ h = this.height;
+ } else if (w === undefined && h === undefined) {
+ w = 1;
+ h = 1;
+ }
+
+ // if the section does not overlap the canvas
+ if(x + w < 0 || y + h < 0 || x > this.width || y > this.height){
+ return [0, 0, 0, 255];
+ }
+
+ var ctx = this._pInst || this;
+
+ var pd = ctx._pixelDensity;
+
+ this.loadPixels.call(ctx);
+
+ // round down to get integer numbers
+ x = Math.floor(x);
+ y = Math.floor(y);
+
+ if (w === 1 && h === 1){
+
+ return [
+ ctx.pixels[pd*4*(y*this.width+x)],
+ ctx.pixels[pd*(4*(y*this.width+x)+1)],
+ ctx.pixels[pd*(4*(y*this.width+x)+2)],
+ ctx.pixels[pd*(4*(y*this.width+x)+3)]
+ ];
+ } else {
+ var sx = x * pd;
+ var sy = y * pd;
+ //auto constrain the width and height to
+ //dimensions of the source image
+ var dw = Math.min(w, ctx.width);
+ var dh = Math.min(h, ctx.height);
+ var sw = dw * pd;
+ var sh = dh * pd;
+
+ var region = new p5.Image(dw, dh);
+ region.canvas.getContext('2d').drawImage(this.canvas, sx, sy, sw, sh,
+ 0, 0, dw, dh);
+
+ return region;
+ }
+};
+
+p5.Renderer2D.prototype.loadPixels = function () {
+ var pd = this._pixelDensity || this._pInst._pixelDensity;
+ var w = this.width * pd;
+ var h = this.height * pd;
+ var imageData = this.drawingContext.getImageData(0, 0, w, h);
+ // @todo this should actually set pixels per object, so diff buffers can
+ // have diff pixel arrays.
+ if (this._pInst) {
+ this._pInst._setProperty('imageData', imageData);
+ this._pInst._setProperty('pixels', imageData.data);
+ } else { // if called by p5.Image
+ this._setProperty('imageData', imageData);
+ this._setProperty('pixels', imageData.data);
+ }
+};
+
+p5.Renderer2D.prototype.set = function (x, y, imgOrCol) {
+ // round down to get integer numbers
+ x = Math.floor(x);
+ y = Math.floor(y);
+ if (imgOrCol instanceof p5.Image) {
+ this.drawingContext.save();
+ this.drawingContext.setTransform(1, 0, 0, 1, 0, 0);
+ this.drawingContext.scale(this._pInst._pixelDensity,
+ this._pInst._pixelDensity);
+ this.drawingContext.drawImage(imgOrCol.canvas, x, y);
+ this.loadPixels.call(this._pInst);
+ this.drawingContext.restore();
+ } else {
+ var ctx = this._pInst || this;
+ var r = 0, g = 0, b = 0, a = 0;
+ var idx = 4*((y * ctx._pixelDensity) *
+ (this.width * ctx._pixelDensity) + (x * ctx._pixelDensity));
+ if (!ctx.imageData) {
+ ctx.loadPixels.call(ctx);
+ }
+ if (typeof imgOrCol === 'number') {
+ if (idx < ctx.pixels.length) {
+ r = imgOrCol;
+ g = imgOrCol;
+ b = imgOrCol;
+ a = 255;
+ //this.updatePixels.call(this);
+ }
+ }
+ else if (imgOrCol instanceof Array) {
+ if (imgOrCol.length < 4) {
+ throw new Error('pixel array must be of the form [R, G, B, A]');
+ }
+ if (idx < ctx.pixels.length) {
+ r = imgOrCol[0];
+ g = imgOrCol[1];
+ b = imgOrCol[2];
+ a = imgOrCol[3];
+ //this.updatePixels.call(this);
+ }
+ } else if (imgOrCol instanceof p5.Color) {
+ if (idx < ctx.pixels.length) {
+ r = imgOrCol.levels[0];
+ g = imgOrCol.levels[1];
+ b = imgOrCol.levels[2];
+ a = imgOrCol.levels[3];
+ //this.updatePixels.call(this);
+ }
+ }
+ // loop over pixelDensity * pixelDensity
+ for (var i = 0; i < ctx._pixelDensity; i++) {
+ for (var j = 0; j < ctx._pixelDensity; j++) {
+ // loop over
+ idx = 4*((y * ctx._pixelDensity + j) * this.width *
+ ctx._pixelDensity + (x * ctx._pixelDensity + i));
+ ctx.pixels[idx] = r;
+ ctx.pixels[idx+1] = g;
+ ctx.pixels[idx+2] = b;
+ ctx.pixels[idx+3] = a;
+ }
+ }
+ }
+};
+
+p5.Renderer2D.prototype.updatePixels = function (x, y, w, h) {
+ var pd = this._pixelDensity || this._pInst._pixelDensity;
+ if (x === undefined &&
+ y === undefined &&
+ w === undefined &&
+ h === undefined) {
+ x = 0;
+ y = 0;
+ w = this.width;
+ h = this.height;
+ }
+ w *= pd;
+ h *= pd;
+
+ if (this._pInst) {
+ this.drawingContext.putImageData(this._pInst.imageData, x, y, 0, 0, w, h);
+ } else {
+ this.drawingContext.putImageData(this.imageData, x, y, 0, 0, w, h);
+ }
+};
+
+//////////////////////////////////////////////
+// SHAPE | 2D Primitives
+//////////////////////////////////////////////
+
+/**
+ * Generate a cubic Bezier representing an arc on the unit circle of total
+ * angle `size` radians, beginning `start` radians above the x-axis. Up to
+ * four of these curves are combined to make a full arc.
+ *
+ * See www.joecridge.me/bezier.pdf for an explanation of the method.
+ */
+p5.Renderer2D.prototype._acuteArcToBezier =
+ function _acuteArcToBezier(start, size) {
+ // Evauate constants.
+ var alpha = size / 2.0,
+ cos_alpha = Math.cos(alpha),
+ sin_alpha = Math.sin(alpha),
+ cot_alpha = 1.0 / Math.tan(alpha),
+ phi = start + alpha, // This is how far the arc needs to be rotated.
+ cos_phi = Math.cos(phi),
+ sin_phi = Math.sin(phi),
+ lambda = (4.0 - cos_alpha) / 3.0,
+ mu = sin_alpha + (cos_alpha - lambda) * cot_alpha;
+
+ // Return rotated waypoints.
+ return {
+ ax: Math.cos(start),
+ ay: Math.sin(start),
+ bx: lambda * cos_phi + mu * sin_phi,
+ by: lambda * sin_phi - mu * cos_phi,
+ cx: lambda * cos_phi - mu * sin_phi,
+ cy: lambda * sin_phi + mu * cos_phi,
+ dx: Math.cos(start + size),
+ dy: Math.sin(start + size)
+ };
+};
+
+p5.Renderer2D.prototype.arc =
+ function(x, y, w, h, start, stop, mode) {
+ var ctx = this.drawingContext;
+ var vals = canvas.arcModeAdjust(x, y, w, h, this._ellipseMode);
+ var rx = vals.w / 2.0;
+ var ry = vals.h / 2.0;
+ var epsilon = 0.00001; // Smallest visible angle on displays up to 4K.
+ var arcToDraw = 0;
+ var curves = [];
+
+ // Create curves
+ while(stop - start > epsilon) {
+ arcToDraw = Math.min(stop - start, constants.HALF_PI);
+ curves.push(this._acuteArcToBezier(start, arcToDraw));
+ start += arcToDraw;
+ }
+
+ // Fill curves
+ if (this._doFill) {
+ ctx.beginPath();
+ curves.forEach(function (curve, index) {
+ if (index === 0) {
+ ctx.moveTo(vals.x + curve.ax * rx, vals.y + curve.ay * ry);
+ }
+ ctx.bezierCurveTo(vals.x + curve.bx * rx, vals.y + curve.by * ry,
+ vals.x + curve.cx * rx, vals.y + curve.cy * ry,
+ vals.x + curve.dx * rx, vals.y + curve.dy * ry);
+ });
+ if (mode === constants.PIE || mode == null) {
+ ctx.lineTo(vals.x, vals.y);
+ }
+ ctx.closePath();
+ ctx.fill();
+ }
+
+ // Stroke curves
+ if (this._doStroke) {
+ ctx.beginPath();
+ curves.forEach(function (curve, index) {
+ if (index === 0) {
+ ctx.moveTo(vals.x + curve.ax * rx, vals.y + curve.ay * ry);
+ }
+ ctx.bezierCurveTo(vals.x + curve.bx * rx, vals.y + curve.by * ry,
+ vals.x + curve.cx * rx, vals.y + curve.cy * ry,
+ vals.x + curve.dx * rx, vals.y + curve.dy * ry);
+ });
+ if (mode === constants.PIE) {
+ ctx.lineTo(vals.x, vals.y);
+ ctx.closePath();
+ } else if (mode === constants.CHORD) {
+ ctx.closePath();
+ }
+ ctx.stroke();
+ }
+ return this;
+};
+
+p5.Renderer2D.prototype.ellipse = function(x, y, w, h) {
+ var ctx = this.drawingContext;
+ var doFill = this._doFill, doStroke = this._doStroke;
+ if (doFill && !doStroke) {
+ if(ctx.fillStyle === styleEmpty) {
+ return this;
+ }
+ } else if (!doFill && doStroke) {
+ if(ctx.strokeStyle === styleEmpty) {
+ return this;
+ }
+ }
+ var vals = canvas.modeAdjust(x, y, w, h, this._ellipseMode);
+ var kappa = 0.5522847498,
+ ox = (vals.w / 2) * kappa, // control point offset horizontal
+ oy = (vals.h / 2) * kappa, // control point offset vertical
+ xe = vals.x + vals.w, // x-end
+ ye = vals.y + vals.h, // y-end
+ xm = vals.x + vals.w / 2, // x-middle
+ ym = vals.y + vals.h / 2; // y-middle
+ ctx.beginPath();
+ ctx.moveTo(vals.x, ym);
+ ctx.bezierCurveTo(vals.x, ym - oy, xm - ox, vals.y, xm, vals.y);
+ ctx.bezierCurveTo(xm + ox, vals.y, xe, ym - oy, xe, ym);
+ ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
+ ctx.bezierCurveTo(xm - ox, ye, vals.x, ym + oy, vals.x, ym);
+ ctx.closePath();
+ if (doFill) {
+ ctx.fill();
+ }
+ if (doStroke) {
+ ctx.stroke();
+ }
+};
+
+p5.Renderer2D.prototype.line = function(x1, y1, x2, y2) {
+ var ctx = this.drawingContext;
+ if (!this._doStroke) {
+ return this;
+ } else if(ctx.strokeStyle === styleEmpty){
+ return this;
+ }
+ // Translate the line by (0.5, 0.5) to draw it crisp
+ if (ctx.lineWidth % 2 === 1) {
+ ctx.translate(0.5, 0.5);
+ }
+ ctx.beginPath();
+ ctx.moveTo(x1, y1);
+ ctx.lineTo(x2, y2);
+ ctx.stroke();
+ if (ctx.lineWidth % 2 === 1) {
+ ctx.translate(-0.5, -0.5);
+ }
+ return this;
+};
+
+p5.Renderer2D.prototype.point = function(x, y) {
+ var ctx = this.drawingContext;
+ var s = ctx.strokeStyle;
+ var f = ctx.fillStyle;
+ if (!this._doStroke) {
+ return this;
+ } else if(ctx.strokeStyle === styleEmpty){
+ return this;
+ }
+ x = Math.round(x);
+ y = Math.round(y);
+ ctx.fillStyle = s;
+ if (ctx.lineWidth > 1) {
+ ctx.beginPath();
+ ctx.arc(
+ x,
+ y,
+ ctx.lineWidth / 2,
+ 0,
+ constants.TWO_PI,
+ false
+ );
+ ctx.fill();
+ } else {
+ ctx.fillRect(x, y, 1, 1);
+ }
+ ctx.fillStyle = f;
+};
+
+p5.Renderer2D.prototype.quad =
+ function(x1, y1, x2, y2, x3, y3, x4, y4) {
+ var ctx = this.drawingContext;
+ var doFill = this._doFill, doStroke = this._doStroke;
+ if (doFill && !doStroke) {
+ if(ctx.fillStyle === styleEmpty) {
+ return this;
+ }
+ } else if (!doFill && doStroke) {
+ if(ctx.strokeStyle === styleEmpty) {
+ return this;
+ }
+ }
+ ctx.beginPath();
+ ctx.moveTo(x1, y1);
+ ctx.lineTo(x2, y2);
+ ctx.lineTo(x3, y3);
+ ctx.lineTo(x4, y4);
+ ctx.closePath();
+ if (doFill) {
+ ctx.fill();
+ }
+ if (doStroke) {
+ ctx.stroke();
+ }
+ return this;
+};
+
+p5.Renderer2D.prototype.rect = function(x, y, w, h, tl, tr, br, bl) {
+ var ctx = this.drawingContext;
+ var doFill = this._doFill, doStroke = this._doStroke;
+ if (doFill && !doStroke) {
+ if(ctx.fillStyle === styleEmpty) {
+ return this;
+ }
+ } else if (!doFill && doStroke) {
+ if(ctx.strokeStyle === styleEmpty) {
+ return this;
+ }
+ }
+ var vals = canvas.modeAdjust(x, y, w, h, this._rectMode);
+ // Translate the line by (0.5, 0.5) to draw a crisp rectangle border
+ if (this._doStroke && ctx.lineWidth % 2 === 1) {
+ ctx.translate(0.5, 0.5);
+ }
+ ctx.beginPath();
+
+ if (typeof tl === 'undefined') {
+ // No rounded corners
+ ctx.rect(vals.x, vals.y, vals.w, vals.h);
+ } else {
+ // At least one rounded corner
+ // Set defaults when not specified
+ if (typeof tr === 'undefined') { tr = tl; }
+ if (typeof br === 'undefined') { br = tr; }
+ if (typeof bl === 'undefined') { bl = br; }
+
+ // Cache and compute several values
+ var _x = vals.x;
+ var _y = vals.y;
+ var _w = vals.w;
+ var _h = vals.h;
+ var hw = _w / 2;
+ var hh = _h / 2;
+
+ // Clip radii
+ if (_w < 2 * tl) { tl = hw; }
+ if (_h < 2 * tl) { tl = hh; }
+ if (_w < 2 * tr) { tr = hw; }
+ if (_h < 2 * tr) { tr = hh; }
+ if (_w < 2 * br) { br = hw; }
+ if (_h < 2 * br) { br = hh; }
+ if (_w < 2 * bl) { bl = hw; }
+ if (_h < 2 * bl) { bl = hh; }
+
+ // Draw shape
+ ctx.beginPath();
+ ctx.moveTo(_x + tl, _y);
+ ctx.arcTo(_x + _w, _y, _x + _w, _y + _h, tr);
+ ctx.arcTo(_x + _w, _y + _h, _x, _y + _h, br);
+ ctx.arcTo(_x, _y + _h, _x, _y, bl);
+ ctx.arcTo(_x, _y, _x + _w, _y, tl);
+ ctx.closePath();
+ }
+ if (this._doFill) {
+ ctx.fill();
+ }
+ if (this._doStroke) {
+ ctx.stroke();
+ }
+ if (this._doStroke && ctx.lineWidth % 2 === 1) {
+ ctx.translate(-0.5, -0.5);
+ }
+ return this;
+};
+
+p5.Renderer2D.prototype.triangle = function(x1, y1, x2, y2, x3, y3) {
+ var ctx = this.drawingContext;
+ var doFill = this._doFill, doStroke = this._doStroke;
+ if (doFill && !doStroke) {
+ if(ctx.fillStyle === styleEmpty) {
+ return this;
+ }
+ } else if (!doFill && doStroke) {
+ if(ctx.strokeStyle === styleEmpty) {
+ return this;
+ }
+ }
+ ctx.beginPath();
+ ctx.moveTo(x1, y1);
+ ctx.lineTo(x2, y2);
+ ctx.lineTo(x3, y3);
+ ctx.closePath();
+ if (doFill) {
+ ctx.fill();
+ }
+ if (doStroke) {
+ ctx.stroke();
+ }
+};
+
+p5.Renderer2D.prototype.endShape =
+function (mode, vertices, isCurve, isBezier,
+ isQuadratic, isContour, shapeKind) {
+ if (vertices.length === 0) {
+ return this;
+ }
+ if (!this._doStroke && !this._doFill) {
+ return this;
+ }
+ var closeShape = mode === constants.CLOSE;
+ var v;
+ if (closeShape && !isContour) {
+ vertices.push(vertices[0]);
+ }
+ var i, j;
+ var numVerts = vertices.length;
+ if (isCurve && (shapeKind === constants.POLYGON || shapeKind === null)) {
+ if (numVerts > 3) {
+ var b = [], s = 1 - this._curveTightness;
+ this.drawingContext.beginPath();
+ this.drawingContext.moveTo(vertices[1][0], vertices[1][1]);
+ for (i = 1; i + 2 < numVerts; i++) {
+ v = vertices[i];
+ b[0] = [
+ v[0],
+ v[1]
+ ];
+ b[1] = [
+ v[0] + (s * vertices[i + 1][0] - s * vertices[i - 1][0]) / 6,
+ v[1] + (s * vertices[i + 1][1] - s * vertices[i - 1][1]) / 6
+ ];
+ b[2] = [
+ vertices[i + 1][0] +
+ (s * vertices[i][0]-s * vertices[i + 2][0]) / 6,
+ vertices[i + 1][1]+(s * vertices[i][1] - s*vertices[i + 2][1]) / 6
+ ];
+ b[3] = [
+ vertices[i + 1][0],
+ vertices[i + 1][1]
+ ];
+ this.drawingContext.bezierCurveTo(b[1][0],b[1][1],
+ b[2][0],b[2][1],b[3][0],b[3][1]);
+ }
+ if (closeShape) {
+ this.drawingContext.lineTo(vertices[i + 1][0], vertices[i + 1][1]);
+ }
+ this._doFillStrokeClose();
+ }
+ } else if (isBezier&&(shapeKind===constants.POLYGON ||shapeKind === null)) {
+ this.drawingContext.beginPath();
+ for (i = 0; i < numVerts; i++) {
+ if (vertices[i].isVert) {
+ if (vertices[i].moveTo) {
+ this.drawingContext.moveTo(vertices[i][0], vertices[i][1]);
+ } else {
+ this.drawingContext.lineTo(vertices[i][0], vertices[i][1]);
+ }
+ } else {
+ this.drawingContext.bezierCurveTo(vertices[i][0], vertices[i][1],
+ vertices[i][2], vertices[i][3], vertices[i][4], vertices[i][5]);
+ }
+ }
+ this._doFillStrokeClose();
+ } else if (isQuadratic &&
+ (shapeKind === constants.POLYGON || shapeKind === null)) {
+ this.drawingContext.beginPath();
+ for (i = 0; i < numVerts; i++) {
+ if (vertices[i].isVert) {
+ if (vertices[i].moveTo) {
+ this.drawingContext.moveTo([0], vertices[i][1]);
+ } else {
+ this.drawingContext.lineTo(vertices[i][0], vertices[i][1]);
+ }
+ } else {
+ this.drawingContext.quadraticCurveTo(vertices[i][0], vertices[i][1],
+ vertices[i][2], vertices[i][3]);
+ }
+ }
+ this._doFillStrokeClose();
+ } else {
+ if (shapeKind === constants.POINTS) {
+ for (i = 0; i < numVerts; i++) {
+ v = vertices[i];
+ if (this._doStroke) {
+ this._pInst.stroke(v[6]);
+ }
+ this._pInst.point(v[0], v[1]);
+ }
+ } else if (shapeKind === constants.LINES) {
+ for (i = 0; i + 1 < numVerts; i += 2) {
+ v = vertices[i];
+ if (this._doStroke) {
+ this._pInst.stroke(vertices[i + 1][6]);
+ }
+ this._pInst.line(v[0], v[1], vertices[i + 1][0], vertices[i + 1][1]);
+ }
+ } else if (shapeKind === constants.TRIANGLES) {
+ for (i = 0; i + 2 < numVerts; i += 3) {
+ v = vertices[i];
+ this.drawingContext.beginPath();
+ this.drawingContext.moveTo(v[0], v[1]);
+ this.drawingContext.lineTo(vertices[i + 1][0], vertices[i + 1][1]);
+ this.drawingContext.lineTo(vertices[i + 2][0], vertices[i + 2][1]);
+ this.drawingContext.lineTo(v[0], v[1]);
+ if (this._doFill) {
+ this._pInst.fill(vertices[i + 2][5]);
+ this.drawingContext.fill();
+ }
+ if (this._doStroke) {
+ this._pInst.stroke(vertices[i + 2][6]);
+ this.drawingContext.stroke();
+ }
+ this.drawingContext.closePath();
+ }
+ } else if (shapeKind === constants.TRIANGLE_STRIP) {
+ for (i = 0; i + 1 < numVerts; i++) {
+ v = vertices[i];
+ this.drawingContext.beginPath();
+ this.drawingContext.moveTo(vertices[i + 1][0], vertices[i + 1][1]);
+ this.drawingContext.lineTo(v[0], v[1]);
+ if (this._doStroke) {
+ this._pInst.stroke(vertices[i + 1][6]);
+ }
+ if (this._doFill) {
+ this._pInst.fill(vertices[i + 1][5]);
+ }
+ if (i + 2 < numVerts) {
+ this.drawingContext.lineTo(vertices[i + 2][0], vertices[i + 2][1]);
+ if (this._doStroke) {
+ this._pInst.stroke(vertices[i + 2][6]);
+ }
+ if (this._doFill) {
+ this._pInst.fill(vertices[i + 2][5]);
+ }
+ }
+ this._doFillStrokeClose();
+ }
+ } else if (shapeKind === constants.TRIANGLE_FAN) {
+ if (numVerts > 2) {
+ this.drawingContext.beginPath();
+ this.drawingContext.moveTo(vertices[0][0], vertices[0][1]);
+ this.drawingContext.lineTo(vertices[1][0], vertices[1][1]);
+ this.drawingContext.lineTo(vertices[2][0], vertices[2][1]);
+ if (this._doFill) {
+ this._pInst.fill(vertices[2][5]);
+ }
+ if (this._doStroke) {
+ this._pInst.stroke(vertices[2][6]);
+ }
+ this._doFillStrokeClose();
+ for (i = 3; i < numVerts; i++) {
+ v = vertices[i];
+ this.drawingContext.beginPath();
+ this.drawingContext.moveTo(vertices[0][0], vertices[0][1]);
+ this.drawingContext.lineTo(vertices[i - 1][0], vertices[i - 1][1]);
+ this.drawingContext.lineTo(v[0], v[1]);
+ if (this._doFill) {
+ this._pInst.fill(v[5]);
+ }
+ if (this._doStroke) {
+ this._pInst.stroke(v[6]);
+ }
+ this._doFillStrokeClose();
+ }
+ }
+ } else if (shapeKind === constants.QUADS) {
+ for (i = 0; i + 3 < numVerts; i += 4) {
+ v = vertices[i];
+ this.drawingContext.beginPath();
+ this.drawingContext.moveTo(v[0], v[1]);
+ for (j = 1; j < 4; j++) {
+ this.drawingContext.lineTo(vertices[i + j][0], vertices[i + j][1]);
+ }
+ this.drawingContext.lineTo(v[0], v[1]);
+ if (this._doFill) {
+ this._pInst.fill(vertices[i + 3][5]);
+ }
+ if (this._doStroke) {
+ this._pInst.stroke(vertices[i + 3][6]);
+ }
+ this._doFillStrokeClose();
+ }
+ } else if (shapeKind === constants.QUAD_STRIP) {
+ if (numVerts > 3) {
+ for (i = 0; i + 1 < numVerts; i += 2) {
+ v = vertices[i];
+ this.drawingContext.beginPath();
+ if (i + 3 < numVerts) {
+ this.drawingContext.moveTo(vertices[i + 2][0], vertices[i+2][1]);
+ this.drawingContext.lineTo(v[0], v[1]);
+ this.drawingContext.lineTo(vertices[i + 1][0], vertices[i+1][1]);
+ this.drawingContext.lineTo(vertices[i + 3][0], vertices[i+3][1]);
+ if (this._doFill) {
+ this._pInst.fill(vertices[i + 3][5]);
+ }
+ if (this._doStroke) {
+ this._pInst.stroke(vertices[i + 3][6]);
+ }
+ } else {
+ this.drawingContext.moveTo(v[0], v[1]);
+ this.drawingContext.lineTo(vertices[i + 1][0], vertices[i+1][1]);
+ }
+ this._doFillStrokeClose();
+ }
+ }
+ } else {
+ this.drawingContext.beginPath();
+ this.drawingContext.moveTo(vertices[0][0], vertices[0][1]);
+ for (i = 1; i < numVerts; i++) {
+ v = vertices[i];
+ if (v.isVert) {
+ if (v.moveTo) {
+ this.drawingContext.moveTo(v[0], v[1]);
+ } else {
+ this.drawingContext.lineTo(v[0], v[1]);
+ }
+ }
+ }
+ this._doFillStrokeClose();
+ }
+ }
+ isCurve = false;
+ isBezier = false;
+ isQuadratic = false;
+ isContour = false;
+ if (closeShape) {
+ vertices.pop();
+ }
+ return this;
+};
+//////////////////////////////////////////////
+// SHAPE | Attributes
+//////////////////////////////////////////////
+
+p5.Renderer2D.prototype.noSmooth = function() {
+ if ('imageSmoothingEnabled' in this.drawingContext) {
+ this.drawingContext.imageSmoothingEnabled = false;
+ }
+ else if ('mozImageSmoothingEnabled' in this.drawingContext) {
+ this.drawingContext.mozImageSmoothingEnabled = false;
+ }
+ else if ('webkitImageSmoothingEnabled' in this.drawingContext) {
+ this.drawingContext.webkitImageSmoothingEnabled = false;
+ }
+ else if ('msImageSmoothingEnabled' in this.drawingContext) {
+ this.drawingContext.msImageSmoothingEnabled = false;
+ }
+ return this;
+};
+
+p5.Renderer2D.prototype.smooth = function() {
+ if ('imageSmoothingEnabled' in this.drawingContext) {
+ this.drawingContext.imageSmoothingEnabled = true;
+ }
+ else if ('mozImageSmoothingEnabled' in this.drawingContext) {
+ this.drawingContext.mozImageSmoothingEnabled = true;
+ }
+ else if ('webkitImageSmoothingEnabled' in this.drawingContext) {
+ this.drawingContext.webkitImageSmoothingEnabled = true;
+ }
+ else if ('msImageSmoothingEnabled' in this.drawingContext) {
+ this.drawingContext.msImageSmoothingEnabled = true;
+ }
+ return this;
+};
+
+p5.Renderer2D.prototype.strokeCap = function(cap) {
+ if (cap === constants.ROUND ||
+ cap === constants.SQUARE ||
+ cap === constants.PROJECT) {
+ this.drawingContext.lineCap = cap;
+ }
+ return this;
+};
+
+p5.Renderer2D.prototype.strokeJoin = function(join) {
+ if (join === constants.ROUND ||
+ join === constants.BEVEL ||
+ join === constants.MITER) {
+ this.drawingContext.lineJoin = join;
+ }
+ return this;
+};
+
+p5.Renderer2D.prototype.strokeWeight = function(w) {
+ if (typeof w === 'undefined' || w === 0) {
+ // hack because lineWidth 0 doesn't work
+ this.drawingContext.lineWidth = 0.0001;
+ } else {
+ this.drawingContext.lineWidth = w;
+ }
+ return this;
+};
+
+p5.Renderer2D.prototype._getFill = function(){
+ return this.drawingContext.fillStyle;
+};
+
+p5.Renderer2D.prototype._getStroke = function(){
+ return this.drawingContext.strokeStyle;
+};
+
+//////////////////////////////////////////////
+// SHAPE | Curves
+//////////////////////////////////////////////
+p5.Renderer2D.prototype.bezier = function (x1, y1, x2, y2, x3, y3, x4, y4) {
+ this._pInst.beginShape();
+ this._pInst.vertex(x1, y1);
+ this._pInst.bezierVertex(x2, y2, x3, y3, x4, y4);
+ this._pInst.endShape();
+ return this;
+};
+
+p5.Renderer2D.prototype.curve = function (x1, y1, x2, y2, x3, y3, x4, y4) {
+ this._pInst.beginShape();
+ this._pInst.curveVertex(x1, y1);
+ this._pInst.curveVertex(x2, y2);
+ this._pInst.curveVertex(x3, y3);
+ this._pInst.curveVertex(x4, y4);
+ this._pInst.endShape();
+ return this;
+};
+
+//////////////////////////////////////////////
+// SHAPE | Vertex
+//////////////////////////////////////////////
+
+p5.Renderer2D.prototype._doFillStrokeClose = function () {
+ if (this._doFill) {
+ this.drawingContext.fill();
+ }
+ if (this._doStroke) {
+ this.drawingContext.stroke();
+ }
+ this.drawingContext.closePath();
+};
+
+//////////////////////////////////////////////
+// TRANSFORM
+//////////////////////////////////////////////
+
+p5.Renderer2D.prototype.applyMatrix =
+function(n00, n01, n02, n10, n11, n12) {
+ this.drawingContext.transform(n00, n01, n02, n10, n11, n12);
+};
+
+p5.Renderer2D.prototype.resetMatrix = function() {
+ this.drawingContext.setTransform(1, 0, 0, 1, 0, 0);
+ this.drawingContext.scale(this._pInst._pixelDensity,
+ this._pInst._pixelDensity);
+ return this;
+};
+
+p5.Renderer2D.prototype.rotate = function(r) {
+ this.drawingContext.rotate(r);
+};
+
+p5.Renderer2D.prototype.scale = function(x,y) {
+ this.drawingContext.scale(x, y);
+ return this;
+};
+
+p5.Renderer2D.prototype.shearX = function(angle) {
+ if (this._pInst._angleMode === constants.DEGREES) {
+ angle = this._pInst.radians(angle);
+ }
+ this.drawingContext.transform(1, 0, this._pInst.tan(angle), 1, 0, 0);
+ return this;
+};
+
+p5.Renderer2D.prototype.shearY = function(angle) {
+ if (this._pInst._angleMode === constants.DEGREES) {
+ angle = this._pInst.radians(angle);
+ }
+ this.drawingContext.transform(1, this._pInst.tan(angle), 0, 1, 0, 0);
+ return this;
+};
+
+p5.Renderer2D.prototype.translate = function(x, y) {
+ this.drawingContext.translate(x, y);
+ return this;
+};
+
+//////////////////////////////////////////////
+// TYPOGRAPHY
+//
+//////////////////////////////////////////////
+
+p5.Renderer2D.prototype.text = function (str, x, y, maxWidth, maxHeight) {
+
+ var p = this._pInst, cars, n, ii, jj, line, testLine,
+ testWidth, words, totalHeight, baselineHacked,
+ finalMaxHeight = Number.MAX_VALUE;
+
+ // baselineHacked: (HACK)
+ // A temporary fix to conform to Processing's implementation
+ // of BASELINE vertical alignment in a bounding box
+
+ if (!(this._doFill || this._doStroke)) {
+ return;
+ }
+
+ if (typeof str !== 'string') {
+ str = str.toString();
+ }
+
+ str = str.replace(/(\t)/g, ' ');
+ cars = str.split('\n');
+
+ if (typeof maxWidth !== 'undefined') {
+
+ totalHeight = 0;
+ for (ii = 0; ii < cars.length; ii++) {
+ line = '';
+ words = cars[ii].split(' ');
+ for (n = 0; n < words.length; n++) {
+ testLine = line + words[n] + ' ';
+ testWidth = this.textWidth(testLine);
+ if (testWidth > maxWidth) {
+ line = words[n] + ' ';
+ totalHeight += p.textLeading();
+ } else {
+ line = testLine;
+ }
+ }
+ }
+
+ if (this._rectMode === constants.CENTER) {
+
+ x -= maxWidth / 2;
+ y -= maxHeight / 2;
+ }
+
+ switch (this.drawingContext.textAlign) {
+
+ case constants.CENTER:
+ x += maxWidth / 2;
+ break;
+ case constants.RIGHT:
+ x += maxWidth;
+ break;
+ }
+
+ if (typeof maxHeight !== 'undefined') {
+
+ switch (this.drawingContext.textBaseline) {
+ case constants.BOTTOM:
+ y += (maxHeight - totalHeight);
+ break;
+ case constants._CTX_MIDDLE:
+ y += (maxHeight - totalHeight) / 2;
+ break;
+ case constants.BASELINE:
+ baselineHacked = true;
+ this.drawingContext.textBaseline = constants.TOP;
+ break;
+ }
+
+ // remember the max-allowed y-position for any line (fix to #928)
+ finalMaxHeight = (y + maxHeight) - p.textAscent();
+ }
+
+ for (ii = 0; ii < cars.length; ii++) {
+
+ line = '';
+ words = cars[ii].split(' ');
+ for (n = 0; n < words.length; n++) {
+ testLine = line + words[n] + ' ';
+ testWidth = this.textWidth(testLine);
+ if (testWidth > maxWidth && line.length > 0) {
+ this._renderText(p, line, x, y, finalMaxHeight);
+ line = words[n] + ' ';
+ y += p.textLeading();
+ } else {
+ line = testLine;
+ }
+ }
+
+ this._renderText(p, line, x, y, finalMaxHeight);
+ y += p.textLeading();
+ }
+ }
+ else {
+ for (jj = 0; jj < cars.length; jj++) {
+
+ this._renderText(p, cars[jj], x, y, finalMaxHeight);
+ y += p.textLeading();
+ }
+ }
+
+ if (baselineHacked) {
+ this.drawingContext.textBaseline = constants.BASELINE;
+ }
+
+ return p;
+};
+
+p5.Renderer2D.prototype._renderText = function(p, line, x, y, maxY) {
+
+ if (y >= maxY) {
+ return; // don't render lines beyond our maxY position
+ }
+
+ p.push(); // fix to #803
+
+ if (!this._isOpenType()) { // a system/browser font
+
+ // no stroke unless specified by user
+ if (this._doStroke && this._strokeSet) {
+
+ this.drawingContext.strokeText(line, x, y);
+ }
+
+ if (this._doFill) {
+
+ // if fill hasn't been set by user, use default text fill
+ this.drawingContext.fillStyle = this._fillSet ?
+ this.drawingContext.fillStyle : constants._DEFAULT_TEXT_FILL;
+
+ this.drawingContext.fillText(line, x, y);
+ }
+ }
+ else { // an opentype font, let it handle the rendering
+
+ this._textFont._renderPath(line, x, y, { renderer: this });
+ }
+
+ p.pop();
+
+ return p;
+};
+
+p5.Renderer2D.prototype.textWidth = function(s) {
+
+ if (this._isOpenType()) {
+
+ return this._textFont._textWidth(s);
+ }
+
+ return this.drawingContext.measureText(s).width;
+};
+
+p5.Renderer2D.prototype.textAlign = function(h, v) {
+
+ if (arguments.length) {
+
+ if (h === constants.LEFT ||
+ h === constants.RIGHT ||
+ h === constants.CENTER) {
+
+ this.drawingContext.textAlign = h;
+ }
+
+ if (v === constants.TOP ||
+ v === constants.BOTTOM ||
+ v === constants.CENTER ||
+ v === constants.BASELINE) {
+
+ if (v === constants.CENTER) {
+ this.drawingContext.textBaseline = constants._CTX_MIDDLE;
+ } else {
+ this.drawingContext.textBaseline = v;
+ }
+ }
+
+ return this._pInst;
+
+ } else {
+
+ var valign = this.drawingContext.textBaseline;
+
+ if (valign === constants._CTX_MIDDLE) {
+
+ valign = constants.CENTER;
+ }
+
+ return {
+
+ horizontal: this.drawingContext.textAlign,
+ vertical: valign
+ };
+ }
+};
+
+p5.Renderer2D.prototype._applyTextProperties = function() {
+
+ var font, p = this._pInst;
+
+ this._setProperty('_textAscent', null);
+ this._setProperty('_textDescent', null);
+
+ font = this._textFont;
+
+ if (this._isOpenType()) {
+
+ font = this._textFont.font.familyName;
+ this._setProperty('_textStyle', this._textFont.font.styleName);
+ }
+
+ this.drawingContext.font = this._textStyle + ' ' +
+ this._textSize + 'px ' + font;
+
+ return p;
+};
+
+
+//////////////////////////////////////////////
+// STRUCTURE
+//////////////////////////////////////////////
+
+p5.Renderer2D.prototype.push = function() {
+ this.drawingContext.save();
+};
+
+p5.Renderer2D.prototype.pop = function() {
+ this.drawingContext.restore();
+};
+
+module.exports = p5.Renderer2D;
+
+},{"../image/filters":65,"./canvas":46,"./constants":47,"./core":48,"./p5.Renderer":54}],56:[function(_dereq_,module,exports){
+/**
+ * @module Rendering
+ * @submodule Rendering
+ * @for p5
+ */
+
+var p5 = _dereq_('./core');
+var constants = _dereq_('./constants');
+_dereq_('./p5.Graphics');
+_dereq_('./p5.Renderer2D');
+_dereq_('../3d/p5.Renderer3D');
+var defaultId = 'defaultCanvas0'; // this gets set again in createCanvas
+
+/**
+ * Creates a canvas element in the document, and sets the dimensions of it
+ * in pixels. This method should be called only once at the start of setup.
+ * Calling createCanvas more than once in a sketch will result in very
+ * unpredicable behavior. If you want more than one drawing canvas
+ * you could use createGraphics (hidden by default but it can be shown).
+ * <br><br>
+ * The system variables width and height are set by the parameters passed
+ * to this function. If createCanvas() is not used, the window will be
+ * given a default size of 100x100 pixels.
+ *
+ * @method createCanvas
+ * @param {Number} w width of the canvas
+ * @param {Number} h height of the canvas
+ * @param {String} [renderer] 'p2d' | 'webgl'
+ * @return {Object} canvas generated
+ * @example
+ * <div>
+ * <code>
+ * function setup() {
+ * createCanvas(100, 50);
+ * background(153);
+ * line(0, 0, width, height);
+ * }
+ * </code>
+ * </div>
+ */
+
+p5.prototype.createCanvas = function(w, h, renderer) {
+ //optional: renderer, otherwise defaults to p2d
+ var r = renderer || constants.P2D;
+ var isDefault, c;
+
+ //4th arg (isDefault) used when called onLoad,
+ //otherwise hidden to the public api
+ if(arguments[3]){
+ isDefault =
+ (typeof arguments[3] === 'boolean') ? arguments[3] : false;
+ }
+
+ if(r === constants.WEBGL){
+ c = document.getElementById(defaultId);
+ if(c){ //if defaultCanvas already exists
+ c.parentNode.removeChild(c); //replace the existing defaultCanvas
+ }
+ c = document.createElement('canvas');
+ c.id = defaultId;
+ }
+ else {
+ if (isDefault) {
+ c = document.createElement('canvas');
+ var i = 0;
+ while (document.getElementById('defaultCanvas'+i)) {
+ i++;
+ }
+ defaultId = 'defaultCanvas'+i;
+ c.id = defaultId;
+ } else { // resize the default canvas if new one is created
+ c = this.canvas;
+ }
+ }
+
+ // set to invisible if still in setup (to prevent flashing with manipulate)
+ if (!this._setupDone) {
+ c.className += ' p5_hidden'; // tag to show later
+ c.style.visibility='hidden';
+ }
+
+ if (this._userNode) { // user input node case
+ this._userNode.appendChild(c);
+ } else {
+ document.body.appendChild(c);
+ }
+
+
+
+ // Init our graphics renderer
+ //webgl mode
+ if (r === constants.WEBGL) {
+ this._setProperty('_renderer', new p5.Renderer3D(c, this, true));
+ this._isdefaultGraphics = true;
+ }
+ //P2D mode
+ else {
+ if (!this._isdefaultGraphics) {
+ this._setProperty('_renderer', new p5.Renderer2D(c, this, true));
+ this._isdefaultGraphics = true;
+ }
+ }
+ this._renderer.resize(w, h);
+ this._renderer._applyDefaults();
+ if (isDefault) { // only push once
+ this._elements.push(this._renderer);
+ }
+ return this._renderer;
+};
+
+/**
+ * Resizes the canvas to given width and height. The canvas will be cleared
+ * and draw will be called immediately, allowing the sketch to re-render itself
+ * in the resized canvas.
+ * @method resizeCanvas
+ * @example
+ * <div class="norender"><code>
+ * function setup() {
+ * createCanvas(windowWidth, windowHeight);
+ * }
+ *
+ * function draw() {
+ * background(0, 100, 200);
+ * }
+ *
+ * function windowResized() {
+ * resizeCanvas(windowWidth, windowHeight);
+ * }
+ * </code></div>
+ */
+p5.prototype.resizeCanvas = function (w, h, noRedraw) {
+ if (this._renderer) {
+
+ // save canvas properties
+ var props = {};
+ for (var key in this.drawingContext) {
+ var val = this.drawingContext[key];
+ if (typeof val !== 'object' && typeof val !== 'function') {
+ props[key] = val;
+ }
+ }
+ this._renderer.resize(w, h);
+ // reset canvas properties
+ for (var savedKey in props) {
+ this.drawingContext[savedKey] = props[savedKey];
+ }
+ if (!noRedraw) {
+ this.redraw();
+ }
+ }
+};
+
+
+/**
+ * Removes the default canvas for a p5 sketch that doesn't
+ * require a canvas
+ * @method noCanvas
+ * @example
+ * <div>
+ * <code>
+ * function setup() {
+ * noCanvas();
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.noCanvas = function() {
+ if (this.canvas) {
+ this.canvas.parentNode.removeChild(this.canvas);
+ }
+};
+
+/**
+ * Creates and returns a new p5.Renderer object. Use this class if you need
+ * to draw into an off-screen graphics buffer. The two parameters define the
+ * width and height in pixels.
+ *
+ * @method createGraphics
+ * @param {Number} w width of the offscreen graphics buffer
+ * @param {Number} h height of the offscreen graphics buffer
+ * @param {String} renderer either 'p2d' or 'webgl'.
+ * undefined defaults to p2d
+ * @return {Object} offscreen graphics buffer
+ * @example
+ * <div>
+ * <code>
+ * var pg;
+ * function setup() {
+ * createCanvas(100, 100);
+ * pg = createGraphics(100, 100);
+ * }
+ * function draw() {
+ * background(200);
+ * pg.background(100);
+ * pg.noStroke();
+ * pg.ellipse(pg.width/2, pg.height/2, 50, 50);
+ * image(pg, 50, 50);
+ * image(pg, 0, 0, 50, 50);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.createGraphics = function(w, h, renderer){
+ return new p5.Graphics(w, h, renderer, this);
+};
+
+/**
+ * Blends the pixels in the display window according to the defined mode.
+ * There is a choice of the following modes to blend the source pixels (A)
+ * with the ones of pixels already in the display window (B):
+ * <ul>
+ * <li><code>BLEND</code> - linear interpolation of colours: C =
+ * A*factor + B. This is the default blending mode.</li>
+ * <li><code>ADD</code> - sum of A and B</li>
+ * <li><code>DARKEST</code> - only the darkest colour succeeds: C =
+ * min(A*factor, B).</li>
+ * <li><code>LIGHTEST</code> - only the lightest colour succeeds: C =
+ * max(A*factor, B).</li>
+ * <li><code>DIFFERENCE</code> - subtract colors from underlying image.</li>
+ * <li><code>EXCLUSION</code> - similar to <code>DIFFERENCE</code>, but less
+ * extreme.</li>
+ * <li><code>MULTIPLY</code> - multiply the colors, result will always be
+ * darker.</li>
+ * <li><code>SCREEN</code> - opposite multiply, uses inverse values of the
+ * colors.</li>
+ * <li><code>REPLACE</code> - the pixels entirely replace the others and
+ * don't utilize alpha (transparency) values.</li>
+ * <li><code>OVERLAY</code> - mix of <code>MULTIPLY</code> and <code>SCREEN
+ * </code>. Multiplies dark values, and screens light values.</li>
+ * <li><code>HARD_LIGHT</code> - <code>SCREEN</code> when greater than 50%
+ * gray, <code>MULTIPLY</code> when lower.</li>
+ * <li><code>SOFT_LIGHT</code> - mix of <code>DARKEST</code> and
+ * <code>LIGHTEST</code>. Works like <code>OVERLAY</code>, but not as harsh.
+ * </li>
+ * <li><code>DODGE</code> - lightens light tones and increases contrast,
+ * ignores darks.</li>
+ * <li><code>BURN</code> - darker areas are applied, increasing contrast,
+ * ignores lights.</li>
+ * </ul>
+ *
+ * @method blendMode
+ * @param {String/Constant} mode blend mode to set for canvas
+ * @example
+ * <div>
+ * <code>
+ * blendMode(LIGHTEST);
+ * strokeWeight(30);
+ * stroke(80, 150, 255);
+ * line(25, 25, 75, 75);
+ * stroke(255, 50, 50);
+ * line(75, 25, 25, 75);
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * blendMode(MULTIPLY);
+ * strokeWeight(30);
+ * stroke(80, 150, 255);
+ * line(25, 25, 75, 75);
+ * stroke(255, 50, 50);
+ * line(75, 25, 25, 75);
+ * </code>
+ * </div>
+ */
+p5.prototype.blendMode = function(mode) {
+ if (mode === constants.BLEND || mode === constants.DARKEST ||
+ mode === constants.LIGHTEST || mode === constants.DIFFERENCE ||
+ mode === constants.MULTIPLY || mode === constants.EXCLUSION ||
+ mode === constants.SCREEN || mode === constants.REPLACE ||
+ mode === constants.OVERLAY || mode === constants.HARD_LIGHT ||
+ mode === constants.SOFT_LIGHT || mode === constants.DODGE ||
+ mode === constants.BURN || mode === constants.ADD ||
+ mode === constants.NORMAL) {
+ this._renderer.blendMode(mode);
+ } else {
+ throw new Error('Mode '+mode+' not recognized.');
+ }
+};
+
+module.exports = p5;
+
+},{"../3d/p5.Renderer3D":36,"./constants":47,"./core":48,"./p5.Graphics":53,"./p5.Renderer2D":55}],57:[function(_dereq_,module,exports){
+
+// requestAnim shim layer by Paul Irish
+window.requestAnimationFrame = (function(){
+ return window.requestAnimationFrame ||
+ window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.oRequestAnimationFrame ||
+ window.msRequestAnimationFrame ||
+ function(callback, element){
+ // should '60' here be framerate?
+ window.setTimeout(callback, 1000 / 60);
+ };
+})();
+
+// use window.performance() to get max fast and accurate time in milliseconds
+window.performance = window.performance || {};
+window.performance.now = (function(){
+ var load_date = Date.now();
+ return window.performance.now ||
+ window.performance.mozNow ||
+ window.performance.msNow ||
+ window.performance.oNow ||
+ window.performance.webkitNow ||
+ function () {
+ return Date.now() - load_date;
+ };
+})();
+
+/*
+// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
+// http://my.opera.com/emoller/blog/2011/12/20/
+// requestanimationframe-for-smart-er-animating
+// requestAnimationFrame polyfill by Erik Möller
+// fixes from Paul Irish and Tino Zijdel
+(function() {
+ var lastTime = 0;
+ var vendors = ['ms', 'moz', 'webkit', 'o'];
+ for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
+ window.requestAnimationFrame =
+ window[vendors[x]+'RequestAnimationFrame'];
+ window.cancelAnimationFrame =
+ window[vendors[x]+'CancelAnimationFrame'] ||
+ window[vendors[x]+'CancelRequestAnimationFrame'];
+ }
+
+ if (!window.requestAnimationFrame) {
+ window.requestAnimationFrame = function(callback, element) {
+ var currTime = new Date().getTime();
+ var timeToCall = Math.max(0, 16 - (currTime - lastTime));
+ var id = window.setTimeout(function()
+ { callback(currTime + timeToCall); }, timeToCall);
+ lastTime = currTime + timeToCall;
+ return id;
+ };
+ }
+
+ if (!window.cancelAnimationFrame) {
+ window.cancelAnimationFrame = function(id) {
+ clearTimeout(id);
+ };
+ }
+}());
+*/
+
+/**
+ * shim for Uint8ClampedArray.slice
+ * (allows arrayCopy to work with pixels[])
+ * with thanks to http://halfpapstudios.com/blog/tag/html5-canvas/
+ * Enumerable set to false to protect for...in from
+ * Uint8ClampedArray.prototype pollution.
+ */
+(function () {
+ 'use strict';
+ if (typeof Uint8ClampedArray !== 'undefined' &&
+ !Uint8ClampedArray.prototype.slice) {
+ Object.defineProperty(Uint8ClampedArray.prototype, 'slice', {
+ value: Array.prototype.slice,
+ writable: true, configurable: true, enumerable: false
+ });
+ }
+}());
+
+},{}],58:[function(_dereq_,module,exports){
+/**
+ * @module Structure
+ * @submodule Structure
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('./core');
+
+p5.prototype.exit = function() {
+ throw 'exit() not implemented, see remove()';
+};
+/**
+ * Stops p5.js from continuously executing the code within draw().
+ * If loop() is called, the code in draw() begins to run continuously again.
+ * If using noLoop() in setup(), it should be the last line inside the block.
+ * <br><br>
+ * When noLoop() is used, it's not possible to manipulate or access the
+ * screen inside event handling functions such as mousePressed() or
+ * keyPressed(). Instead, use those functions to call redraw() or loop(),
+ * which will run draw(), which can update the screen properly. This means
+ * that when noLoop() has been called, no drawing can happen, and functions
+ * like saveFrame() or loadPixels() may not be used.
+ * <br><br>
+ * Note that if the sketch is resized, redraw() will be called to update
+ * the sketch, even after noLoop() has been specified. Otherwise, the sketch
+ * would enter an odd state until loop() was called.
+ *
+ * @method noLoop
+ * @example
+ * <div><code>
+ * function setup() {
+ * createCanvas(100, 100);
+ * background(200);
+ * noLoop();
+ * }
+
+ * function draw() {
+ * line(10, 10, 90, 90);
+ * }
+ * </code></div>
+ *
+ * <div><code>
+ * var x = 0;
+ * function setup() {
+ * createCanvas(100, 100);
+ * }
+ *
+ * function draw() {
+ * background(204);
+ * x = x + 0.1;
+ * if (x > width) {
+ * x = 0;
+ * }
+ * line(x, 0, x, height);
+ * }
+ *
+ * function mousePressed() {
+ * noLoop();
+ * }
+ *
+ * function mouseReleased() {
+ * loop();
+ * }
+ * </code></div>
+ */
+p5.prototype.noLoop = function() {
+ this._loop = false;
+};
+/**
+ * By default, p5.js loops through draw() continuously, executing the code
+ * within it. However, the draw() loop may be stopped by calling noLoop().
+ * In that case, the draw() loop can be resumed with loop().
+ *
+ * @method loop
+ * @example
+ * <div><code>
+ * var x = 0;
+ * function setup() {
+ * createCanvas(100, 100);
+ * noLoop();
+ * }
+ *
+ * function draw() {
+ * background(204);
+ * x = x + 0.1;
+ * if (x > width) {
+ * x = 0;
+ * }
+ * line(x, 0, x, height);
+ * }
+ *
+ * function mousePressed() {
+ * loop();
+ * }
+ *
+ * function mouseReleased() {
+ * noLoop();
+ * }
+ * </code></div>
+ */
+
+p5.prototype.loop = function() {
+ this._loop = true;
+ this._draw();
+};
+
+/**
+ * The push() function saves the current drawing style settings and
+ * transformations, while pop() restores these settings. Note that these
+ * functions are always used together. They allow you to change the style
+ * and transformation settings and later return to what you had. When a new
+ * state is started with push(), it builds on the current style and transform
+ * information. The push() and pop() functions can be embedded to provide
+ * more control. (See the second example for a demonstration.)
+ * <br><br>
+ * push() stores information related to the current transformation state
+ * and style settings controlled by the following functions: fill(),
+ * stroke(), tint(), strokeWeight(), strokeCap(), strokeJoin(),
+ * imageMode(), rectMode(), ellipseMode(), colorMode(), textAlign(),
+ * textFont(), textMode(), textSize(), textLeading().
+ *
+ * @method push
+ * @example
+ * <div>
+ * <code>
+ * ellipse(0, 50, 33, 33); // Left circle
+ *
+ * push(); // Start a new drawing state
+ * strokeWeight(10);
+ * fill(204, 153, 0);
+ * translate(50, 0);
+ * ellipse(0, 50, 33, 33); // Middle circle
+ * pop(); // Restore original state
+ *
+ * ellipse(100, 50, 33, 33); // Right circle
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * ellipse(0, 50, 33, 33); // Left circle
+ *
+ * push(); // Start a new drawing state
+ * strokeWeight(10);
+ * fill(204, 153, 0);
+ * ellipse(33, 50, 33, 33); // Left-middle circle
+ *
+ * push(); // Start another new drawing state
+ * stroke(0, 102, 153);
+ * ellipse(66, 50, 33, 33); // Right-middle circle
+ * pop(); // Restore previous state
+ *
+ * pop(); // Restore original state
+ *
+ * ellipse(100, 50, 33, 33); // Right circle
+ * </code>
+ * </div>
+ */
+p5.prototype.push = function () {
+ this._renderer.push();
+ this._styles.push({
+ _doStroke: this._renderer._doStroke,
+ _doFill: this._renderer._doFill,
+ _tint: this._renderer._tint,
+ _imageMode: this._renderer._imageMode,
+ _rectMode: this._renderer._rectMode,
+ _ellipseMode: this._renderer._ellipseMode,
+ _colorMode: this._renderer._colorMode,
+ _textFont: this._renderer._textFont,
+ _textLeading: this._renderer._textLeading,
+ _textSize: this._renderer._textSize,
+ _textStyle: this._renderer._textStyle
+ });
+};
+
+/**
+ * The push() function saves the current drawing style settings and
+ * transformations, while pop() restores these settings. Note that these
+ * functions are always used together. They allow you to change the style
+ * and transformation settings and later return to what you had. When a new
+ * state is started with push(), it builds on the current style and transform
+ * information. The push() and pop() functions can be embedded to provide
+ * more control. (See the second example for a demonstration.)
+ * <br><br>
+ * push() stores information related to the current transformation state
+ * and style settings controlled by the following functions: fill(),
+ * stroke(), tint(), strokeWeight(), strokeCap(), strokeJoin(),
+ * imageMode(), rectMode(), ellipseMode(), colorMode(), textAlign(),
+ * textFont(), textMode(), textSize(), textLeading().
+ *
+ * @method pop
+ * @example
+ * <div>
+ * <code>
+ * ellipse(0, 50, 33, 33); // Left circle
+ *
+ * push(); // Start a new drawing state
+ * translate(50, 0);
+ * strokeWeight(10);
+ * fill(204, 153, 0);
+ * ellipse(0, 50, 33, 33); // Middle circle
+ * pop(); // Restore original state
+ *
+ * ellipse(100, 50, 33, 33); // Right circle
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * ellipse(0, 50, 33, 33); // Left circle
+ *
+ * push(); // Start a new drawing state
+ * strokeWeight(10);
+ * fill(204, 153, 0);
+ * ellipse(33, 50, 33, 33); // Left-middle circle
+ *
+ * push(); // Start another new drawing state
+ * stroke(0, 102, 153);
+ * ellipse(66, 50, 33, 33); // Right-middle circle
+ * pop(); // Restore previous state
+ *
+ * pop(); // Restore original state
+ *
+ * ellipse(100, 50, 33, 33); // Right circle
+ * </code>
+ * </div>
+ */
+p5.prototype.pop = function () {
+ this._renderer.pop();
+ var lastS = this._styles.pop();
+ for(var prop in lastS){
+ this._renderer[prop] = lastS[prop];
+ }
+};
+
+p5.prototype.pushStyle = function() {
+ throw new Error('pushStyle() not used, see push()');
+};
+
+p5.prototype.popStyle = function() {
+ throw new Error('popStyle() not used, see pop()');
+};
+
+/**
+ *
+ * Executes the code within draw() one time. This functions allows the
+ * program to update the display window only when necessary, for example
+ * when an event registered by mousePressed() or keyPressed() occurs.
+ * <br><br>
+ * In structuring a program, it only makes sense to call redraw() within
+ * events such as mousePressed(). This is because redraw() does not run
+ * draw() immediately (it only sets a flag that indicates an update is
+ * needed).
+ * <br><br>
+ * The redraw() function does not work properly when called inside draw().
+ * To enable/disable animations, use loop() and noLoop().
+ *
+ * @method redraw
+ * @example
+ * <div><code>
+ * var x = 0;
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ * noLoop();
+ * }
+ *
+ * function draw() {
+ * background(204);
+ * line(x, 0, x, height);
+ * }
+ *
+ * function mousePressed() {
+ * x += 1;
+ * redraw();
+ * }
+ * </code></div>
+ */
+p5.prototype.redraw = function () {
+ var userSetup = this.setup || window.setup;
+ var userDraw = this.draw || window.draw;
+ if (typeof userDraw === 'function') {
+ this.push();
+ if (typeof userSetup === 'undefined') {
+ this.scale(this._pixelDensity, this._pixelDensity);
+ }
+ var self = this;
+ this._registeredMethods.pre.forEach(function (f) {
+ f.call(self);
+ });
+ userDraw();
+ this._registeredMethods.post.forEach(function (f) {
+ f.call(self);
+ });
+ this.pop();
+ }
+};
+
+p5.prototype.size = function() {
+ var s = 'size() is not a valid p5 function, to set the size of the ';
+ s += 'drawing canvas, please use createCanvas() instead';
+ throw s;
+};
+
+
+module.exports = p5;
+
+},{"./core":48}],59:[function(_dereq_,module,exports){
+/**
+ * @module Transform
+ * @submodule Transform
+ * @for p5
+ * @requires core
+ * @requires constants
+ */
+
+
+'use strict';
+
+var p5 = _dereq_('./core');
+var constants = _dereq_('./constants');
+
+/**
+ * Multiplies the current matrix by the one specified through the parameters.
+ * This is very slow because it will try to calculate the inverse of the
+ * transform, so avoid it whenever possible.
+ *
+ * @method applyMatrix
+ * @param {Number} n00 numbers which define the 3x2 matrix to be multiplied
+ * @param {Number} n01 numbers which define the 3x2 matrix to be multiplied
+ * @param {Number} n02 numbers which define the 3x2 matrix to be multiplied
+ * @param {Number} n10 numbers which define the 3x2 matrix to be multiplied
+ * @param {Number} n11 numbers which define the 3x2 matrix to be multiplied
+ * @param {Number} n12 numbers which define the 3x2 matrix to be multiplied
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * // Example in the works.
+ * </code>
+ * </div>
+ */
+p5.prototype.applyMatrix = function(n00, n01, n02, n10, n11, n12) {
+ this._renderer.applyMatrix(n00, n01, n02, n10, n11, n12);
+ return this;
+};
+
+p5.prototype.popMatrix = function() {
+ throw new Error('popMatrix() not used, see pop()');
+};
+
+p5.prototype.printMatrix = function() {
+ throw new Error('printMatrix() not implemented');
+};
+
+p5.prototype.pushMatrix = function() {
+ throw new Error('pushMatrix() not used, see push()');
+};
+
+/**
+ * Replaces the current matrix with the identity matrix.
+ *
+ * @method resetMatrix
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * // Example in the works.
+ * </code>
+ * </div>
+ */
+p5.prototype.resetMatrix = function() {
+ this._renderer.resetMatrix();
+ return this;
+};
+
+/**
+ * Rotates a shape the amount specified by the angle parameter. This
+ * function accounts for angleMode, so angles can be entered in either
+ * RADIANS or DEGREES.
+ * <br><br>
+ * Objects are always rotated around their relative position to the
+ * origin and positive numbers rotate objects in a clockwise direction.
+ * Transformations apply to everything that happens after and subsequent
+ * calls to the function accumulates the effect. For example, calling
+ * rotate(HALF_PI) and then rotate(HALF_PI) is the same as rotate(PI).
+ * All tranformations are reset when draw() begins again.
+ * <br><br>
+ * Technically, rotate() multiplies the current transformation matrix
+ * by a rotation matrix. This function can be further controlled by
+ * the push() and pop().
+ *
+ * @method rotate
+ * @param {Number} angle the angle of rotation, specified in radians
+ * or degrees, depending on current angleMode
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * translate(width/2, height/2);
+ * rotate(PI/3.0);
+ * rect(-26, -26, 52, 52);
+ * </code>
+ * </div>
+ */
+p5.prototype.rotate = function() {
+ var r = arguments[0];
+ if (this._angleMode === constants.DEGREES) {
+ r = this.radians(r);
+ }
+ //in webgl mode
+ if(arguments.length > 1){
+ this._renderer.rotate(r, arguments[1]);
+ }
+ else {
+ this._renderer.rotate(r);
+ }
+ return this;
+};
+
+/**
+ * [rotateX description]
+ * @param {[type]} rad [description]
+ * @return {[type]} [description]
+ */
+p5.prototype.rotateX = function(rad) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ if (this._renderer.isP3D) {
+ this._validateParameters(
+ 'rotateX',
+ args,
+ [
+ ['Number']
+ ]
+ );
+ this._renderer.rotateX(rad);
+ } else {
+ throw 'not supported in p2d. Please use webgl mode';
+ }
+ return this;
+};
+
+/**
+ * [rotateY description]
+ * @param {[type]} rad [description]
+ * @return {[type]} [description]
+ */
+p5.prototype.rotateY = function(rad) {
+ if (this._renderer.isP3D) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ this._validateParameters(
+ 'rotateY',
+ args,
+ [
+ ['Number']
+ ]
+ );
+ this._renderer.rotateY(rad);
+ } else {
+ throw 'not supported in p2d. Please use webgl mode';
+ }
+ return this;
+};
+
+/**
+ * [rotateZ description]
+ * @param {[type]} rad [description]
+ * @return {[type]} [description]
+ */
+p5.prototype.rotateZ = function(rad) {
+ if (this._renderer.isP3D) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ this._validateParameters(
+ 'rotateZ',
+ args,
+ [
+ ['Number']
+ ]
+ );
+ this._renderer.rotateZ(rad);
+ } else {
+ throw 'not supported in p2d. Please use webgl mode';
+ }
+ return this;
+};
+
+/**
+ * Increases or decreases the size of a shape by expanding and contracting
+ * vertices. Objects always scale from their relative origin to the
+ * coordinate system. Scale values are specified as decimal percentages.
+ * For example, the function call scale(2.0) increases the dimension of a
+ * shape by 200%.
+ * <br><br>
+ * Transformations apply to everything that happens after and subsequent
+ * calls to the function multiply the effect. For example, calling scale(2.0)
+ * and then scale(1.5) is the same as scale(3.0). If scale() is called
+ * within draw(), the transformation is reset when the loop begins again.
+ * <br><br>
+ * Using this fuction with the z parameter requires using P3D as a
+ * parameter for size(), as shown in the third example above. This function
+ * can be further controlled with push() and pop().
+ *
+ * @method scale
+ * @param {Number | p5.Vector | Array} s
+ * percent to scale the object, or percentage to
+ * scale the object in the x-axis if multiple arguments
+ * are given
+ * @param {Number} [y] percent to scale the object in the y-axis
+ * @param {Number} [z] percent to scale the object in the z-axis (webgl only)
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * translate(width/2, height/2);
+ * rotate(PI/3.0);
+ * rect(-26, -26, 52, 52);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * rect(30, 20, 50, 50);
+ * scale(0.5, 1.3);
+ * rect(30, 20, 50, 50);
+ * </code>
+ * </div>
+ */
+p5.prototype.scale = function() {
+ var x,y,z;
+ var args = new Array(arguments.length);
+ for(var i = 0; i < args.length; i++) {
+ args[i] = arguments[i];
+ }
+ if(args[0] instanceof p5.Vector){
+ x = args[0].x;
+ y = args[0].y;
+ z = args[0].z;
+ }
+ else if(args[0] instanceof Array){
+ x = args[0][0];
+ y = args[0][1];
+ z = args[0][2] || 1;
+ }
+ else {
+ if(args.length === 1){
+ x = y = z = args[0];
+ }
+ else {
+ x = args[0];
+ y = args[1];
+ z = args[2] || 1;
+ }
+ }
+
+ if(this._renderer.isP3D){
+ this._renderer.scale.call(this._renderer, x,y,z);
+ }
+ else {
+ this._renderer.scale.call(this._renderer, x,y);
+ }
+ return this;
+};
+
+/**
+ * Shears a shape around the x-axis the amount specified by the angle
+ * parameter. Angles should be specified in the current angleMode.
+ * Objects are always sheared around their relative position to the origin
+ * and positive numbers shear objects in a clockwise direction.
+ * <br><br>
+ * Transformations apply to everything that happens after and subsequent
+ * calls to the function accumulates the effect. For example, calling
+ * shearX(PI/2) and then shearX(PI/2) is the same as shearX(PI).
+ * If shearX() is called within the draw(), the transformation is reset when
+ * the loop begins again.
+ * <br><br>
+ * Technically, shearX() multiplies the current transformation matrix by a
+ * rotation matrix. This function can be further controlled by the
+ * push() and pop() functions.
+ *
+ * @method shearX
+ * @param {Number} angle angle of shear specified in radians or degrees,
+ * depending on current angleMode
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * translate(width/4, height/4);
+ * shearX(PI/4.0);
+ * rect(0, 0, 30, 30);
+ * </code>
+ * </div>
+ */
+p5.prototype.shearX = function(angle) {
+ if (this._angleMode === constants.DEGREES) {
+ angle = this.radians(angle);
+ }
+ this._renderer.shearX(angle);
+ return this;
+};
+
+/**
+ * Shears a shape around the y-axis the amount specified by the angle
+ * parameter. Angles should be specified in the current angleMode. Objects
+ * are always sheared around their relative position to the origin and
+ * positive numbers shear objects in a clockwise direction.
+ * <br><br>
+ * Transformations apply to everything that happens after and subsequent
+ * calls to the function accumulates the effect. For example, calling
+ * shearY(PI/2) and then shearY(PI/2) is the same as shearY(PI). If
+ * shearY() is called within the draw(), the transformation is reset when
+ * the loop begins again.
+ * <br><br>
+ * Technically, shearY() multiplies the current transformation matrix by a
+ * rotation matrix. This function can be further controlled by the
+ * push() and pop() functions.
+ *
+ * @method shearY
+ * @param {Number} angle angle of shear specified in radians or degrees,
+ * depending on current angleMode
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * translate(width/4, height/4);
+ * shearY(PI/4.0);
+ * rect(0, 0, 30, 30);
+ * </code>
+ * </div>
+ */
+p5.prototype.shearY = function(angle) {
+ if (this._angleMode === constants.DEGREES) {
+ angle = this.radians(angle);
+ }
+ this._renderer.shearY(angle);
+ return this;
+};
+
+/**
+ * Specifies an amount to displace objects within the display window.
+ * The x parameter specifies left/right translation, the y parameter
+ * specifies up/down translation.
+ * <br><br>
+ * Transformations are cumulative and apply to everything that happens after
+ * and subsequent calls to the function accumulates the effect. For example,
+ * calling translate(50, 0) and then translate(20, 0) is the same as
+ * translate(70, 0). If translate() is called within draw(), the
+ * transformation is reset when the loop begins again. This function can be
+ * further controlled by using push() and pop().
+ *
+ * @method translate
+ * @param {Number} x left/right translation
+ * @param {Number} y up/down translation
+ * @return {p5} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * translate(30, 20);
+ * rect(0, 0, 55, 55);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * rect(0, 0, 55, 55); // Draw rect at original 0,0
+ * translate(30, 20);
+ * rect(0, 0, 55, 55); // Draw rect at new 0,0
+ * translate(14, 14);
+ * rect(0, 0, 55, 55); // Draw rect at new 0,0
+ * </code>
+ * </div>
+ */
+p5.prototype.translate = function(x, y, z) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+
+ if (this._renderer.isP3D) {
+ this._validateParameters(
+ 'translate',
+ args,
+ [
+ //p3d
+ ['Number', 'Number', 'Number']
+ ]
+ );
+ this._renderer.translate(x, y, z);
+ } else {
+ this._validateParameters(
+ 'translate',
+ args,
+ [
+ //p2d
+ ['Number', 'Number']
+ ]
+ );
+ this._renderer.translate(x, y);
+ }
+ return this;
+};
+
+module.exports = p5;
+
+},{"./constants":47,"./core":48}],60:[function(_dereq_,module,exports){
+/**
+ * @module Shape
+ * @submodule Vertex
+ * @for p5
+ * @requires core
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('./core');
+var constants = _dereq_('./constants');
+var shapeKind = null;
+var vertices = [];
+var contourVertices = [];
+var isBezier = false;
+var isCurve = false;
+var isQuadratic = false;
+var isContour = false;
+
+/**
+ * Use the beginContour() and endContour() functions to create negative
+ * shapes within shapes such as the center of the letter 'O'. beginContour()
+ * begins recording vertices for the shape and endContour() stops recording.
+ * The vertices that define a negative shape must "wind" in the opposite
+ * direction from the exterior shape. First draw vertices for the exterior
+ * clockwise order, then for internal shapes, draw vertices
+ * shape in counter-clockwise.
+ * <br><br>
+ * These functions can only be used within a beginShape()/endShape() pair and
+ * transformations such as translate(), rotate(), and scale() do not work
+ * within a beginContour()/endContour() pair. It is also not possible to use
+ * other shapes, such as ellipse() or rect() within.
+ *
+ * @method beginContour
+ * @return {Object} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * translate(50, 50);
+ * stroke(255, 0, 0);
+ * beginShape();
+ * // Exterior part of shape, clockwise winding
+ * vertex(-40, -40);
+ * vertex(40, -40);
+ * vertex(40, 40);
+ * vertex(-40, 40);
+ * // Interior part of shape, counter-clockwise winding
+ * beginContour();
+ * vertex(-20, -20);
+ * vertex(-20, 20);
+ * vertex(20, 20);
+ * vertex(20, -20);
+ * endContour();
+ * endShape(CLOSE);
+ * </code>
+ * </div>
+ */
+p5.prototype.beginContour = function() {
+ contourVertices = [];
+ isContour = true;
+ return this;
+};
+
+/**
+ * Using the beginShape() and endShape() functions allow creating more
+ * complex forms. beginShape() begins recording vertices for a shape and
+ * endShape() stops recording. The value of the kind parameter tells it which
+ * types of shapes to create from the provided vertices. With no mode
+ * specified, the shape can be any irregular polygon.
+ * <br><br>
+ * The parameters available for beginShape() are POINTS, LINES, TRIANGLES,
+ * TRIANGLE_FAN, TRIANGLE_STRIP, QUADS, and QUAD_STRIP. After calling the
+ * beginShape() function, a series of vertex() commands must follow. To stop
+ * drawing the shape, call endShape(). Each shape will be outlined with the
+ * current stroke color and filled with the fill color.
+ * <br><br>
+ * Transformations such as translate(), rotate(), and scale() do not work
+ * within beginShape(). It is also not possible to use other shapes, such as
+ * ellipse() or rect() within beginShape().
+ *
+ * @method beginShape
+ * @param {Number/Constant} kind either POINTS, LINES, TRIANGLES,
+ * TRIANGLE_FAN, TRIANGLE_STRIP, QUADS,
+ * or QUAD_STRIP
+ * @return {Object} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * beginShape();
+ * vertex(30, 20);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * vertex(30, 75);
+ * endShape(CLOSE);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * // currently not working
+ * beginShape(POINTS);
+ * vertex(30, 20);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * vertex(30, 75);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * beginShape(LINES);
+ * vertex(30, 20);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * vertex(30, 75);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * noFill();
+ * beginShape();
+ * vertex(30, 20);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * vertex(30, 75);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * noFill();
+ * beginShape();
+ * vertex(30, 20);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * vertex(30, 75);
+ * endShape(CLOSE);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * beginShape(TRIANGLES);
+ * vertex(30, 75);
+ * vertex(40, 20);
+ * vertex(50, 75);
+ * vertex(60, 20);
+ * vertex(70, 75);
+ * vertex(80, 20);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * beginShape(TRIANGLE_STRIP);
+ * vertex(30, 75);
+ * vertex(40, 20);
+ * vertex(50, 75);
+ * vertex(60, 20);
+ * vertex(70, 75);
+ * vertex(80, 20);
+ * vertex(90, 75);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * beginShape(TRIANGLE_FAN);
+ * vertex(57.5, 50);
+ * vertex(57.5, 15);
+ * vertex(92, 50);
+ * vertex(57.5, 85);
+ * vertex(22, 50);
+ * vertex(57.5, 15);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * beginShape(QUADS);
+ * vertex(30, 20);
+ * vertex(30, 75);
+ * vertex(50, 75);
+ * vertex(50, 20);
+ * vertex(65, 20);
+ * vertex(65, 75);
+ * vertex(85, 75);
+ * vertex(85, 20);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * beginShape(QUAD_STRIP);
+ * vertex(30, 20);
+ * vertex(30, 75);
+ * vertex(50, 20);
+ * vertex(50, 75);
+ * vertex(65, 20);
+ * vertex(65, 75);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * beginShape();
+ * vertex(20, 20);
+ * vertex(40, 20);
+ * vertex(40, 40);
+ * vertex(60, 40);
+ * vertex(60, 60);
+ * vertex(20, 60);
+ * endShape(CLOSE);
+ * </code>
+ * </div>
+ */
+p5.prototype.beginShape = function(kind) {
+ if (kind === constants.POINTS ||
+ kind === constants.LINES ||
+ kind === constants.TRIANGLES ||
+ kind === constants.TRIANGLE_FAN ||
+ kind === constants.TRIANGLE_STRIP ||
+ kind === constants.QUADS ||
+ kind === constants.QUAD_STRIP) {
+ shapeKind = kind;
+ } else {
+ shapeKind = null;
+ }
+ if(this._renderer.isP3D){
+ this._renderer.beginShape(kind);
+ } else {
+ vertices = [];
+ contourVertices = [];
+ }
+ return this;
+};
+
+/**
+ * Specifies vertex coordinates for Bezier curves. Each call to
+ * bezierVertex() defines the position of two control points and
+ * one anchor point of a Bezier curve, adding a new segment to a
+ * line or shape.
+ * <br><br>
+ * The first time bezierVertex() is used within a
+ * beginShape() call, it must be prefaced with a call to vertex()
+ * to set the first anchor point. This function must be used between
+ * beginShape() and endShape() and only when there is no MODE
+ * parameter specified to beginShape().
+ *
+ * @method bezierVertex
+ * @param {Number} x2 x-coordinate for the first control point
+ * @param {Number} y2 y-coordinate for the first control point
+ * @param {Number} x3 x-coordinate for the second control point
+ * @param {Number} y3 y-coordinate for the second control point
+ * @param {Number} x4 x-coordinate for the anchor point
+ * @param {Number} y4 y-coordinate for the anchor point
+ * @return {Object} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ * beginShape();
+ * vertex(30, 20);
+ * bezierVertex(80, 0, 80, 75, 30, 75);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * beginShape();
+ * vertex(30, 20);
+ * bezierVertex(80, 0, 80, 75, 30, 75);
+ * bezierVertex(50, 80, 60, 25, 30, 20);
+ * endShape();
+ * </code>
+ * </div>
+ */
+p5.prototype.bezierVertex = function(x2, y2, x3, y3, x4, y4) {
+ if (vertices.length === 0) {
+ throw 'vertex() must be used once before calling bezierVertex()';
+ } else {
+ isBezier = true;
+ var vert = [];
+ for (var i = 0; i < arguments.length; i++) {
+ vert[i] = arguments[i];
+ }
+ vert.isVert = false;
+ if (isContour) {
+ contourVertices.push(vert);
+ } else {
+ vertices.push(vert);
+ }
+ }
+ return this;
+};
+
+/**
+ * Specifies vertex coordinates for curves. This function may only
+ * be used between beginShape() and endShape() and only when there
+ * is no MODE parameter specified to beginShape().
+ * <br><br>
+ * The first and last points in a series of curveVertex() lines will be used to
+ * guide the beginning and end of a the curve. A minimum of four
+ * points is required to draw a tiny curve between the second and
+ * third points. Adding a fifth point with curveVertex() will draw
+ * the curve between the second, third, and fourth points. The
+ * curveVertex() function is an implementation of Catmull-Rom
+ * splines.
+ *
+ * @method curveVertex
+ * @param {Number} x x-coordinate of the vertex
+ * @param {Number} y y-coordinate of the vertex
+ * @return {Object} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ * beginShape();
+ * curveVertex(84, 91);
+ * curveVertex(84, 91);
+ * curveVertex(68, 19);
+ * curveVertex(21, 17);
+ * curveVertex(32, 100);
+ * curveVertex(32, 100);
+ * endShape();
+ * </code>
+ * </div>
+ */
+p5.prototype.curveVertex = function(x,y) {
+ isCurve = true;
+ this.vertex(x, y);
+ return this;
+};
+
+/**
+ * Use the beginContour() and endContour() functions to create negative
+ * shapes within shapes such as the center of the letter 'O'. beginContour()
+ * begins recording vertices for the shape and endContour() stops recording.
+ * The vertices that define a negative shape must "wind" in the opposite
+ * direction from the exterior shape. First draw vertices for the exterior
+ * clockwise order, then for internal shapes, draw vertices
+ * shape in counter-clockwise.
+ * <br><br>
+ * These functions can only be used within a beginShape()/endShape() pair and
+ * transformations such as translate(), rotate(), and scale() do not work
+ * within a beginContour()/endContour() pair. It is also not possible to use
+ * other shapes, such as ellipse() or rect() within.
+ *
+ * @method endContour
+ * @return {Object} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * translate(50, 50);
+ * stroke(255, 0, 0);
+ * beginShape();
+ * // Exterior part of shape, clockwise winding
+ * vertex(-40, -40);
+ * vertex(40, -40);
+ * vertex(40, 40);
+ * vertex(-40, 40);
+ * // Interior part of shape, counter-clockwise winding
+ * beginContour();
+ * vertex(-20, -20);
+ * vertex(-20, 20);
+ * vertex(20, 20);
+ * vertex(20, -20);
+ * endContour();
+ * endShape(CLOSE);
+ * </code>
+ * </div>
+ */
+p5.prototype.endContour = function() {
+ var vert = contourVertices[0].slice(); // copy all data
+ vert.isVert = contourVertices[0].isVert;
+ vert.moveTo = false;
+ contourVertices.push(vert);
+
+ vertices.push(vertices[0]);
+ for (var i = 0; i < contourVertices.length; i++) {
+ vertices.push(contourVertices[i]);
+ }
+ return this;
+};
+
+/**
+ * The endShape() function is the companion to beginShape() and may only be
+ * called after beginShape(). When endshape() is called, all of image data
+ * defined since the previous call to beginShape() is written into the image
+ * buffer. The constant CLOSE as the value for the MODE parameter to close
+ * the shape (to connect the beginning and the end).
+ *
+ * @method endShape
+ * @param {Number/Constant} mode use CLOSE to close the shape
+ * @return {Object} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ *
+ * beginShape();
+ * vertex(20, 20);
+ * vertex(45, 20);
+ * vertex(45, 80);
+ * endShape(CLOSE);
+ *
+ * beginShape();
+ * vertex(50, 20);
+ * vertex(75, 20);
+ * vertex(75, 80);
+ * endShape();
+ * </code>
+ * </div>
+ */
+p5.prototype.endShape = function(mode) {
+ if(this._renderer.isP3D){
+ this._renderer.endShape();
+ }else{
+ if (vertices.length === 0) { return this; }
+ if (!this._renderer._doStroke && !this._renderer._doFill) { return this; }
+
+ var closeShape = mode === constants.CLOSE;
+
+ // if the shape is closed, the first element is also the last element
+ if (closeShape && !isContour) {
+ vertices.push(vertices[0]);
+ }
+
+ this._renderer.endShape(mode, vertices, isCurve, isBezier,
+ isQuadratic, isContour, shapeKind);
+
+ // Reset some settings
+ isCurve = false;
+ isBezier = false;
+ isQuadratic = false;
+ isContour = false;
+
+ // If the shape is closed, the first element was added as last element.
+ // We must remove it again to prevent the list of vertices from growing
+ // over successive calls to endShape(CLOSE)
+ if (closeShape) {
+ vertices.pop();
+ }
+ }
+ return this;
+};
+
+/**
+ * Specifies vertex coordinates for quadratic Bezier curves. Each call to
+ * quadraticVertex() defines the position of one control points and one
+ * anchor point of a Bezier curve, adding a new segment to a line or shape.
+ * The first time quadraticVertex() is used within a beginShape() call, it
+ * must be prefaced with a call to vertex() to set the first anchor point.
+ * This function must be used between beginShape() and endShape() and only
+ * when there is no MODE parameter specified to beginShape().
+ *
+ * @method quadraticVertex
+ * @param {Number} cx x-coordinate for the control point
+ * @param {Number} cy y-coordinate for the control point
+ * @param {Number} x3 x-coordinate for the anchor point
+ * @param {Number} y3 y-coordinate for the anchor point
+ * @return {Object} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * noFill();
+ * strokeWeight(4);
+ * beginShape();
+ * vertex(20, 20);
+ * quadraticVertex(80, 20, 50, 50);
+ * endShape();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * noFill();
+ * strokeWeight(4);
+ * beginShape();
+ * vertex(20, 20);
+ * quadraticVertex(80, 20, 50, 50);
+ * quadraticVertex(20, 80, 80, 80);
+ * vertex(80, 60);
+ * endShape();
+ * </code>
+ * </div>
+ */
+p5.prototype.quadraticVertex = function(cx, cy, x3, y3) {
+ //if we're drawing a contour, put the points into an
+ // array for inside drawing
+ if(this._contourInited) {
+ var pt = {};
+ pt.x = cx;
+ pt.y = cy;
+ pt.x3 = x3;
+ pt.y3 = y3;
+ pt.type = constants.QUADRATIC;
+ this._contourVertices.push(pt);
+
+ return this;
+ }
+ if (vertices.length > 0) {
+ isQuadratic = true;
+ var vert = [];
+ for (var i = 0; i < arguments.length; i++) {
+ vert[i] = arguments[i];
+ }
+ vert.isVert = false;
+ if (isContour) {
+ contourVertices.push(vert);
+ } else {
+ vertices.push(vert);
+ }
+ } else {
+ throw 'vertex() must be used once before calling quadraticVertex()';
+ }
+ return this;
+};
+
+/**
+ * All shapes are constructed by connecting a series of vertices. vertex()
+ * is used to specify the vertex coordinates for points, lines, triangles,
+ * quads, and polygons. It is used exclusively within the beginShape() and
+ * endShape() functions.
+ *
+ * @method vertex
+ * @param {Number} x x-coordinate of the vertex
+ * @param {Number} y y-coordinate of the vertex
+ * @return {Object} the p5 object
+ * @example
+ * <div>
+ * <code>
+ * beginShape(POINTS);
+ * vertex(30, 20);
+ * vertex(85, 20);
+ * vertex(85, 75);
+ * vertex(30, 75);
+ * endShape();
+ * </code>
+ * </div>
+ */
+p5.prototype.vertex = function(x, y, moveTo) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ if(this._renderer.isP3D){
+ this._validateParameters(
+ 'vertex',
+ args,
+ [
+ ['Number', 'Number', 'Number']
+ ]
+ );
+ this._renderer.vertex
+ (arguments[0], arguments[1], arguments[2]);
+ }else{
+ this._validateParameters(
+ 'vertex',
+ args,
+ [
+ ['Number', 'Number'],
+ ['Number', 'Number', 'Number']
+ ]
+ );
+ var vert = [];
+ vert.isVert = true;
+ vert[0] = x;
+ vert[1] = y;
+ vert[2] = 0;
+ vert[3] = 0;
+ vert[4] = 0;
+ vert[5] = this._renderer._getFill();
+ vert[6] = this._renderer._getStroke();
+
+ if (moveTo) {
+ vert.moveTo = moveTo;
+ }
+ if (isContour) {
+ if (contourVertices.length === 0) {
+ vert.moveTo = true;
+ }
+ contourVertices.push(vert);
+ } else {
+ vertices.push(vert);
+ }
+ }
+ return this;
+};
+
+module.exports = p5;
+
+},{"./constants":47,"./core":48}],61:[function(_dereq_,module,exports){
+/**
+ * @module Events
+ * @submodule Acceleration
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * The system variable deviceOrientation always contains the orientation of
+ * the device. The value of this variable will either be set 'landscape'
+ * or 'portrait'. If no data is available it will be set to 'undefined'.
+ *
+ * @property deviceOrientation
+ */
+p5.prototype.deviceOrientation = undefined;
+
+/**
+ * The system variable accelerationX always contains the acceleration of the
+ * device along the x axis. Value is represented as meters per second squared.
+ *
+ * @property accelerationX
+ */
+p5.prototype.accelerationX = 0;
+
+/**
+ * The system variable accelerationY always contains the acceleration of the
+ * device along the y axis. Value is represented as meters per second squared.
+ *
+ * @property accelerationY
+ */
+p5.prototype.accelerationY = 0;
+
+/**
+ * The system variable accelerationZ always contains the acceleration of the
+ * device along the z axis. Value is represented as meters per second squared.
+ *
+ * @property accelerationZ
+ */
+p5.prototype.accelerationZ = 0;
+
+/**
+ * The system variable pAccelerationX always contains the acceleration of the
+ * device along the x axis in the frame previous to the current frame. Value
+ * is represented as meters per second squared.
+ *
+ * @property pAccelerationX
+ */
+p5.prototype.pAccelerationX = 0;
+
+/**
+ * The system variable pAccelerationY always contains the acceleration of the
+ * device along the y axis in the frame previous to the current frame. Value
+ * is represented as meters per second squared.
+ *
+ * @property pAccelerationY
+ */
+p5.prototype.pAccelerationY = 0;
+
+/**
+ * The system variable pAccelerationZ always contains the acceleration of the
+ * device along the z axis in the frame previous to the current frame. Value
+ * is represented as meters per second squared.
+ *
+ * @property pAccelerationZ
+ */
+p5.prototype.pAccelerationZ = 0;
+
+/**
+ * _updatePAccelerations updates the pAcceleration values
+ *
+ * @private
+ */
+p5.prototype._updatePAccelerations = function(){
+ this._setProperty('pAccelerationX', this.accelerationX);
+ this._setProperty('pAccelerationY', this.accelerationY);
+ this._setProperty('pAccelerationZ', this.accelerationZ);
+};
+
+/**
+ * The system variable rotationX always contains the rotation of the
+ * device along the x axis. Value is represented as 0 to +/-180 degrees.
+ * <br><br>
+ * Note: The order the rotations are called is important, ie. if used
+ * together, it must be called in the order Z-X-Y or there might be
+ * unexpected behaviour.
+ *
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ * background(200);
+ * //rotateZ(radians(rotationZ));
+ * rotateX(radians(rotationX));
+ * //rotateY(radians(rotationY));
+ * box(200, 200, 200);
+ * }
+ * </code>
+ * </div>
+ *
+ * @property rotationX
+ */
+p5.prototype.rotationX = 0;
+
+/**
+ * The system variable rotationY always contains the rotation of the
+ * device along the y axis. Value is represented as 0 to +/-90 degrees.
+ * <br><br>
+ * Note: The order the rotations are called is important, ie. if used
+ * together, it must be called in the order Z-X-Y or there might be
+ * unexpected behaviour.
+ *
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ * background(200);
+ * //rotateZ(radians(rotationZ));
+ * //rotateX(radians(rotationX));
+ * rotateY(radians(rotationY));
+ * box(200, 200, 200);
+ * }
+ * </code>
+ * </div>
+ *
+ * @property rotationY
+ */
+p5.prototype.rotationY = 0;
+
+/**
+ * The system variable rotationZ always contains the rotation of the
+ * device along the z axis. Value is represented as 0 to 359 degrees.
+ * <br><br>
+ * Unlike rotationX and rotationY, this variable is available for devices
+ * with a built-in compass only.
+ * <br><br>
+ * Note: The order the rotations are called is important, ie. if used
+ * together, it must be called in the order Z-X-Y or there might be
+ * unexpected behaviour.
+ *
+ * @example
+ * <div>
+ * <code>
+ * function setup(){
+ * createCanvas(100, 100, WEBGL);
+ * }
+ *
+ * function draw(){
+ * background(200);
+ * rotateZ(radians(rotationZ));
+ * //rotateX(radians(rotationX));
+ * //rotateY(radians(rotationY));
+ * box(200, 200, 200);
+ * }
+ * </code>
+ * </div>
+ *
+ * @property rotationZ
+ */
+p5.prototype.rotationZ = 0;
+
+/**
+ * The system variable pRotationX always contains the rotation of the
+ * device along the x axis in the frame previous to the current frame. Value
+ * is represented as 0 to +/-180 degrees.
+ * <br><br>
+ * pRotationX can also be used with rotationX to determine the rotate
+ * direction of the device along the X-axis.
+ * @example
+ * <div class='norender'>
+ * <code>
+ * // A simple if statement looking at whether
+ * // rotationX - pRotationX < 0 is true or not will be
+ * // sufficient for determining the rotate direction
+ * // in most cases.
+ *
+ * // Some extra logic is needed to account for cases where
+ * // the angles wrap around.
+ * var rotateDirection = 'clockwise';
+ *
+ * // Simple range conversion to make things simpler.
+ * // This is not absolutely neccessary but the logic
+ * // will be different in that case.
+ *
+ * var rX = rotationX + 180;
+ * var pRX = pRotationX + 180;
+ *
+ * if ((rX - pRX > 0 && rX - pRX < 270)|| rX - pRX < -270){
+ * rotateDirection = 'clockwise';
+ * } else if (rX - pRX < 0 || rX - pRX > 270){
+ * rotateDirection = 'counter-clockwise';
+ * }
+ * </code>
+ * </div>
+ *
+ * @property pRotationX
+ */
+p5.prototype.pRotationX = 0;
+
+/**
+ * The system variable pRotationY always contains the rotation of the
+ * device along the y axis in the frame previous to the current frame. Value
+ * is represented as 0 to +/-90 degrees.
+ * <br><br>
+ * pRotationY can also be used with rotationY to determine the rotate
+ * direction of the device along the Y-axis.
+ * @example
+ * <div class='norender'>
+ * <code>
+ * // A simple if statement looking at whether
+ * // rotationY - pRotationY < 0 is true or not will be
+ * // sufficient for determining the rotate direction
+ * // in most cases.
+ *
+ * // Some extra logic is needed to account for cases where
+ * // the angles wrap around.
+ * var rotateDirection = 'clockwise';
+ *
+ * // Simple range conversion to make things simpler.
+ * // This is not absolutely neccessary but the logic
+ * // will be different in that case.
+ *
+ * var rY = rotationY + 180;
+ * var pRY = pRotationY + 180;
+ *
+ * if ((rY - pRY > 0 && rY - pRY < 270)|| rY - pRY < -270){
+ * rotateDirection = 'clockwise';
+ * } else if (rY - pRY < 0 || rY - pRY > 270){
+ * rotateDirection = 'counter-clockwise';
+ * }
+ * </code>
+ * </div>
+ *
+ * @property pRotationY
+ */
+p5.prototype.pRotationY = 0;
+
+/**
+ * The system variable pRotationZ always contains the rotation of the
+ * device along the z axis in the frame previous to the current frame. Value
+ * is represented as 0 to 359 degrees.
+ * <br><br>
+ * pRotationZ can also be used with rotationZ to determine the rotate
+ * direction of the device along the Z-axis.
+ * @example
+ * <div class='norender'>
+ * <code>
+ * // A simple if statement looking at whether
+ * // rotationZ - pRotationZ < 0 is true or not will be
+ * // sufficient for determining the rotate direction
+ * // in most cases.
+ *
+ * // Some extra logic is needed to account for cases where
+ * // the angles wrap around.
+ * var rotateDirection = 'clockwise';
+ *
+ * if ((rotationZ - pRotationZ > 0 &&
+ * rotationZ - pRotationZ < 270)||
+ * rotationZ - pRotationZ < -270){
+ *
+ * rotateDirection = 'clockwise';
+ *
+ * } else if (rotationZ - pRotationZ < 0 ||
+ * rotationZ - pRotationZ > 270){
+ *
+ * rotateDirection = 'counter-clockwise';
+ *
+ * }
+ * </code>
+ * </div>
+ *
+ * @property pRotationZ
+ */
+p5.prototype.pRotationZ = 0;
+
+var startAngleX = 0;
+var startAngleY = 0;
+var startAngleZ = 0;
+
+var rotateDirectionX = 'clockwise';
+var rotateDirectionY = 'clockwise';
+var rotateDirectionZ = 'clockwise';
+
+var pRotateDirectionX;
+var pRotateDirectionY;
+var pRotateDirectionZ;
+
+p5.prototype._updatePRotations = function(){
+ this._setProperty('pRotationX', this.rotationX);
+ this._setProperty('pRotationY', this.rotationY);
+ this._setProperty('pRotationZ', this.rotationZ);
+};
+
+p5.prototype.turnAxis = undefined;
+
+var move_threshold = 0.5;
+var shake_threshold = 30;
+
+/**
+ * The setMoveThreshold() function is used to set the movement threshold for
+ * the deviceMoved() function. The default threshold is set to 0.5.
+ *
+ * @method setMoveThreshold
+ * @param {number} value The threshold value
+ */
+p5.prototype.setMoveThreshold = function(val){
+ if(typeof val === 'number'){
+ move_threshold = val;
+ }
+};
+
+/**
+ * The setShakeThreshold() function is used to set the movement threshold for
+ * the deviceShaken() function. The default threshold is set to 30.
+ *
+ * @method setShakeThreshold
+ * @param {number} value The threshold value
+ */
+p5.prototype.setShakeThreshold = function(val){
+ if(typeof val === 'number'){
+ shake_threshold = val;
+ }
+};
+
+/**
+ * The deviceMoved() function is called when the device is moved by more than
+ * the threshold value along X, Y or Z axis. The default threshold is set to
+ * 0.5.
+ * @method deviceMoved
+ * @example
+ * <div>
+ * <code>
+ * // Run this example on a mobile device
+ * // Move the device around
+ * // to change the value.
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function deviceMoved() {
+ * value = value + 5;
+ * if (value > 255) {
+ * value = 0;
+ * }
+ * }
+ * </code>
+ * </div>
+ */
+
+/**
+ * The deviceTurned() function is called when the device rotates by
+ * more than 90 degrees continuously.
+ * <br><br>
+ * The axis that triggers the deviceTurned() method is stored in the turnAxis
+ * variable. The deviceTurned() method can be locked to trigger on any axis:
+ * X, Y or Z by comparing the turnAxis variable to 'X', 'Y' or 'Z'.
+ *
+ * @method deviceTurned
+ * @example
+ * <div>
+ * <code>
+ * // Run this example on a mobile device
+ * // Rotate the device by 90 degrees
+ * // to change the value.
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function deviceTurned() {
+ * if (value == 0){
+ * value = 255
+ * } else if (value == 255) {
+ * value = 0;
+ * }
+ * }
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * // Run this example on a mobile device
+ * // Rotate the device by 90 degrees in the
+ * // X-axis to change the value.
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function deviceTurned() {
+ * if (turnAxis == 'X'){
+ * if (value == 0){
+ * value = 255
+ * } else if (value == 255) {
+ * value = 0;
+ * }
+ * }
+ * }
+ * </code>
+ * </div>
+ */
+
+/**
+ * The deviceShaken() function is called when the device total acceleration
+ * changes of accelerationX and accelerationY values is more than
+ * the threshold value. The default threshold is set to 30.
+ * @method deviceShaken
+ * @example
+ * <div>
+ * <code>
+ * // Run this example on a mobile device
+ * // Shake the device to change the value.
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function deviceShaken() {
+ * value = value + 5;
+ * if (value > 255) {
+ * value = 0;
+ * }
+ * }
+ * </code>
+ * </div>
+ */
+
+p5.prototype._ondeviceorientation = function (e) {
+ this._updatePRotations();
+ this._setProperty('rotationX', e.beta);
+ this._setProperty('rotationY', e.gamma);
+ this._setProperty('rotationZ', e.alpha);
+ this._handleMotion();
+};
+p5.prototype._ondevicemotion = function (e) {
+ this._updatePAccelerations();
+ this._setProperty('accelerationX', e.acceleration.x * 2);
+ this._setProperty('accelerationY', e.acceleration.y * 2);
+ this._setProperty('accelerationZ', e.acceleration.z * 2);
+ this._handleMotion();
+};
+p5.prototype._handleMotion = function() {
+ if (window.orientation === 90 || window.orientation === -90) {
+ this._setProperty('deviceOrientation', 'landscape');
+ } else if (window.orientation === 0) {
+ this._setProperty('deviceOrientation', 'portrait');
+ } else if (window.orientation === undefined) {
+ this._setProperty('deviceOrientation', 'undefined');
+ }
+ var deviceMoved = this.deviceMoved || window.deviceMoved;
+ if (typeof deviceMoved === 'function') {
+ if (Math.abs(this.accelerationX - this.pAccelerationX) > move_threshold ||
+ Math.abs(this.accelerationY - this.pAccelerationY) > move_threshold ||
+ Math.abs(this.accelerationZ - this.pAccelerationZ) > move_threshold) {
+ deviceMoved();
+ }
+ }
+ var deviceTurned = this.deviceTurned || window.deviceTurned;
+ if (typeof deviceTurned === 'function') {
+ // The angles given by rotationX etc is from range -180 to 180.
+ // The following will convert them to 0 to 360 for ease of calculation
+ // of cases when the angles wrapped around.
+ // _startAngleX will be converted back at the end and updated.
+ var wRX = this.rotationX + 180;
+ var wPRX = this.pRotationX + 180;
+ var wSAX = startAngleX + 180;
+ if ((wRX - wPRX > 0 && wRX - wPRX < 270)|| wRX - wPRX < -270){
+ rotateDirectionX = 'clockwise';
+ } else if (wRX - wPRX < 0 || wRX - wPRX > 270){
+ rotateDirectionX = 'counter-clockwise';
+ }
+ if (rotateDirectionX !== pRotateDirectionX){
+ wSAX = wRX;
+ }
+ if (Math.abs(wRX - wSAX) > 90 && Math.abs(wRX - wSAX) < 270){
+ wSAX = wRX;
+ this._setProperty('turnAxis', 'X');
+ deviceTurned();
+ }
+ pRotateDirectionX = rotateDirectionX;
+ startAngleX = wSAX - 180;
+
+ // Y-axis is identical to X-axis except for changing some names.
+ var wRY = this.rotationY + 180;
+ var wPRY = this.pRotationY + 180;
+ var wSAY = startAngleY + 180;
+ if ((wRY - wPRY > 0 && wRY - wPRY < 270)|| wRY - wPRY < -270){
+ rotateDirectionY = 'clockwise';
+ } else if (wRY - wPRY < 0 || wRY - this.pRotationY > 270){
+ rotateDirectionY = 'counter-clockwise';
+ }
+ if (rotateDirectionY !== pRotateDirectionY){
+ wSAY = wRY;
+ }
+ if (Math.abs(wRY - wSAY) > 90 && Math.abs(wRY - wSAY) < 270){
+ wSAY = wRY;
+ this._setProperty('turnAxis', 'Y');
+ deviceTurned();
+ }
+ pRotateDirectionY = rotateDirectionY;
+ startAngleY = wSAY - 180;
+
+ // Z-axis is already in the range 0 to 360
+ // so no conversion is needed.
+ if ((this.rotationZ - this.pRotationZ > 0 &&
+ this.rotationZ - this.pRotationZ < 270)||
+ this.rotationZ - this.pRotationZ < -270){
+ rotateDirectionZ = 'clockwise';
+ } else if (this.rotationZ - this.pRotationZ < 0 ||
+ this.rotationZ - this.pRotationZ > 270){
+ rotateDirectionZ = 'counter-clockwise';
+ }
+ if (rotateDirectionZ !== pRotateDirectionZ){
+ startAngleZ = this.rotationZ;
+ }
+ if (Math.abs(this.rotationZ - startAngleZ) > 90 &&
+ Math.abs(this.rotationZ - startAngleZ) < 270){
+ startAngleZ = this.rotationZ;
+ this._setProperty('turnAxis', 'Z');
+ deviceTurned();
+ }
+ pRotateDirectionZ = rotateDirectionZ;
+ this._setProperty('turnAxis', undefined);
+ }
+ var deviceShaken = this.deviceShaken || window.deviceShaken;
+ if (typeof deviceShaken === 'function') {
+ var accelerationChangeX;
+ var accelerationChangeY;
+ // Add accelerationChangeZ if acceleration change on Z is needed
+ if (this.pAccelerationX !== null) {
+ accelerationChangeX = Math.abs(this.accelerationX - this.pAccelerationX);
+ accelerationChangeY = Math.abs(this.accelerationY - this.pAccelerationY);
+ }
+ if (accelerationChangeX + accelerationChangeY > shake_threshold) {
+ deviceShaken();
+ }
+ }
+};
+
+
+module.exports = p5;
+
+},{"../core/core":48}],62:[function(_dereq_,module,exports){
+/**
+ * @module Events
+ * @submodule Keyboard
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * Holds the key codes of currently pressed keys.
+ * @private
+ */
+var downKeys = {};
+
+/**
+ * The boolean system variable keyIsPressed is true if any key is pressed
+ * and false if no keys are pressed.
+ *
+ * @property keyIsPressed
+ * @example
+ * <div>
+ * <code>
+ * var value = 0;
+ * function draw() {
+ * if (keyIsPressed === true) {
+ * fill(0);
+ * } else {
+ * fill(255);
+ * }
+ * rect(25, 25, 50, 50);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.isKeyPressed = false;
+p5.prototype.keyIsPressed = false; // khan
+
+/**
+ * The system variable key always contains the value of the most recent
+ * key on the keyboard that was typed. To get the proper capitalization, it
+ * is best to use it within keyTyped(). For non-ASCII keys, use the keyCode
+ * variable.
+ *
+ * @property key
+ * @example
+ * <div><code>
+ * // Click any key to display it!
+ * // (Not Guaranteed to be Case Sensitive)
+ * function setup() {
+ * fill(245, 123, 158);
+ * textSize(50);
+ * }
+ *
+ * function draw() {
+ * background(200);
+ * text(key, 33,65); // Display last key pressed.
+ * }
+ * </div></code>
+ */
+p5.prototype.key = '';
+
+/**
+ * The variable keyCode is used to detect special keys such as BACKSPACE,
+ * DELETE, ENTER, RETURN, TAB, ESCAPE, SHIFT, CONTROL, OPTION, ALT, UP_ARROW,
+ * DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW.
+ *
+ * @property keyCode
+ * @example
+ * <div><code>
+ * var fillVal = 126;
+ * function draw() {
+ * fill(fillVal);
+ * rect(25, 25, 50, 50);
+ * }
+ *
+ * function keyPressed() {
+ * if (keyCode == UP_ARROW) {
+ * fillVal = 255;
+ * } else if (keyCode == DOWN_ARROW) {
+ * fillVal = 0;
+ * }
+ * return false; // prevent default
+ * }
+ * </code></div>
+ */
+p5.prototype.keyCode = 0;
+
+/**
+ * The keyPressed() function is called once every time a key is pressed. The
+ * keyCode for the key that was pressed is stored in the keyCode variable.
+ * <br><br>
+ * For non-ASCII keys, use the keyCode variable. You can check if the keyCode
+ * equals BACKSPACE, DELETE, ENTER, RETURN, TAB, ESCAPE, SHIFT, CONTROL,
+ * OPTION, ALT, UP_ARROW, DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW.
+ * <br><br>
+ * For ASCII keys that was pressed is stored in the key variable. However, it
+ * does not distinguish between uppercase and lowercase. For this reason, it
+ * is recommended to use keyTyped() to read the key variable, in which the
+ * case of the variable will be distinguished.
+ * <br><br>
+ * Because of how operating systems handle key repeats, holding down a key
+ * may cause multiple calls to keyTyped() (and keyReleased() as well). The
+ * rate of repeat is set by the operating system and how each computer is
+ * configured.<br><br>
+ * Browsers may have different default
+ * behaviors attached to various key events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ * @method keyPressed
+ * @example
+ * <div>
+ * <code>
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function keyPressed() {
+ * if (value === 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * }
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function keyPressed() {
+ * if (keyCode === LEFT_ARROW) {
+ * value = 255;
+ * } else if (keyCode === RIGHT_ARROW) {
+ * value = 0;
+ * }
+ * }
+ * </code>
+ * </div>
+ * <div class="norender">
+ * <code>
+ * function keyPressed(){
+ * // Do something
+ * return false; // prevent any default behaviour
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._onkeydown = function (e) {
+ if (downKeys[e.which]) { // prevent multiple firings
+ return;
+ }
+ this._setProperty('isKeyPressed', true);
+ this._setProperty('keyIsPressed', true);
+ this._setProperty('keyCode', e.which);
+ downKeys[e.which] = true;
+ var key = String.fromCharCode(e.which);
+ if (!key) {
+ key = e.which;
+ }
+ this._setProperty('key', key);
+ var keyPressed = this.keyPressed || window.keyPressed;
+ if (typeof keyPressed === 'function' && !e.charCode) {
+ var executeDefault = keyPressed(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+};
+/**
+ * The keyReleased() function is called once every time a key is released.
+ * See key and keyCode for more information.<br><br>
+ * Browsers may have different default
+ * behaviors attached to various key events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ * @method keyReleased
+ * @example
+ * <div>
+ * <code>
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function keyReleased() {
+ * if (value === 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * return false; // prevent any default behavior
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._onkeyup = function (e) {
+ var keyReleased = this.keyReleased || window.keyReleased;
+ this._setProperty('isKeyPressed', false);
+ this._setProperty('keyIsPressed', false);
+ this._setProperty('_lastKeyCodeTyped', null);
+ downKeys[e.which] = false;
+ //delete this._downKeys[e.which];
+ var key = String.fromCharCode(e.which);
+ if (!key) {
+ key = e.which;
+ }
+ this._setProperty('key', key);
+ this._setProperty('keyCode', e.which);
+ if (typeof keyReleased === 'function') {
+ var executeDefault = keyReleased(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+};
+
+/**
+ * The keyTyped() function is called once every time a key is pressed, but
+ * action keys such as Ctrl, Shift, and Alt are ignored. The most recent
+ * key pressed will be stored in the key variable.
+ * <br><br>
+ * Because of how operating systems handle key repeats, holding down a key
+ * will cause multiple calls to keyTyped() (and keyReleased() as well). The
+ * rate of repeat is set by the operating system and how each computer is
+ * configured.<br><br>
+ * Browsers may have different default behaviors attached to various key
+ * events. To prevent any default behavior for this event, add "return false"
+ * to the end of the method.
+ *
+ * @method keyTyped
+ * @example
+ * <div>
+ * <code>
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function keyTyped() {
+ * if (key === 'a') {
+ * value = 255;
+ * } else if (key === 'b') {
+ * value = 0;
+ * }
+ * return false; // prevent any default behavior
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._onkeypress = function (e) {
+ if (e.which === this._lastKeyCodeTyped) { // prevent multiple firings
+ return;
+ }
+ this._setProperty('keyCode', e.which);
+ this._setProperty('_lastKeyCodeTyped', e.which); // track last keyCode
+ this._setProperty('key', String.fromCharCode(e.which));
+ var keyTyped = this.keyTyped || window.keyTyped;
+ if (typeof keyTyped === 'function') {
+ var executeDefault = keyTyped(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+};
+/**
+ * The onblur function is called when the user is no longer focused
+ * on the p5 element. Because the keyup events will not fire if the user is
+ * not focused on the element we must assume all keys currently down have
+ * been released.
+ */
+p5.prototype._onblur = function (e) {
+ downKeys = {};
+};
+
+/**
+ * The keyIsDown() function checks if the key is currently down, i.e. pressed.
+ * It can be used if you have an object that moves, and you want several keys
+ * to be able to affect its behaviour simultaneously, such as moving a
+ * sprite diagonally. You can put in any number representing the keyCode of
+ * the key, or use any of the variable keyCode names listed
+ * <a href="http://p5js.org/reference/#p5/keyCode">here</a>.
+ *
+ * @method keyIsDown
+ * @param {Number} code The key to check for.
+ * @return {Boolean} whether key is down or not
+ * @example
+ * <div><code>
+ * var x = 100;
+ * var y = 100;
+ *
+ * function setup() {
+ * createCanvas(512, 512);
+ * }
+ *
+ * function draw() {
+ * if (keyIsDown(LEFT_ARROW))
+ * x-=5;
+ *
+ * if (keyIsDown(RIGHT_ARROW))
+ * x+=5;
+ *
+ * if (keyIsDown(UP_ARROW))
+ * y-=5;
+ *
+ * if (keyIsDown(DOWN_ARROW))
+ * y+=5;
+ *
+ * clear();
+ * fill(255, 0, 0);
+ * ellipse(x, y, 50, 50);
+ * }
+ * </code></div>
+ */
+p5.prototype.keyIsDown = function(code) {
+ return downKeys[code];
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],63:[function(_dereq_,module,exports){
+/**
+ * @module Events
+ * @submodule Mouse
+ * @for p5
+ * @requires core
+ * @requires constants
+ */
+
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var constants = _dereq_('../core/constants');
+
+/*
+ * These are helper vars that store the mouseX and mouseY vals
+ * between the time that a mouse event happens and the next frame
+ * of draw. This is done to deal with the asynchronicity of event
+ * calls interacting with the draw loop. When a mouse event occurs
+ * the _nextMouseX/Y vars are updated, then on each call of draw, mouseX/Y
+ * and pmouseX/Y are updated using the _nextMouseX/Y vals.
+ */
+p5.prototype._nextMouseX = 0;
+p5.prototype._nextMouseY = 0;
+
+/**
+ * The system variable mouseX always contains the current horizontal
+ * position of the mouse, relative to (0, 0) of the canvas.
+ *
+ * @property mouseX
+ *
+ * @example
+ * <div>
+ * <code>
+ * // Move the mouse across the canvas
+ * function draw() {
+ * background(244, 248, 252);
+ * line(mouseX, 0, mouseX, 100);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.mouseX = 0;
+
+/**
+ * The system variable mouseY always contains the current vertical position
+ * of the mouse, relative to (0, 0) of the canvas.
+ *
+ * @property mouseY
+ *
+ * @example
+ * <div>
+ * <code>
+ * // Move the mouse across the canvas
+ * function draw() {
+ * background(244, 248, 252);
+ * line(0, mouseY, 100, mouseY);
+ *}
+ * </code>
+ * </div>
+ */
+p5.prototype.mouseY = 0;
+
+/**
+ * The system variable pmouseX always contains the horizontal position of
+ * the mouse in the frame previous to the current frame, relative to (0, 0)
+ * of the canvas.
+ *
+ * @property pmouseX
+ *
+ * @example
+ * <div>
+ * <code>
+ * // Move the mouse across the canvas to leave a trail
+ * function setup() {
+ * //slow down the frameRate to make it more visible
+ * frameRate(10);
+ * }
+ *
+ * function draw() {
+ * background(244, 248, 252);
+ * line(mouseX, mouseY, pmouseX, pmouseY);
+ * print(pmouseX + " -> " + mouseX);
+ * }
+ *
+ * </code>
+ * </div>
+ */
+p5.prototype.pmouseX = 0;
+
+/**
+ * The system variable pmouseY always contains the vertical position of the
+ * mouse in the frame previous to the current frame, relative to (0, 0) of
+ * the canvas.
+ *
+ * @property pmouseY
+ *
+ * @example
+ * <div>
+ * <code>
+ * function draw() {
+ * background(237, 34, 93);
+ * fill(0);
+ * //draw a square only if the mouse is not moving
+ * if(mouseY == pmouseY && mouseX == pmouseX)
+ * rect(20,20,60,60);
+ *
+ * print(pmouseY + " -> " + mouseY);
+ * }
+ *
+ * </code>
+ * </div>
+ */
+p5.prototype.pmouseY = 0;
+
+/**
+ * The system variable winMouseX always contains the current horizontal
+ * position of the mouse, relative to (0, 0) of the window.
+ *
+ * @property winMouseX
+ *
+ * @example
+ * <div>
+ * <code>
+ * var myCanvas;
+ *
+ * function setup() {
+ * //use a variable to store a pointer to the canvas
+ * myCanvas = createCanvas(100, 100);
+ * }
+ *
+ * function draw() {
+ * background(237, 34, 93);
+ * fill(0);
+ *
+ * //move the canvas to the horizontal mouse position
+ * //relative to the window
+ * myCanvas.position(winMouseX+1, windowHeight/2);
+ *
+ * //the y of the square is relative to the canvas
+ * rect(20,mouseY,60,60);
+ * }
+ *
+ * </code>
+ * </div>
+ */
+p5.prototype.winMouseX = 0;
+
+/**
+ * The system variable winMouseY always contains the current vertical
+ * position of the mouse, relative to (0, 0) of the window.
+ *
+ * @property winMouseY
+ *
+ * @example
+ * <div>
+ * <code>
+ *var myCanvas;
+ *
+ * function setup() {
+ * //use a variable to store a pointer to the canvas
+ * myCanvas = createCanvas(100, 100);
+ * }
+ *
+ * function draw() {
+ * background(237, 34, 93);
+ * fill(0);
+ *
+ * //move the canvas to the vertical mouse position
+ * //relative to the window
+ * myCanvas.position(windowWidth/2, winMouseY+1);
+ *
+ * //the x of the square is relative to the canvas
+ * rect(mouseX,20,60,60);
+ * }
+ *
+ * </code>
+ * </div>
+ */
+p5.prototype.winMouseY = 0;
+
+/**
+ * The system variable pwinMouseX always contains the horizontal position
+ * of the mouse in the frame previous to the current frame, relative to
+ * (0, 0) of the window.
+ *
+ * @property pwinMouseX
+ *
+ * @example
+ * <div>
+ * <code>
+ *
+ * var myCanvas;
+ *
+ * function setup() {
+ * //use a variable to store a pointer to the canvas
+ * myCanvas = createCanvas(100, 100);
+ * noStroke();
+ * fill(237, 34, 93);
+ * }
+ *
+ * function draw() {
+ * clear();
+ * //the difference between previous and
+ * //current x position is the horizontal mouse speed
+ * var speed = abs(winMouseX-pwinMouseX);
+ * //change the size of the circle
+ * //according to the horizontal speed
+ * ellipse(50, 50, 10+speed*5, 10+speed*5);
+ * //move the canvas to the mouse position
+ * myCanvas.position( winMouseX+1, winMouseY+1);
+ * }
+ *
+ * </code>
+ * </div>
+ */
+p5.prototype.pwinMouseX = 0;
+
+/**
+ * The system variable pwinMouseY always contains the vertical position of
+ * the mouse in the frame previous to the current frame, relative to (0, 0)
+ * of the window.
+ *
+ * @property pwinMouseY
+ *
+ *
+ * @example
+ * <div>
+ * <code>
+ *
+ * var myCanvas;
+ *
+ * function setup() {
+ * //use a variable to store a pointer to the canvas
+ * myCanvas = createCanvas(100, 100);
+ * noStroke();
+ * fill(237, 34, 93);
+ * }
+ *
+ * function draw() {
+ * clear();
+ * //the difference between previous and
+ * //current y position is the vertical mouse speed
+ * var speed = abs(winMouseY-pwinMouseY);
+ * //change the size of the circle
+ * //according to the vertical speed
+ * ellipse(50, 50, 10+speed*5, 10+speed*5);
+ * //move the canvas to the mouse position
+ * myCanvas.position( winMouseX+1, winMouseY+1);
+ * }
+ *
+ * </code>
+ * </div>
+ */
+p5.prototype.pwinMouseY = 0;
+
+/**
+ * Processing automatically tracks if the mouse button is pressed and which
+ * button is pressed. The value of the system variable mouseButton is either
+ * LEFT, RIGHT, or CENTER depending on which button was pressed last.
+ * Warning: different browsers may track mouseButton differently.
+ *
+ * @property mouseButton
+ *
+ * @example
+ * <div>
+ * <code>
+ * function draw() {
+ * background(237, 34, 93);
+ * fill(0);
+ *
+ * if (mouseIsPressed) {
+ * if (mouseButton == LEFT)
+ * ellipse(50, 50, 50, 50);
+ * if (mouseButton == RIGHT)
+ * rect(25, 25, 50, 50);
+ * if (mouseButton == CENTER)
+ * triangle(23, 75, 50, 20, 78, 75);
+ * }
+ *
+ * print(mouseButton);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.mouseButton = 0;
+
+/**
+ * The boolean system variable mouseIsPressed is true if the mouse is pressed
+ * and false if not.
+ *
+ * @property mouseIsPressed
+ *
+ * @example
+ * <div>
+ * <code>
+ * function draw() {
+ * background(237, 34, 93);
+ * fill(0);
+ *
+ * if (mouseIsPressed)
+ * ellipse(50, 50, 50, 50);
+ * else
+ * rect(25, 25, 50, 50);
+ *
+ * print(mouseIsPressed);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.mouseIsPressed = false;
+p5.prototype.isMousePressed = false; // both are supported
+
+p5.prototype._updateNextMouseCoords = function(e) {
+ if(e.type === 'touchstart' ||
+ e.type === 'touchmove' ||
+ e.type === 'touchend') {
+ this._setProperty('_nextMouseX', this._nextTouchX);
+ this._setProperty('_nextMouseY', this._nextTouchY);
+ } else {
+ if(this._curElement !== null) {
+ var mousePos = getMousePos(this._curElement.elt, e);
+ this._setProperty('_nextMouseX', mousePos.x);
+ this._setProperty('_nextMouseY', mousePos.y);
+ }
+ }
+ this._setProperty('winMouseX', e.pageX);
+ this._setProperty('winMouseY', e.pageY);
+};
+
+p5.prototype._updateMouseCoords = function() {
+ this._setProperty('pmouseX', this.mouseX);
+ this._setProperty('pmouseY', this.mouseY);
+ this._setProperty('mouseX', this._nextMouseX);
+ this._setProperty('mouseY', this._nextMouseY);
+ this._setProperty('pwinMouseX', this.winMouseX);
+ this._setProperty('pwinMouseY', this.winMouseY);
+};
+
+function getMousePos(canvas, evt) {
+ var rect = canvas.getBoundingClientRect();
+ return {
+ x: evt.clientX - rect.left,
+ y: evt.clientY - rect.top
+ };
+}
+
+p5.prototype._setMouseButton = function(e) {
+ if (e.button === 1) {
+ this._setProperty('mouseButton', constants.CENTER);
+ } else if (e.button === 2) {
+ this._setProperty('mouseButton', constants.RIGHT);
+ } else {
+ this._setProperty('mouseButton', constants.LEFT);
+ }
+};
+
+/**
+ * The mouseMoved() function is called every time the mouse moves and a mouse
+ * button is not pressed.<br><br>
+ * Browsers may have different default
+ * behaviors attached to various mouse events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ * @method mouseMoved
+ * @example
+ * <div>
+ * <code>
+ * // Move the mouse across the page
+ * // to change its value
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function mouseMoved() {
+ * value = value + 5;
+ * if (value > 255) {
+ * value = 0;
+ * }
+ * }
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * function mouseMoved() {
+ * ellipse(mouseX, mouseY, 5, 5);
+ * // prevent default
+ * return false;
+ * }
+ * </code>
+ * </div>
+ */
+
+/**
+ * The mouseDragged() function is called once every time the mouse moves and
+ * a mouse button is pressed. If no mouseDragged() function is defined, the
+ * touchMoved() function will be called instead if it is defined.<br><br>
+ * Browsers may have different default
+ * behaviors attached to various mouse events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ * @method mouseDragged
+ * @example
+ * <div>
+ * <code>
+ * // Drag the mouse across the page
+ * // to change its value
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function mouseDragged() {
+ * value = value + 5;
+ * if (value > 255) {
+ * value = 0;
+ * }
+ * }
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * function mouseDragged() {
+ * ellipse(mouseX, mouseY, 5, 5);
+ * // prevent default
+ * return false;
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._onmousemove = function(e){
+ var context = this._isGlobal ? window : this;
+ var executeDefault;
+ this._updateNextMouseCoords(e);
+ this._updateNextTouchCoords(e);
+ if (!this.isMousePressed) {
+ if (typeof context.mouseMoved === 'function') {
+ executeDefault = context.mouseMoved(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+ }
+ else {
+ if (typeof context.mouseDragged === 'function') {
+ executeDefault = context.mouseDragged(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ } else if (typeof context.touchMoved === 'function') {
+ executeDefault = context.touchMoved(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+ }
+};
+
+/**
+ * The mousePressed() function is called once after every time a mouse button
+ * is pressed. The mouseButton variable (see the related reference entry)
+ * can be used to determine which button has been pressed. If no
+ * mousePressed() function is defined, the touchStarted() function will be
+ * called instead if it is defined.<br><br>
+ * Browsers may have different default
+ * behaviors attached to various mouse events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ * @method mousePressed
+ * @example
+ * <div>
+ * <code>
+ * // Click within the image to change
+ * // the value of the rectangle
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function mousePressed() {
+ * if (value == 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * }
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * function mousePressed() {
+ * ellipse(mouseX, mouseY, 5, 5);
+ * // prevent default
+ * return false;
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._onmousedown = function(e) {
+ var context = this._isGlobal ? window : this;
+ var executeDefault;
+ this._setProperty('isMousePressed', true);
+ this._setProperty('mouseIsPressed', true);
+ this._setMouseButton(e);
+ this._updateNextMouseCoords(e);
+ this._updateNextTouchCoords(e);
+ if (typeof context.mousePressed === 'function') {
+ executeDefault = context.mousePressed(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ } else if (typeof context.touchStarted === 'function') {
+ executeDefault = context.touchStarted(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+};
+
+/**
+ * The mouseReleased() function is called every time a mouse button is
+ * released. If no mouseReleased() function is defined, the touchEnded()
+ * function will be called instead if it is defined.<br><br>
+ * Browsers may have different default
+ * behaviors attached to various mouse events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ *
+ * @method mouseReleased
+ * @example
+ * <div>
+ * <code>
+ * // Click within the image to change
+ * // the value of the rectangle
+ * // after the mouse has been clicked
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function mouseReleased() {
+ * if (value == 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * }
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * function mouseReleased() {
+ * ellipse(mouseX, mouseY, 5, 5);
+ * // prevent default
+ * return false;
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._onmouseup = function(e) {
+ var context = this._isGlobal ? window : this;
+ var executeDefault;
+ this._setProperty('isMousePressed', false);
+ this._setProperty('mouseIsPressed', false);
+ if (typeof context.mouseReleased === 'function') {
+ executeDefault = context.mouseReleased(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ } else if (typeof context.touchEnded === 'function') {
+ executeDefault = context.touchEnded(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+};
+
+p5.prototype._ondragend = p5.prototype._onmouseup;
+p5.prototype._ondragover = p5.prototype._onmousemove;
+
+/**
+ * The mouseClicked() function is called once after a mouse button has been
+ * pressed and then released.<br><br>
+ * Browsers may have different default
+ * behaviors attached to various mouse events. To prevent any default
+ * behavior for this event, add "return false" to the end of the method.
+ *
+ * @method mouseClicked
+ * @example
+ * <div>
+ * <code>
+ * // Click within the image to change
+ * // the value of the rectangle
+ * // after the mouse has been clicked
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function mouseClicked() {
+ * if (value == 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * }
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * function mouseClicked() {
+ * ellipse(mouseX, mouseY, 5, 5);
+ * // prevent default
+ * return false;
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._onclick = function(e) {
+ var context = this._isGlobal ? window : this;
+ if (typeof context.mouseClicked === 'function') {
+ var executeDefault = context.mouseClicked(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+};
+
+/**
+ * The function mouseWheel() is executed every time a vertical mouse wheel
+ * event is detected either triggered by an actual mouse wheel or by a
+ * touchpad.<br><br>
+ * The event.delta property returns the amount the mouse wheel
+ * have scrolled. The values can be positive or negative depending on the
+ * scroll direction (on OS X with "natural" scrolling enabled, the signs
+ * are inverted).<br><br>
+ * Browsers may have different default behaviors attached to various
+ * mouse events. To prevent any default behavior for this event, add
+ * "return false" to the end of the method.<br><br>
+ * Due to the current support of the "wheel" event on Safari, the function
+ * may only work as expected if "return false" is included while using Safari.
+ *
+ * @method mouseWheel
+ *
+ * @example
+ * <div>
+ * <code>
+ * var pos = 25;
+ *
+ * function draw() {
+ * background(237, 34, 93);
+ * fill(0);
+ * rect(25, pos, 50, 50);
+ * }
+ *
+ * function mouseWheel(event) {
+ * print(event.delta);
+ * //move the square according to the vertical scroll amount
+ * pos += event.delta;
+ * //uncomment to block page scrolling
+ * //return false;
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._onwheel = function(e) {
+ var context = this._isGlobal ? window : this;
+ if (typeof context.mouseWheel === 'function') {
+ e.delta = e.deltaY;
+ var executeDefault = context.mouseWheel(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+};
+
+module.exports = p5;
+
+},{"../core/constants":47,"../core/core":48}],64:[function(_dereq_,module,exports){
+/**
+ * @module Events
+ * @submodule Touch
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/*
+ * These are helper vars that store the touchX and touchY vals
+ * between the time that a mouse event happens and the next frame
+ * of draw. This is done to deal with the asynchronicity of event
+ * calls interacting with the draw loop. When a touch event occurs
+ * the _nextTouchX/Y vars are updated, then on each call of draw, touchX/Y
+ * and ptouchX/Y are updated using the _nextMouseX/Y vals.
+ */
+p5.prototype._nextTouchX = 0;
+p5.prototype._nextTouchY = 0;
+
+/**
+ * The system variable touchX always contains the horizontal position of
+ * one finger, relative to (0, 0) of the canvas. This is best used for
+ * single touch interactions. For multi-touch interactions, use the
+ * touches[] array.
+ *
+ * @property touchX
+ */
+p5.prototype.touchX = 0;
+
+/**
+ * The system variable touchY always contains the vertical position of
+ * one finger, relative to (0, 0) of the canvas. This is best used for
+ * single touch interactions. For multi-touch interactions, use the
+ * touches[] array.
+ *
+ * @property touchY
+ */
+p5.prototype.touchY = 0;
+
+/**
+ * The system variable ptouchX always contains the horizontal position of
+ * one finger, relative to (0, 0) of the canvas, in the frame previous to the
+ * current frame.
+ *
+ * @property ptouchX
+ */
+p5.prototype.ptouchX = 0;
+
+/**
+ * The system variable ptouchY always contains the vertical position of
+ * one finger, relative to (0, 0) of the canvas, in the frame previous to the
+ * current frame.
+ *
+ * @property ptouchY
+ */
+p5.prototype.ptouchY = 0;
+
+/**
+ * The system variable touches[] contains an array of the positions of all
+ * current touch points, relative to (0, 0) of the canvas, and IDs identifying a
+ * unique touch as it moves. Each element in the array is an object with x, y,
+ * and id properties.
+ *
+ * @property touches[]
+ */
+p5.prototype.touches = [];
+
+/**
+ * The boolean system variable touchIsDown is true if the screen is
+ * touched and false if not.
+ *
+ * @property touchIsDown
+ */
+p5.prototype.touchIsDown = false;
+
+p5.prototype._updateNextTouchCoords = function(e) {
+ if(e.type === 'mousedown' ||
+ e.type === 'mousemove' ||
+ e.type === 'mouseup'){
+ this._setProperty('_nextTouchX', this._nextMouseX);
+ this._setProperty('_nextTouchY', this._nextMouseY);
+ } else {
+ if(this._curElement !== null) {
+ var touchInfo = getTouchInfo(this._curElement.elt, e, 0);
+ this._setProperty('_nextTouchX', touchInfo.x);
+ this._setProperty('_nextTouchY', touchInfo.y);
+
+ var touches = [];
+ for(var i = 0; i < e.touches.length; i++){
+ touches[i] = getTouchInfo(this._curElement.elt, e, i);
+ }
+ this._setProperty('touches', touches);
+ }
+ }
+};
+
+p5.prototype._updateTouchCoords = function() {
+ this._setProperty('ptouchX', this.touchX);
+ this._setProperty('ptouchY', this.touchY);
+ this._setProperty('touchX', this._nextTouchX);
+ this._setProperty('touchY', this._nextTouchY);
+};
+
+function getTouchInfo(canvas, e, i) {
+ i = i || 0;
+ var rect = canvas.getBoundingClientRect();
+ var touch = e.touches[i] || e.changedTouches[i];
+ return {
+ x: touch.clientX - rect.left,
+ y: touch.clientY - rect.top,
+ id: touch.identifier
+ };
+}
+
+/**
+ * The touchStarted() function is called once after every time a touch is
+ * registered. If no touchStarted() function is defined, the mousePressed()
+ * function will be called instead if it is defined.<br><br>
+ * Browsers may have different default behaviors attached to various touch
+ * events. To prevent any default behavior for this event, add "return false"
+ * to the end of the method.
+ *
+ * @method touchStarted
+ * @example
+ * <div>
+ * <code>
+ * // Touch within the image to change
+ * // the value of the rectangle
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function touchStarted() {
+ * if (value == 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * }
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * function touchStarted() {
+ * ellipse(touchX, touchY, 5, 5);
+ * // prevent default
+ * return false;
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._ontouchstart = function(e) {
+ var context = this._isGlobal ? window : this;
+ var executeDefault;
+ this._updateNextTouchCoords(e);
+ this._updateNextMouseCoords(e);
+ this._setProperty('touchIsDown', true);
+ if(typeof context.touchStarted === 'function') {
+ executeDefault = context.touchStarted(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ } else if (typeof context.mousePressed === 'function') {
+ executeDefault = context.mousePressed(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ //this._setMouseButton(e);
+ }
+};
+
+/**
+ * The touchMoved() function is called every time a touch move is registered.
+ * If no touchMoved() function is defined, the mouseDragged() function will
+ * be called instead if it is defined.<br><br>
+ * Browsers may have different default behaviors attached to various touch
+ * events. To prevent any default behavior for this event, add "return false"
+ * to the end of the method.
+ *
+ * @method touchMoved
+ * @example
+ * <div>
+ * <code>
+ * // Move your finger across the page
+ * // to change its value
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function touchMoved() {
+ * value = value + 5;
+ * if (value > 255) {
+ * value = 0;
+ * }
+ * }
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * function touchMoved() {
+ * ellipse(touchX, touchY, 5, 5);
+ * // prevent default
+ * return false;
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._ontouchmove = function(e) {
+ var context = this._isGlobal ? window : this;
+ var executeDefault;
+ this._updateNextTouchCoords(e);
+ this._updateNextMouseCoords(e);
+ if (typeof context.touchMoved === 'function') {
+ executeDefault = context.touchMoved(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ } else if (typeof context.mouseDragged === 'function') {
+ executeDefault = context.mouseDragged(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+};
+
+/**
+ * The touchEnded() function is called every time a touch ends. If no
+ * touchEnded() function is defined, the mouseReleased() function will be
+ * called instead if it is defined.<br><br>
+ * Browsers may have different default behaviors attached to various touch
+ * events. To prevent any default behavior for this event, add "return false"
+ * to the end of the method.
+ *
+ * @method touchEnded
+ * @example
+ * <div>
+ * <code>
+ * // Release touch within the image to
+ * // change the value of the rectangle
+ *
+ * var value = 0;
+ * function draw() {
+ * fill(value);
+ * rect(25, 25, 50, 50);
+ * }
+ * function touchEnded() {
+ * if (value == 0) {
+ * value = 255;
+ * } else {
+ * value = 0;
+ * }
+ * }
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * function touchEnded() {
+ * ellipse(touchX, touchY, 5, 5);
+ * // prevent default
+ * return false;
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype._ontouchend = function(e) {
+ this._updateNextTouchCoords(e);
+ this._updateNextMouseCoords(e);
+ if (this.touches.length === 0) {
+ this._setProperty('touchIsDown', false);
+ }
+ var context = this._isGlobal ? window : this;
+ var executeDefault;
+ if (typeof context.touchEnded === 'function') {
+ executeDefault = context.touchEnded(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ } else if (typeof context.mouseReleased === 'function') {
+ executeDefault = context.mouseReleased(e);
+ if(executeDefault === false) {
+ e.preventDefault();
+ }
+ }
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],65:[function(_dereq_,module,exports){
+/*global ImageData:false */
+
+/**
+ * This module defines the filters for use with image buffers.
+ *
+ * This module is basically a collection of functions stored in an object
+ * as opposed to modules. The functions are destructive, modifying
+ * the passed in canvas rather than creating a copy.
+ *
+ * Generally speaking users of this module will use the Filters.apply method
+ * on a canvas to create an effect.
+ *
+ * A number of functions are borrowed/adapted from
+ * http://www.html5rocks.com/en/tutorials/canvas/imagefilters/
+ * or the java processing implementation.
+ */
+
+'use strict';
+
+var Filters = {};
+
+
+/*
+ * Helper functions
+ */
+
+
+/**
+ * Returns the pixel buffer for a canvas
+ *
+ * @private
+ *
+ * @param {Canvas|ImageData} canvas the canvas to get pixels from
+ * @return {Uint8ClampedArray} a one-dimensional array containing
+ * the data in thc RGBA order, with integer
+ * values between 0 and 255
+ */
+Filters._toPixels = function (canvas) {
+ if (canvas instanceof ImageData) {
+ return canvas.data;
+ } else {
+ return canvas.getContext('2d').getImageData(
+ 0,
+ 0,
+ canvas.width,
+ canvas.height
+ ).data;
+ }
+};
+
+/**
+ * Returns a 32 bit number containing ARGB data at ith pixel in the
+ * 1D array containing pixels data.
+ *
+ * @private
+ *
+ * @param {Uint8ClampedArray} data array returned by _toPixels()
+ * @param {Integer} i index of a 1D Image Array
+ * @return {Integer} 32 bit integer value representing
+ * ARGB value.
+ */
+Filters._getARGB = function (data, i) {
+ var offset = i * 4;
+ return (data[offset+3] << 24) & 0xff000000 |
+ (data[offset] << 16) & 0x00ff0000 |
+ (data[offset+1] << 8) & 0x0000ff00 |
+ data[offset+2] & 0x000000ff;
+};
+
+/**
+ * Modifies pixels RGBA values to values contained in the data object.
+ *
+ * @private
+ *
+ * @param {Uint8ClampedArray} pixels array returned by _toPixels()
+ * @param {Int32Array} data source 1D array where each value
+ * represents ARGB values
+ */
+Filters._setPixels = function (pixels, data) {
+ var offset = 0;
+ for( var i = 0, al = pixels.length; i < al; i++) {
+ offset = i*4;
+ pixels[offset + 0] = (data[i] & 0x00ff0000)>>>16;
+ pixels[offset + 1] = (data[i] & 0x0000ff00)>>>8;
+ pixels[offset + 2] = (data[i] & 0x000000ff);
+ pixels[offset + 3] = (data[i] & 0xff000000)>>>24;
+ }
+};
+
+/**
+ * Returns the ImageData object for a canvas
+ * https://developer.mozilla.org/en-US/docs/Web/API/ImageData
+ *
+ * @private
+ *
+ * @param {Canvas|ImageData} canvas canvas to get image data from
+ * @return {ImageData} Holder of pixel data (and width and
+ * height) for a canvas
+ */
+Filters._toImageData = function (canvas) {
+ if (canvas instanceof ImageData) {
+ return canvas;
+ } else {
+ return canvas.getContext('2d').getImageData(
+ 0,
+ 0,
+ canvas.width,
+ canvas.height
+ );
+ }
+};
+
+/**
+ * Returns a blank ImageData object.
+ *
+ * @private
+ *
+ * @param {Integer} width
+ * @param {Integer} height
+ * @return {ImageData}
+ */
+Filters._createImageData = function (width, height) {
+ Filters._tmpCanvas = document.createElement('canvas');
+ Filters._tmpCtx = Filters._tmpCanvas.getContext('2d');
+ return this._tmpCtx.createImageData(width, height);
+};
+
+
+/**
+ * Applys a filter function to a canvas.
+ *
+ * The difference between this and the actual filter functions defined below
+ * is that the filter functions generally modify the pixel buffer but do
+ * not actually put that data back to the canvas (where it would actually
+ * update what is visible). By contrast this method does make the changes
+ * actually visible in the canvas.
+ *
+ * The apply method is the method that callers of this module would generally
+ * use. It has been separated from the actual filters to support an advanced
+ * use case of creating a filter chain that executes without actually updating
+ * the canvas in between everystep.
+ *
+ * @param {[type]} func [description]
+ * @param {[type]} canvas [description]
+ * @param {[type]} level [description]
+ * @return {[type]} [description]
+ */
+Filters.apply = function (canvas, func, filterParam) {
+ var ctx = canvas.getContext('2d');
+ var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
+
+ //Filters can either return a new ImageData object, or just modify
+ //the one they received.
+ var newImageData = func(imageData, filterParam);
+ if (newImageData instanceof ImageData) {
+ ctx.putImageData(newImageData, 0, 0, 0, 0, canvas.width, canvas.height);
+ } else {
+ ctx.putImageData(imageData, 0, 0, 0, 0, canvas.width, canvas.height);
+ }
+};
+
+
+/*
+ * Filters
+ */
+
+
+/**
+ * Converts the image to black and white pixels depending if they are above or
+ * below the threshold defined by the level parameter. The parameter must be
+ * between 0.0 (black) and 1.0 (white). If no level is specified, 0.5 is used.
+ *
+ * Borrowed from http://www.html5rocks.com/en/tutorials/canvas/imagefilters/
+ *
+ * @param {Canvas} canvas
+ * @param {Float} level
+ */
+Filters.threshold = function (canvas, level) {
+ var pixels = Filters._toPixels(canvas);
+
+ if (level === undefined) {
+ level = 0.5;
+ }
+ var thresh = Math.floor(level * 255);
+
+ for (var i = 0; i < pixels.length; i += 4) {
+ var r = pixels[i];
+ var g = pixels[i + 1];
+ var b = pixels[i + 2];
+ var gray = (0.2126 * r + 0.7152 * g + 0.0722 * b);
+ var val;
+ if (gray >= thresh) {
+ val = 255;
+ } else {
+ val = 0;
+ }
+ pixels[i] = pixels[i + 1] = pixels[i + 2] = val;
+ }
+
+};
+
+
+/**
+ * Converts any colors in the image to grayscale equivalents.
+ * No parameter is used.
+ *
+ * Borrowed from http://www.html5rocks.com/en/tutorials/canvas/imagefilters/
+ *
+ * @param {Canvas} canvas
+ */
+Filters.gray = function (canvas) {
+ var pixels = Filters._toPixels(canvas);
+
+ for (var i = 0; i < pixels.length; i += 4) {
+ var r = pixels[i];
+ var g = pixels[i + 1];
+ var b = pixels[i + 2];
+
+ // CIE luminance for RGB
+ var gray = (0.2126 * r + 0.7152 * g + 0.0722 * b);
+ pixels[i] = pixels[i + 1] = pixels[i + 2] = gray;
+ }
+};
+
+/**
+ * Sets the alpha channel to entirely opaque. No parameter is used.
+ *
+ * @param {Canvas} canvas
+ */
+Filters.opaque = function (canvas) {
+ var pixels = Filters._toPixels(canvas);
+
+ for (var i = 0; i < pixels.length; i += 4) {
+ pixels[i + 3] = 255;
+ }
+
+ return pixels;
+};
+
+/**
+ * Sets each pixel to its inverse value. No parameter is used.
+ * @param {Invert}
+ */
+Filters.invert = function (canvas) {
+ var pixels = Filters._toPixels(canvas);
+
+ for (var i = 0; i < pixels.length; i += 4) {
+ pixels[i] = 255 - pixels[i];
+ pixels[i + 1] = 255 - pixels[i + 1];
+ pixels[i + 2] = 255 - pixels[i + 2];
+ }
+
+};
+
+
+/**
+ * Limits each channel of the image to the number of colors specified as
+ * the parameter. The parameter can be set to values between 2 and 255, but
+ * results are most noticeable in the lower ranges.
+ *
+ * Adapted from java based processing implementation
+ *
+ * @param {Canvas} canvas
+ * @param {Integer} level
+ */
+Filters.posterize = function (canvas, level) {
+ var pixels = Filters._toPixels(canvas);
+
+ if ((level < 2) || (level > 255)) {
+ throw new Error(
+ 'Level must be greater than 2 and less than 255 for posterize'
+ );
+ }
+
+ var levels1 = level - 1;
+ for (var i = 0; i < pixels.length; i+=4) {
+ var rlevel = pixels[i];
+ var glevel = pixels[i + 1];
+ var blevel = pixels[i + 2];
+
+ pixels[i] = (((rlevel * level) >> 8) * 255) / levels1;
+ pixels[i + 1] = (((glevel * level) >> 8) * 255) / levels1;
+ pixels[i + 2] = (((blevel * level) >> 8) * 255) / levels1;
+ }
+};
+
+/**
+ * reduces the bright areas in an image
+ * @param {Canvas} canvas
+ *
+ */
+Filters.dilate = function (canvas) {
+ var pixels = Filters._toPixels(canvas);
+ var currIdx = 0;
+ var maxIdx = pixels.length ? pixels.length/4 : 0;
+ var out = new Int32Array(maxIdx);
+ var currRowIdx, maxRowIdx, colOrig, colOut, currLum;
+ var idxRight, idxLeft, idxUp, idxDown,
+ colRight, colLeft, colUp, colDown,
+ lumRight, lumLeft, lumUp, lumDown;
+
+ while(currIdx < maxIdx) {
+ currRowIdx = currIdx;
+ maxRowIdx = currIdx + canvas.width;
+ while (currIdx < maxRowIdx) {
+ colOrig = colOut = Filters._getARGB(pixels, currIdx);
+ idxLeft = currIdx - 1;
+ idxRight = currIdx + 1;
+ idxUp = currIdx - canvas.width;
+ idxDown = currIdx + canvas.width;
+
+ if (idxLeft < currRowIdx) {
+ idxLeft = currIdx;
+ }
+ if (idxRight >= maxRowIdx) {
+ idxRight = currIdx;
+ }
+ if (idxUp < 0){
+ idxUp = 0;
+ }
+ if (idxDown >= maxIdx) {
+ idxDown = currIdx;
+ }
+ colUp = Filters._getARGB(pixels, idxUp);
+ colLeft = Filters._getARGB(pixels, idxLeft);
+ colDown = Filters._getARGB(pixels, idxDown);
+ colRight = Filters._getARGB(pixels, idxRight);
+
+ //compute luminance
+ currLum = 77*(colOrig>>16&0xff) +
+ 151*(colOrig>>8&0xff) +
+ 28*(colOrig&0xff);
+ lumLeft = 77*(colLeft>>16&0xff) +
+ 151*(colLeft>>8&0xff) +
+ 28*(colLeft&0xff);
+ lumRight = 77*(colRight>>16&0xff) +
+ 151*(colRight>>8&0xff) +
+ 28*(colRight&0xff);
+ lumUp = 77*(colUp>>16&0xff) +
+ 151*(colUp>>8&0xff) +
+ 28*(colUp&0xff);
+ lumDown = 77*(colDown>>16&0xff) +
+ 151*(colDown>>8&0xff) +
+ 28*(colDown&0xff);
+
+ if (lumLeft > currLum) {
+ colOut = colLeft;
+ currLum = lumLeft;
+ }
+ if (lumRight > currLum) {
+ colOut = colRight;
+ currLum = lumRight;
+ }
+ if (lumUp > currLum) {
+ colOut = colUp;
+ currLum = lumUp;
+ }
+ if (lumDown > currLum) {
+ colOut = colDown;
+ currLum = lumDown;
+ }
+ out[currIdx++]=colOut;
+ }
+ }
+ Filters._setPixels(pixels, out);
+};
+
+/**
+ * increases the bright areas in an image
+ * @param {Canvas} canvas
+ *
+ */
+Filters.erode = function(canvas) {
+ var pixels = Filters._toPixels(canvas);
+ var currIdx = 0;
+ var maxIdx = pixels.length ? pixels.length/4 : 0;
+ var out = new Int32Array(maxIdx);
+ var currRowIdx, maxRowIdx, colOrig, colOut, currLum;
+ var idxRight, idxLeft, idxUp, idxDown,
+ colRight, colLeft, colUp, colDown,
+ lumRight, lumLeft, lumUp, lumDown;
+
+ while(currIdx < maxIdx) {
+ currRowIdx = currIdx;
+ maxRowIdx = currIdx + canvas.width;
+ while (currIdx < maxRowIdx) {
+ colOrig = colOut = Filters._getARGB(pixels, currIdx);
+ idxLeft = currIdx - 1;
+ idxRight = currIdx + 1;
+ idxUp = currIdx - canvas.width;
+ idxDown = currIdx + canvas.width;
+
+ if (idxLeft < currRowIdx) {
+ idxLeft = currIdx;
+ }
+ if (idxRight >= maxRowIdx) {
+ idxRight = currIdx;
+ }
+ if (idxUp < 0) {
+ idxUp = 0;
+ }
+ if (idxDown >= maxIdx) {
+ idxDown = currIdx;
+ }
+ colUp = Filters._getARGB(pixels, idxUp);
+ colLeft = Filters._getARGB(pixels, idxLeft);
+ colDown = Filters._getARGB(pixels, idxDown);
+ colRight = Filters._getARGB(pixels, idxRight);
+
+ //compute luminance
+ currLum = 77*(colOrig>>16&0xff) +
+ 151*(colOrig>>8&0xff) +
+ 28*(colOrig&0xff);
+ lumLeft = 77*(colLeft>>16&0xff) +
+ 151*(colLeft>>8&0xff) +
+ 28*(colLeft&0xff);
+ lumRight = 77*(colRight>>16&0xff) +
+ 151*(colRight>>8&0xff) +
+ 28*(colRight&0xff);
+ lumUp = 77*(colUp>>16&0xff) +
+ 151*(colUp>>8&0xff) +
+ 28*(colUp&0xff);
+ lumDown = 77*(colDown>>16&0xff) +
+ 151*(colDown>>8&0xff) +
+ 28*(colDown&0xff);
+
+ if (lumLeft < currLum) {
+ colOut = colLeft;
+ currLum = lumLeft;
+ }
+ if (lumRight < currLum) {
+ colOut = colRight;
+ currLum = lumRight;
+ }
+ if (lumUp < currLum) {
+ colOut = colUp;
+ currLum = lumUp;
+ }
+ if (lumDown < currLum) {
+ colOut = colDown;
+ currLum = lumDown;
+ }
+
+ out[currIdx++]=colOut;
+ }
+ }
+ Filters._setPixels(pixels, out);
+};
+
+// BLUR
+
+// internal kernel stuff for the gaussian blur filter
+var blurRadius;
+var blurKernelSize;
+var blurKernel;
+var blurMult;
+
+/*
+ * Port of https://github.com/processing/processing/blob/
+ * master/core/src/processing/core/PImage.java#L1250
+ *
+ * Optimized code for building the blur kernel.
+ * further optimized blur code (approx. 15% for radius=20)
+ * bigger speed gains for larger radii (~30%)
+ * added support for various image types (ALPHA, RGB, ARGB)
+ * [toxi 050728]
+ */
+function buildBlurKernel(r) {
+ var radius = (r * 3.5)|0;
+ radius = (radius < 1) ? 1 : ((radius < 248) ? radius : 248);
+
+ if (blurRadius !== radius) {
+ blurRadius = radius;
+ blurKernelSize = 1 + blurRadius<<1;
+ blurKernel = new Int32Array(blurKernelSize);
+ blurMult = new Array(blurKernelSize);
+ for(var l = 0; l < blurKernelSize; l++){
+ blurMult[l] = new Int32Array(256);
+ }
+
+ var bk,bki;
+ var bm,bmi;
+
+ for (var i = 1, radiusi = radius - 1; i < radius; i++) {
+ blurKernel[radius+i] = blurKernel[radiusi] = bki = radiusi * radiusi;
+ bm = blurMult[radius+i];
+ bmi = blurMult[radiusi--];
+ for (var j = 0; j < 256; j++){
+ bm[j] = bmi[j] = bki * j;
+ }
+ }
+ bk = blurKernel[radius] = radius * radius;
+ bm = blurMult[radius];
+
+ for (var k = 0; k < 256; k++){
+ bm[k] = bk * k;
+ }
+ }
+
+}
+
+// Port of https://github.com/processing/processing/blob/
+// master/core/src/processing/core/PImage.java#L1433
+function blurARGB(canvas, radius) {
+ var pixels = Filters._toPixels(canvas);
+ var width = canvas.width;
+ var height = canvas.height;
+ var numPackedPixels = width * height;
+ var argb = new Int32Array(numPackedPixels);
+ for (var j = 0; j < numPackedPixels; j++) {
+ argb[j] = Filters._getARGB(pixels, j);
+ }
+ var sum, cr, cg, cb, ca;
+ var read, ri, ym, ymi, bk0;
+ var a2 = new Int32Array(numPackedPixels);
+ var r2 = new Int32Array(numPackedPixels);
+ var g2 = new Int32Array(numPackedPixels);
+ var b2 = new Int32Array(numPackedPixels);
+ var yi = 0;
+ buildBlurKernel(radius);
+ var x, y, i;
+ var bm;
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ cb = cg = cr = ca = sum = 0;
+ read = x - blurRadius;
+ if (read < 0) {
+ bk0 = -read;
+ read = 0;
+ } else {
+ if (read >= width) {
+ break;
+ }
+ bk0 = 0;
+ }
+ for (i = bk0; i < blurKernelSize; i++) {
+ if (read >= width) {
+ break;
+ }
+ var c = argb[read + yi];
+ bm = blurMult[i];
+ ca += bm[(c & -16777216) >>> 24];
+ cr += bm[(c & 16711680) >> 16];
+ cg += bm[(c & 65280) >> 8];
+ cb += bm[c & 255];
+ sum += blurKernel[i];
+ read++;
+ }
+ ri = yi + x;
+ a2[ri] = ca / sum;
+ r2[ri] = cr / sum;
+ g2[ri] = cg / sum;
+ b2[ri] = cb / sum;
+ }
+ yi += width;
+ }
+ yi = 0;
+ ym = -blurRadius;
+ ymi = ym * width;
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ cb = cg = cr = ca = sum = 0;
+ if (ym < 0) {
+ bk0 = ri = -ym;
+ read = x;
+ } else {
+ if (ym >= height) {
+ break;
+ }
+ bk0 = 0;
+ ri = ym;
+ read = x + ymi;
+ }
+ for (i = bk0; i < blurKernelSize; i++) {
+ if (ri >= height) {
+ break;
+ }
+ bm = blurMult[i];
+ ca += bm[a2[read]];
+ cr += bm[r2[read]];
+ cg += bm[g2[read]];
+ cb += bm[b2[read]];
+ sum += blurKernel[i];
+ ri++;
+ read += width;
+ }
+ argb[x + yi] = (ca/sum)<<24 | (cr/sum)<<16 | (cg/sum)<<8 | (cb/sum);
+ }
+ yi += width;
+ ymi += width;
+ ym++;
+ }
+ Filters._setPixels(pixels, argb);
+}
+
+Filters.blur = function(canvas, radius){
+ blurARGB(canvas, radius);
+};
+
+
+module.exports = Filters;
+
+},{}],66:[function(_dereq_,module,exports){
+/**
+ * @module Image
+ * @submodule Image
+ * @for p5
+ * @requires core
+ */
+
+/**
+ * This module defines the p5 methods for the p5.Image class
+ * for drawing images to the main display canvas.
+ */
+'use strict';
+
+
+var p5 = _dereq_('../core/core');
+
+/* global frames:true */// This is not global, but JSHint is not aware that
+// this module is implicitly enclosed with Browserify: this overrides the
+// redefined-global error and permits using the name "frames" for the array
+// of saved animation frames.
+var frames = [];
+
+
+/**
+ * Creates a new p5.Image (the datatype for storing images). This provides a
+ * fresh buffer of pixels to play with. Set the size of the buffer with the
+ * width and height parameters.
+ * <br><br>
+ * .pixels gives access to an array containing the values for all the pixels
+ * in the display window.
+ * These values are numbers. This array is the size (including an appropriate
+ * factor for the pixelDensity) of the display window x4,
+ * representing the R, G, B, A values in order for each pixel, moving from
+ * left to right across each row, then down each column. See .pixels for
+ * more info. It may also be simpler to use set() or get().
+ * <br><br>
+ * Before accessing the pixels of an image, the data must loaded with the
+ * loadPixels() function. After the array data has been modified, the
+ * updatePixels() function must be run to update the changes.
+ *
+ * @method createImage
+ * @param {Integer} width width in pixels
+ * @param {Integer} height height in pixels
+ * @return {p5.Image} the p5.Image object
+ * @example
+ * <div>
+ * <code>
+ * img = createImage(66, 66);
+ * img.loadPixels();
+ * for (i = 0; i < img.width; i++) {
+ * for (j = 0; j < img.height; j++) {
+ * img.set(i, j, color(0, 90, 102));
+ * }
+ * }
+ * img.updatePixels();
+ * image(img, 17, 17);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * img = createImage(66, 66);
+ * img.loadPixels();
+ * for (i = 0; i < img.width; i++) {
+ * for (j = 0; j < img.height; j++) {
+ * img.set(i, j, color(0, 90, 102, i % img.width * 2));
+ * }
+ * }
+ * img.updatePixels();
+ * image(img, 17, 17);
+ * image(img, 34, 34);
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var pink = color(255, 102, 204);
+ * img = createImage(66, 66);
+ * img.loadPixels();
+ * var d = pixelDensity;
+ * var halfImage = 4 * (width * d) * (height/2 * d);
+ * for (var i = 0; i < halfImage; i+=4) {
+ * img.pixels[i] = red(pink);
+ * img.pixels[i+1] = green(pink);
+ * img.pixels[i+2] = blue(pink);
+ * img.pixels[i+3] = alpha(pink);
+ * }
+ * img.updatePixels();
+ * image(img, 17, 17);
+ * </code>
+ * </div>
+ */
+p5.prototype.createImage = function(width, height) {
+ return new p5.Image(width, height);
+};
+
+/**
+ * Save the current canvas as an image. In Safari, this will open the
+ * image in the window and the user must provide their own
+ * filename on save-as. Other browsers will either save the
+ * file immediately, or prompt the user with a dialogue window.
+ *
+ * @method saveCanvas
+ * @param {[selectedCanvas]} canvas a variable representing a
+ * specific html5 canvas (optional)
+ * @param {[String]} filename
+ * @param {[String]} extension 'jpg' or 'png'
+ * @example
+ * <div class='norender'><code>
+ * function setup() {
+ * var c = createCanvas(100, 100);
+ * background(255, 0, 0);
+ * saveCanvas(c, 'myCanvas', 'jpg');
+ * }
+ * </code></div>
+ * <div class='norender'><code>
+ * // note that this example has the same result as above
+ * // if no canvas is specified, defaults to main canvas
+ * function setup() {
+ * createCanvas(100, 100);
+ * background(255, 0, 0);
+ * saveCanvas('myCanvas', 'jpg');
+ * }
+ * </code></div>
+ * <div class='norender'><code>
+ * // all of the following are valid
+ * saveCanvas(c, 'myCanvas', 'jpg');
+ * saveCanvas(c, 'myCanvas');
+ * saveCanvas(c);
+ * saveCanvas('myCanvas', 'png');
+ * saveCanvas('myCanvas');
+ * saveCanvas();
+ * </code></div>
+ */
+p5.prototype.saveCanvas = function() {
+
+ var cnv, filename, extension;
+ if (arguments.length === 3) {
+ cnv = arguments[0];
+ filename = arguments[1];
+ extension = arguments[2];
+ } else if (arguments.length === 2) {
+ if (typeof arguments[0] === 'object') {
+ cnv = arguments[0];
+ filename = arguments[1];
+ } else {
+ filename = arguments[0];
+ extension = arguments[1];
+ }
+ } else if (arguments.length === 1) {
+ if (typeof arguments[0] === 'object') {
+ cnv = arguments[0];
+ } else {
+ filename = arguments[0];
+ }
+ }
+
+ if (cnv instanceof p5.Element) {
+ cnv = cnv.elt;
+ }
+ if (!(cnv instanceof HTMLCanvasElement)) {
+ cnv = null;
+ }
+
+ if (!extension) {
+ extension = p5.prototype._checkFileExtension(filename, extension)[1];
+ if (extension === '') {
+ extension = 'png';
+ }
+ }
+
+ if (!cnv) {
+ if (this._curElement && this._curElement.elt) {
+ cnv = this._curElement.elt;
+ }
+ }
+
+ if ( p5.prototype._isSafari() ) {
+ var aText = 'Hello, Safari user!\n';
+ aText += 'Now capturing a screenshot...\n';
+ aText += 'To save this image,\n';
+ aText += 'go to File --> Save As.\n';
+ alert(aText);
+ window.location.href = cnv.toDataURL();
+ } else {
+ var mimeType;
+ if (typeof(extension) === 'undefined') {
+ extension = 'png';
+ mimeType = 'image/png';
+ }
+ else {
+ switch(extension){
+ case 'png':
+ mimeType = 'image/png';
+ break;
+ case 'jpeg':
+ mimeType = 'image/jpeg';
+ break;
+ case 'jpg':
+ mimeType = 'image/jpeg';
+ break;
+ default:
+ mimeType = 'image/png';
+ break;
+ }
+ }
+ var downloadMime = 'image/octet-stream';
+ var imageData = cnv.toDataURL(mimeType);
+ imageData = imageData.replace(mimeType, downloadMime);
+
+ p5.prototype.downloadFile(imageData, filename, extension);
+ }
+};
+
+/**
+ * Capture a sequence of frames that can be used to create a movie.
+ * Accepts a callback. For example, you may wish to send the frames
+ * to a server where they can be stored or converted into a movie.
+ * If no callback is provided, the browser will attempt to download
+ * all of the images that have just been created.
+ *
+ * @method saveFrames
+ * @param {String} filename
+ * @param {String} extension 'jpg' or 'png'
+ * @param {Number} duration Duration in seconds to save the frames for.
+ * @param {Number} framerate Framerate to save the frames in.
+ * @param {Function} [callback] A callback function that will be executed
+ to handle the image data. This function
+ should accept an array as argument. The
+ array will contain the spcecified number of
+ frames of objects. Each object have three
+ properties: imageData - an
+ image/octet-stream, filename and extension.
+ */
+p5.prototype.saveFrames = function(fName, ext, _duration, _fps, callback) {
+ var duration = _duration || 3;
+ duration = p5.prototype.constrain(duration, 0, 15);
+ duration = duration * 1000;
+ var fps = _fps || 15;
+ fps = p5.prototype.constrain(fps, 0, 22);
+ var count = 0;
+
+ var makeFrame = p5.prototype._makeFrame;
+ var cnv = this._curElement.elt;
+ var frameFactory = setInterval(function(){
+ makeFrame(fName + count, ext, cnv);
+ count++;
+ },1000/fps);
+
+ setTimeout(function(){
+ clearInterval(frameFactory);
+ if (callback) {
+ callback(frames);
+ }
+ else {
+ for (var i = 0; i < frames.length; i++) {
+ var f = frames[i];
+ p5.prototype.downloadFile(f.imageData, f.filename, f.ext);
+ }
+ }
+ frames = []; // clear frames
+ }, duration + 0.01);
+};
+
+p5.prototype._makeFrame = function(filename, extension, _cnv) {
+ var cnv;
+ if (this) {
+ cnv = this._curElement.elt;
+ } else {
+ cnv = _cnv;
+ }
+ var mimeType;
+ if (!extension) {
+ extension = 'png';
+ mimeType = 'image/png';
+ }
+ else {
+ switch(extension.toLowerCase()){
+ case 'png':
+ mimeType = 'image/png';
+ break;
+ case 'jpeg':
+ mimeType = 'image/jpeg';
+ break;
+ case 'jpg':
+ mimeType = 'image/jpeg';
+ break;
+ default:
+ mimeType = 'image/png';
+ break;
+ }
+ }
+ var downloadMime = 'image/octet-stream';
+ var imageData = cnv.toDataURL(mimeType);
+ imageData = imageData.replace(mimeType, downloadMime);
+
+ var thisFrame = {};
+ thisFrame.imageData = imageData;
+ thisFrame.filename = filename;
+ thisFrame.ext = extension;
+ frames.push(thisFrame);
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],67:[function(_dereq_,module,exports){
+/**
+ * @module Image
+ * @submodule Loading & Displaying
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var Filters = _dereq_('./filters');
+var canvas = _dereq_('../core/canvas');
+var constants = _dereq_('../core/constants');
+
+_dereq_('../core/error_helpers');
+
+/**
+ * Loads an image from a path and creates a p5.Image from it.
+ * <br><br>
+ * The image may not be immediately available for rendering
+ * If you want to ensure that the image is ready before doing
+ * anything with it, place the loadImage() call in preload().
+ * You may also supply a callback function to handle the image when it's ready.
+ * <br><br>
+ * The path to the image should be relative to the HTML file
+ * that links in your sketch. Loading an from a URL or other
+ * remote location may be blocked due to your browser's built-in
+ * security.
+ *
+ * @method loadImage
+ * @param {String} path Path of the image to be loaded
+ * @param {Function(p5.Image)} [successCallback] Function to be called once
+ * the image is loaded. Will be passed the
+ * p5.Image.
+ * @param {Function(Event)} [failureCallback] called with event error if
+ * the image fails to load.
+ * @return {p5.Image} the p5.Image object
+ * @example
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/laDefense.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * }
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * function setup() {
+ * // here we use a callback to display the image after loading
+ * loadImage("assets/laDefense.jpg", function(img) {
+ * image(img, 0, 0);
+ * });
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.loadImage = function(path, successCallback, failureCallback) {
+ var img = new Image();
+ var pImg = new p5.Image(1, 1, this);
+ var decrementPreload = p5._getDecrementPreload.apply(this, arguments);
+
+ img.onload = function() {
+ pImg.width = pImg.canvas.width = img.width;
+ pImg.height = pImg.canvas.height = img.height;
+
+ // Draw the image into the backing canvas of the p5.Image
+ pImg.drawingContext.drawImage(img, 0, 0);
+
+ if (typeof successCallback === 'function') {
+ successCallback(pImg);
+ }
+ if (decrementPreload && (successCallback !== decrementPreload)) {
+ decrementPreload();
+ }
+ };
+ img.onerror = function(e) {
+ p5._friendlyFileLoadError(0,img.src);
+ // don't get failure callback mixed up with decrementPreload
+ if ((typeof failureCallback === 'function') &&
+ (failureCallback !== decrementPreload)) {
+ failureCallback(e);
+ }
+ };
+
+ //set crossOrigin in case image is served which CORS headers
+ //this will let us draw to canvas without tainting it.
+ //see https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image
+ // When using data-uris the file will be loaded locally
+ // so we don't need to worry about crossOrigin with base64 file types
+ if(path.indexOf('data:image/') !== 0) {
+ img.crossOrigin = 'Anonymous';
+ }
+
+ //start loading the image
+ img.src = path;
+
+ return pImg;
+};
+
+/**
+ * Validates clipping params. Per drawImage spec sWidth and sHight cannot be
+ * negative or greater than image intrinsic width and height
+ * @private
+ * @param {Number} sVal
+ * @param {Number} iVal
+ * @returns {Number}
+ * @private
+ */
+function _sAssign(sVal, iVal) {
+ if (sVal > 0 && sVal < iVal) {
+ return sVal;
+ }
+ else {
+ return iVal;
+ }
+}
+
+/**
+ * Draw an image to the main canvas of the p5js sketch
+ *
+ * @method image
+ * @param {p5.Image} img the image to display
+ * @param {Number} [sx=0] The X coordinate of the top left corner of the
+ * sub-rectangle of the source image to draw into
+ * the destination canvas.
+ * @param {Number} [sy=0] The Y coordinate of the top left corner of the
+ * sub-rectangle of the source image to draw into
+ * the destination canvas.
+ * @param {Number} [sWidth=img.width] The width of the sub-rectangle of the
+ * source image to draw into the destination
+ * canvas.
+ * @param {Number} [sHeight=img.height] The height of the sub-rectangle of the
+ * source image to draw into the
+ * destination context.
+ * @param {Number} [dx=0] The X coordinate in the destination canvas at
+ * which to place the top-left corner of the
+ * source image.
+ * @param {Number} [dy=0] The Y coordinate in the destination canvas at
+ * which to place the top-left corner of the
+ * source image.
+ * @param {Number} [dWidth] The width to draw the image in the destination
+ * canvas. This allows scaling of the drawn image.
+ * @param {Number} [dHeight] The height to draw the image in the destination
+ * canvas. This allows scaling of the drawn image.
+ * @example
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/laDefense.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * image(img, 0, 0, 100, 100);
+ * image(img, 0, 0, 100, 100, 0, 0, 100, 100);
+ * }
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * function setup() {
+ * // here we use a callback to display the image after loading
+ * loadImage("assets/laDefense.jpg", function(img) {
+ * image(img, 0, 0);
+ * });
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.image =
+ function(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) {
+ // Temporarily disabling until options for p5.Graphics are added.
+ // var args = new Array(arguments.length);
+ // for (var i = 0; i < args.length; ++i) {
+ // args[i] = arguments[i];
+ // }
+ // this._validateParameters(
+ // 'image',
+ // args,
+ // [
+ // ['p5.Image', 'Number', 'Number'],
+ // ['p5.Image', 'Number', 'Number', 'Number', 'Number']
+ // ]
+ // );
+
+ // set defaults per spec: https://goo.gl/3ykfOq
+ if (arguments.length <= 5) {
+ dx = sx || 0;
+ dy = sy || 0;
+ sx = 0;
+ sy = 0;
+ if (img.elt && img.elt.videoWidth && !img.canvas) { // video no canvas
+ var actualW = img.elt.videoWidth;
+ var actualH = img.elt.videoHeight;
+ dWidth = sWidth || img.elt.width;
+ dHeight = sHeight || img.elt.width*actualH/actualW;
+ sWidth = actualW;
+ sHeight = actualH;
+ } else {
+ dWidth = sWidth || img.width;
+ dHeight = sHeight || img.height;
+ sWidth = img.width;
+ sHeight = img.height;
+ }
+ } else if (arguments.length === 9) {
+ sx = sx || 0;
+ sy = sy || 0;
+ sWidth = _sAssign(sWidth, img.width);
+ sHeight = _sAssign(sHeight, img.height);
+
+ dx = dx || 0;
+ dy = dy || 0;
+ dWidth = dWidth || img.width;
+ dHeight = dHeight || img.height;
+ } else {
+ throw 'Wrong number of arguments to image()';
+ }
+
+ var vals = canvas.modeAdjust(dx, dy, dWidth, dHeight,
+ this._renderer._imageMode);
+
+ // tint the image if there is a tint
+ this._renderer.image(img, sx, sy, sWidth, sHeight, vals.x, vals.y, vals.w,
+ vals.h);
+};
+
+/**
+ * Sets the fill value for displaying images. Images can be tinted to
+ * specified colors or made transparent by including an alpha value.
+ * <br><br>
+ * To apply transparency to an image without affecting its color, use
+ * white as the tint color and specify an alpha value. For instance,
+ * tint(255, 128) will make an image 50% transparent (assuming the default
+ * alpha range of 0-255, which can be changed with colorMode()).
+ * <br><br>
+ * The value for the gray parameter must be less than or equal to the current
+ * maximum value as specified by colorMode(). The default maximum value is
+ * 255.
+ *
+ * @method tint
+ * @param {Number|Array} v1 gray value, red or hue value (depending on the
+ * current color mode), or color Array
+ * @param {Number|Array} [v2] green or saturation value (depending on the
+ * current color mode)
+ * @param {Number|Array} [v3] blue or brightness value (depending on the
+ * current color mode)
+ * @param {Number|Array} [a] opacity of the background
+ * @example
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/laDefense.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * tint(0, 153, 204); // Tint blue
+ * image(img, 50, 0);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/laDefense.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * tint(0, 153, 204, 126); // Tint blue and set transparency
+ * image(img, 50, 0);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/laDefense.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * tint(255, 126); // Apply transparency without changing color
+ * image(img, 50, 0);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.tint = function () {
+ var c = this.color.apply(this, arguments);
+ this._renderer._tint = c.levels;
+};
+
+/**
+ * Removes the current fill value for displaying images and reverts to
+ * displaying images with their original hues.
+ *
+ * @method noTint
+ * @example
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * tint(0, 153, 204); // Tint blue
+ * image(img, 0, 0);
+ * noTint(); // Disable tint
+ * image(img, 50, 0);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.noTint = function() {
+ this._renderer._tint = null;
+};
+
+/**
+ * Apply the current tint color to the input image, return the resulting
+ * canvas.
+ *
+ * @param {p5.Image} The image to be tinted
+ * @return {canvas} The resulting tinted canvas
+ *
+ */
+p5.prototype._getTintedImageCanvas = function(img) {
+ if (!img.canvas) {
+ return img;
+ }
+ var pixels = Filters._toPixels(img.canvas);
+ var tmpCanvas = document.createElement('canvas');
+ tmpCanvas.width = img.canvas.width;
+ tmpCanvas.height = img.canvas.height;
+ var tmpCtx = tmpCanvas.getContext('2d');
+ var id = tmpCtx.createImageData(img.canvas.width, img.canvas.height);
+ var newPixels = id.data;
+
+ for(var i = 0; i < pixels.length; i += 4) {
+ var r = pixels[i];
+ var g = pixels[i+1];
+ var b = pixels[i+2];
+ var a = pixels[i+3];
+
+ newPixels[i] = r*this._renderer._tint[0]/255;
+ newPixels[i+1] = g*this._renderer._tint[1]/255;
+ newPixels[i+2] = b*this._renderer._tint[2]/255;
+ newPixels[i+3] = a*this._renderer._tint[3]/255;
+ }
+
+ tmpCtx.putImageData(id, 0, 0);
+ return tmpCanvas;
+};
+
+/**
+ * Set image mode. Modifies the location from which images are drawn by
+ * changing the way in which parameters given to image() are interpreted.
+ * The default mode is imageMode(CORNER), which interprets the second and
+ * third parameters of image() as the upper-left corner of the image. If
+ * two additional parameters are specified, they are used to set the image's
+ * width and height.
+ * <br><br>
+ * imageMode(CORNERS) interprets the second and third parameters of image()
+ * as the location of one corner, and the fourth and fifth parameters as the
+ * opposite corner.
+ * <br><br>
+ * imageMode(CENTER) interprets the second and third parameters of image()
+ * as the image's center point. If two additional parameters are specified,
+ * they are used to set the image's width and height.
+ *
+ * @method imageMode
+ * @param {String} m The mode: either CORNER, CORNERS, or CENTER.
+ * @example
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * imageMode(CORNER);
+ * image(img, 10, 10, 50, 50);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * imageMode(CORNERS);
+ * image(img, 10, 10, 90, 40);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * imageMode(CENTER);
+ * image(img, 50, 50, 80, 80);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.imageMode = function(m) {
+ if (m === constants.CORNER ||
+ m === constants.CORNERS ||
+ m === constants.CENTER) {
+ this._renderer._imageMode = m;
+ }
+};
+
+
+module.exports = p5;
+
+},{"../core/canvas":46,"../core/constants":47,"../core/core":48,"../core/error_helpers":51,"./filters":65}],68:[function(_dereq_,module,exports){
+/**
+ * @module Image
+ * @submodule Image
+ * @requires core
+ * @requires constants
+ * @requires filters
+ */
+
+/**
+ * This module defines the p5.Image class and P5 methods for
+ * drawing images to the main display canvas.
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var Filters = _dereq_('./filters');
+
+/*
+ * Class methods
+ */
+
+/**
+ * Creates a new p5.Image. A p5.Image is a canvas backed representation of an
+ * image.
+ * <br><br>
+ * p5 can display .gif, .jpg and .png images. Images may be displayed
+ * in 2D and 3D space. Before an image is used, it must be loaded with the
+ * loadImage() function. The p5.Image class contains fields for the width and
+ * height of the image, as well as an array called pixels[] that contains the
+ * values for every pixel in the image.
+ * <br><br>
+ * The methods described below allow easy access to the image's pixels and
+ * alpha channel and simplify the process of compositing.
+ * <br><br>
+ * Before using the pixels[] array, be sure to use the loadPixels() method on
+ * the image to make sure that the pixel data is properly loaded.
+ *
+ * @class p5.Image
+ * @constructor
+ * @param {Number} width
+ * @param {Number} height
+ * @param {Object} pInst An instance of a p5 sketch.
+ */
+p5.Image = function(width, height){
+ /**
+ * Image width.
+ * @property width
+ * @example
+ * <div><code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ * image(img, 0, 0);
+ * for (var i=0; i < img.width; i++) {
+ * var c = img.get(i, img.height/2);
+ * stroke(c);
+ * line(i, height/2, i, height);
+ * }
+ * }
+ * </code></div>
+ */
+ this.width = width;
+ /**
+ * Image height.
+ * @property height
+ * @example
+ * <div><code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ * image(img, 0, 0);
+ * for (var i=0; i < img.height; i++) {
+ * var c = img.get(img.width/2, i);
+ * stroke(c);
+ * line(0, i, width/2, i);
+ * }
+ * }
+ * </code></div>
+ */
+ this.height = height;
+ this.canvas = document.createElement('canvas');
+ this.canvas.width = this.width;
+ this.canvas.height = this.height;
+ this.drawingContext = this.canvas.getContext('2d');
+ this._pixelDensity = 1;
+ //used for webgl texturing only
+ this.isTexture = false;
+ /**
+ * Array containing the values for all the pixels in the display window.
+ * These values are numbers. This array is the size (include an appropriate
+ * factor for pixelDensity) of the display window x4,
+ * representing the R, G, B, A values in order for each pixel, moving from
+ * left to right across each row, then down each column. Retina and other
+ * high denisty displays may have more pixels[] (by a factor of
+ * pixelDensity^2).
+ * For example, if the image is 100x100 pixels, there will be 40,000. With
+ * pixelDensity = 2, there will be 160,000. The first four values
+ * (indices 0-3) in the array will be the R, G, B, A values of the pixel at
+ * (0, 0). The second four values (indices 4-7) will contain the R, G, B, A
+ * values of the pixel at (1, 0). More generally, to set values for a pixel
+ * at (x, y):
+ * <code><pre>var d = pixelDensity;
+ * for (var i = 0; i < d; i++) {
+ * for (var j = 0; j < d; j++) {
+ * // loop over
+ * idx = 4*((y * d + j) * width * d + (x * d + i));
+ * pixels[idx] = r;
+ * pixels[idx+1] = g;
+ * pixels[idx+2] = b;
+ * pixels[idx+3] = a;
+ * }
+ * }
+ * </pre></code>
+ * <br><br>
+ * Before accessing this array, the data must loaded with the loadPixels()
+ * function. After the array data has been modified, the updatePixels()
+ * function must be run to update the changes.
+ * @property pixels[]
+ * @example
+ * <div>
+ * <code>
+ * img = createImage(66, 66);
+ * img.loadPixels();
+ * for (i = 0; i < img.width; i++) {
+ * for (j = 0; j < img.height; j++) {
+ * img.set(i, j, color(0, 90, 102));
+ * }
+ * }
+ * img.updatePixels();
+ * image(img, 17, 17);
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * var pink = color(255, 102, 204);
+ * img = createImage(66, 66);
+ * img.loadPixels();
+ * for (var i = 0; i < 4*(width*height/2); i+=4) {
+ * img.pixels[i] = red(pink);
+ * img.pixels[i+1] = green(pink);
+ * img.pixels[i+2] = blue(pink);
+ * img.pixels[i+3] = alpha(pink);
+ * }
+ * img.updatePixels();
+ * image(img, 17, 17);
+ * </code>
+ * </div>
+ */
+ this.pixels = [];
+};
+
+/**
+ * Helper fxn for sharing pixel methods
+ *
+ */
+p5.Image.prototype._setProperty = function (prop, value) {
+ this[prop] = value;
+};
+
+/**
+ * Loads the pixels data for this image into the [pixels] attribute.
+ *
+ * @method loadPixels
+ * @example
+ * <div><code>
+ * var myImage;
+ * var halfImage;
+ *
+ * function preload() {
+ * myImage = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * myImage.loadPixels();
+ * halfImage = 4 * width * height/2;
+ * for(var i = 0; i < halfImage; i++){
+ * myImage.pixels[i+halfImage] = myImage.pixels[i];
+ * }
+ * myImage.updatePixels();
+ * }
+ *
+ * function draw() {
+ * image(myImage, 0, 0);
+ * }
+ * </code></div>
+ */
+p5.Image.prototype.loadPixels = function(){
+ p5.Renderer2D.prototype.loadPixels.call(this);
+};
+
+/**
+ * Updates the backing canvas for this image with the contents of
+ * the [pixels] array.
+ *
+ * @method updatePixels
+ * @param {Integer|undefined} x x-offset of the target update area for the
+ * underlying canvas
+ * @param {Integer|undefined} y y-offset of the target update area for the
+ * underlying canvas
+ * @param {Integer|undefined} w height of the target update area for the
+ * underlying canvas
+ * @param {Integer|undefined} h height of the target update area for the
+ * underlying canvas
+ * @example
+ * <div><code>
+ * var myImage;
+ * var halfImage;
+ *
+ * function preload() {
+ * myImage = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * myImage.loadPixels();
+ * halfImage = 4 * width * height/2;
+ * for(var i = 0; i < halfImage; i++){
+ * myImage.pixels[i+halfImage] = myImage.pixels[i];
+ * }
+ * myImage.updatePixels();
+ * }
+ *
+ * function draw() {
+ * image(myImage, 0, 0);
+ * }
+ * </code></div>
+ */
+p5.Image.prototype.updatePixels = function(x, y, w, h){
+ p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h);
+};
+
+/**
+ * Get a region of pixels from an image.
+ *
+ * If no params are passed, those whole image is returned,
+ * if x and y are the only params passed a single pixel is extracted
+ * if all params are passed a rectangle region is extracted and a p5.Image
+ * is returned.
+ *
+ * Returns undefined if the region is outside the bounds of the image
+ *
+ * @method get
+ * @param {Number} [x] x-coordinate of the pixel
+ * @param {Number} [y] y-coordinate of the pixel
+ * @param {Number} [w] width
+ * @param {Number} [h] height
+ * @return {Array/Color | p5.Image} color of pixel at x,y in array format
+ * [R, G, B, A] or p5.Image
+ * @example
+ * <div><code>
+ * var myImage;
+ * var c;
+ *
+ * function preload() {
+ * myImage = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * background(myImage);
+ * noStroke();
+ * c = myImage.get(60, 90);
+ * fill(c);
+ * rect(25, 25, 50, 50);
+ * }
+ *
+ * //get() returns color here
+ * </code></div>
+ */
+p5.Image.prototype.get = function(x, y, w, h){
+ return p5.Renderer2D.prototype.get.call(this, x, y, w, h);
+};
+
+/**
+ * Set the color of a single pixel or write an image into
+ * this p5.Image.
+ *
+ * Note that for a large number of pixels this will
+ * be slower than directly manipulating the pixels array
+ * and then calling updatePixels().
+ *
+ * @method set
+ * @param {Number} x x-coordinate of the pixel
+ * @param {Number} y y-coordinate of the pixel
+ * @param {Number|Array|Object} a grayscale value | pixel array |
+ * a p5.Color | image to copy
+ * @example
+ * <div>
+ * <code>
+ * img = createImage(66, 66);
+ * img.loadPixels();
+ * for (i = 0; i < img.width; i++) {
+ * for (j = 0; j < img.height; j++) {
+ * img.set(i, j, color(0, 90, 102, i % img.width * 2));
+ * }
+ * }
+ * img.updatePixels();
+ * image(img, 17, 17);
+ * image(img, 34, 34);
+ * </code>
+ * </div>
+ */
+p5.Image.prototype.set = function(x, y, imgOrCol){
+ p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol);
+};
+
+/**
+ * Resize the image to a new width and height. To make the image scale
+ * proportionally, use 0 as the value for the wide or high parameter.
+ * For instance, to make the width of an image 150 pixels, and change
+ * the height using the same proportion, use resize(150, 0).
+ *
+ * @method resize
+ * @param {Number} width the resized image width
+ * @param {Number} height the resized image height
+ * @example
+ * <div><code>
+ * var img;
+ *
+ * function setup() {
+ * img = loadImage("assets/rockies.jpg");
+ * }
+
+ * function draw() {
+ * image(img, 0, 0);
+ * }
+ *
+ * function mousePressed() {
+ * img.resize(50, 100);
+ * }
+ * </code></div>
+ */
+p5.Image.prototype.resize = function(width, height){
+
+ // Copy contents to a temporary canvas, resize the original
+ // and then copy back.
+ //
+ // There is a faster approach that involves just one copy and swapping the
+ // this.canvas reference. We could switch to that approach if (as i think
+ // is the case) there an expectation that the user would not hold a
+ // reference to the backing canvas of a p5.Image. But since we do not
+ // enforce that at the moment, I am leaving in the slower, but safer
+ // implementation.
+
+ // auto-resize
+ if (width === 0 && height === 0) {
+ width = this.canvas.width;
+ height = this.canvas.height;
+ } else if (width === 0) {
+ width = this.canvas.width * height / this.canvas.height;
+ } else if (height === 0) {
+ height = this.canvas.height * width / this.canvas.width;
+ }
+
+ var tempCanvas = document.createElement('canvas');
+ tempCanvas.width = width;
+ tempCanvas.height = height;
+ tempCanvas.getContext('2d').drawImage(this.canvas,
+ 0, 0, this.canvas.width, this.canvas.height,
+ 0, 0, tempCanvas.width, tempCanvas.height
+ );
+
+
+ // Resize the original canvas, which will clear its contents
+ this.canvas.width = this.width = width;
+ this.canvas.height = this.height = height;
+
+ //Copy the image back
+
+ this.drawingContext.drawImage(tempCanvas,
+ 0, 0, width, height,
+ 0, 0, width, height
+ );
+
+ if(this.pixels.length > 0){
+ this.loadPixels();
+ }
+};
+
+/**
+ * Copies a region of pixels from one image to another. If no
+ * srcImage is specified this is used as the source. If the source
+ * and destination regions aren't the same size, it will
+ * automatically resize source pixels to fit the specified
+ * target region.
+ *
+ * @method copy
+ * @param {p5.Image|undefined} srcImage source image
+ * @param {Integer} sx X coordinate of the source's upper left corner
+ * @param {Integer} sy Y coordinate of the source's upper left corner
+ * @param {Integer} sw source image width
+ * @param {Integer} sh source image height
+ * @param {Integer} dx X coordinate of the destination's upper left corner
+ * @param {Integer} dy Y coordinate of the destination's upper left corner
+ * @param {Integer} dw destination image width
+ * @param {Integer} dh destination image height
+ * @example
+ * <div><code>
+ * var photo;
+ * var bricks;
+ * var x;
+ * var y;
+ *
+ * function preload() {
+ * photo = loadImage("assets/rockies.jpg");
+ * bricks = loadImage("assets/bricks.jpg");
+ * }
+ *
+ * function setup() {
+ * x = bricks.width/2;
+ * y = bricks.height/2;
+ * photo.copy(bricks, 0, 0, x, y, 0, 0, x, y);
+ * image(photo, 0, 0);
+ * }
+ * </code></div>
+ */
+p5.Image.prototype.copy = function () {
+ p5.prototype.copy.apply(this, arguments);
+};
+
+/**
+ * Masks part of an image from displaying by loading another
+ * image and using it's blue channel as an alpha channel for
+ * this image.
+ *
+ * @method mask
+ * @param {p5.Image} srcImage source image
+ * @example
+ * <div><code>
+ * var photo, maskImage;
+ * function preload() {
+ * photo = loadImage("assets/rockies.jpg");
+ * maskImage = loadImage("assets/mask2.png");
+ * }
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ * photo.mask(maskImage);
+ * image(photo, 0, 0);
+ * }
+ * </code></div>
+ *
+ * http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/
+ *
+ */
+// TODO: - Accept an array of alpha values.
+// - Use other channels of an image. p5 uses the
+// blue channel (which feels kind of arbitrary). Note: at the
+// moment this method does not match native processings original
+// functionality exactly.
+p5.Image.prototype.mask = function(p5Image) {
+ if(p5Image === undefined){
+ p5Image = this;
+ }
+ var currBlend = this.drawingContext.globalCompositeOperation;
+
+ var scaleFactor = 1;
+ if (p5Image instanceof p5.Renderer) {
+ scaleFactor = p5Image._pInst._pixelDensity;
+ }
+
+ var copyArgs = [
+ p5Image,
+ 0,
+ 0,
+ scaleFactor*p5Image.width,
+ scaleFactor*p5Image.height,
+ 0,
+ 0,
+ this.width,
+ this.height
+ ];
+
+ this.drawingContext.globalCompositeOperation = 'destination-in';
+ this.copy.apply(this, copyArgs);
+ this.drawingContext.globalCompositeOperation = currBlend;
+};
+
+/**
+ * Applies an image filter to a p5.Image
+ *
+ * @method filter
+ * @param {String} operation one of threshold, gray, invert, posterize and
+ * opaque see Filters.js for docs on each available
+ * filter
+ * @param {Number|undefined} value
+ * @example
+ * <div><code>
+ * var photo1;
+ * var photo2;
+ *
+ * function preload() {
+ * photo1 = loadImage("assets/rockies.jpg");
+ * photo2 = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * photo2.filter("gray");
+ * image(photo1, 0, 0);
+ * image(photo2, width/2, 0);
+ * }
+ * </code></div>
+ */
+p5.Image.prototype.filter = function(operation, value) {
+ Filters.apply(this.canvas, Filters[operation.toLowerCase()], value);
+};
+
+/**
+ * Copies a region of pixels from one image to another, using a specified
+ * blend mode to do the operation.
+ *
+ * @method blend
+ * @param {p5.Image|undefined} srcImage source image
+ * @param {Integer} sx X coordinate of the source's upper left corner
+ * @param {Integer} sy Y coordinate of the source's upper left corner
+ * @param {Integer} sw source image width
+ * @param {Integer} sh source image height
+ * @param {Integer} dx X coordinate of the destination's upper left corner
+ * @param {Integer} dy Y coordinate of the destination's upper left corner
+ * @param {Integer} dw destination image width
+ * @param {Integer} dh destination image height
+ * @param {Integer} blendMode the blend mode
+ *
+ * Available blend modes are: normal | multiply | screen | overlay |
+ * darken | lighten | color-dodge | color-burn | hard-light |
+ * soft-light | difference | exclusion | hue | saturation |
+ * color | luminosity
+ *
+ *
+ * http://blogs.adobe.com/webplatform/2013/01/28/blending-features-in-canvas/
+ *
+ */
+p5.Image.prototype.blend = function() {
+ p5.prototype.blend.apply(this, arguments);
+};
+
+/**
+ * Saves the image to a file and force the browser to download it.
+ * Accepts two strings for filename and file extension
+ * Supports png (default) and jpg.
+ *
+ * @method save
+ * @param {String} filename give your file a name
+ * @param {String} extension 'png' or 'jpg'
+ * @example
+ * <div><code>
+ * var photo;
+ *
+ * function preload() {
+ * photo = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function draw() {
+ * image(photo, 0, 0);
+ * }
+ *
+ * function keyTyped() {
+ * if (key == 's') {
+ * photo.save("photo", "png");
+ * }
+ * }
+ * </code></div>
+ */
+p5.Image.prototype.save = function(filename, extension) {
+ var mimeType;
+ if (!extension) {
+ extension = 'png';
+ mimeType = 'image/png';
+ }
+ else {
+ // en.wikipedia.org/wiki/Comparison_of_web_browsers#Image_format_support
+ switch(extension.toLowerCase()){
+ case 'png':
+ mimeType = 'image/png';
+ break;
+ case 'jpeg':
+ mimeType = 'image/jpeg';
+ break;
+ case 'jpg':
+ mimeType = 'image/jpeg';
+ break;
+ default:
+ mimeType = 'image/png';
+ break;
+ }
+ }
+ var downloadMime = 'image/octet-stream';
+ var imageData = this.canvas.toDataURL(mimeType);
+ imageData = imageData.replace(mimeType, downloadMime);
+
+ //Make the browser download the file
+ p5.prototype.downloadFile(imageData, filename, extension);
+};
+
+/**
+ * creates a gl texture
+ * used in WEBGL mode only
+ * @param {[type]} tex [description]
+ * @return {[type]} [description]
+ */
+p5.Image.prototype.createTexture = function(tex){
+ //this.texture = tex;
+ return this;
+};
+
+module.exports = p5.Image;
+
+},{"../core/core":48,"./filters":65}],69:[function(_dereq_,module,exports){
+/**
+ * @module Image
+ * @submodule Pixels
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var Filters = _dereq_('./filters');
+_dereq_('../color/p5.Color');
+
+/**
+ * <a href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference
+ * /Global_Objects/Uint8ClampedArray' target='_blank'>Uint8ClampedArray</a>
+ * containing the values for all the pixels in the display window.
+ * These values are numbers. This array is the size (include an appropriate
+ * factor for pixelDensity) of the display window x4,
+ * representing the R, G, B, A values in order for each pixel, moving from
+ * left to right across each row, then down each column. Retina and other
+ * high denisty displays will have more pixels[] (by a factor of
+ * pixelDensity^2).
+ * For example, if the image is 100x100 pixels, there will be 40,000. On a
+ * retina display, there will be 160,000.
+ * <br><br>
+ * The first four values (indices 0-3) in the array will be the R, G, B, A
+ * values of the pixel at (0, 0). The second four values (indices 4-7) will
+ * contain the R, G, B, A values of the pixel at (1, 0). More generally, to
+ * set values for a pixel at (x, y):
+ * <code><pre>
+ * var d = pixelDensity;
+ * for (var i = 0; i < d; i++) {
+ * for (var j = 0; j < d; j++) {
+ * // loop over
+ * idx = 4 * ((y * d + j) * width * d + (x * d + i));
+ * pixels[idx] = r;
+ * pixels[idx+1] = g;
+ * pixels[idx+2] = b;
+ * pixels[idx+3] = a;
+ * }
+ * }
+ * </pre></code>
+ *
+ * <p>While the above method is complex, it is flexible enough to work with
+ * any pixelDensity. Note that set() will automatically take care of
+ * setting all the appropriate values in pixels[] for a given (x, y) at
+ * any pixelDensity, but the performance may not be as fast when lots of
+ * modifications are made to the pixel array.
+ * <br><br>
+ * Before accessing this array, the data must loaded with the loadPixels()
+ * function. After the array data has been modified, the updatePixels()
+ * function must be run to update the changes.
+ * <br><br>
+ * Note that this is not a standard javascript array. This means that
+ * standard javascript functions such as <code>slice()</code> or
+ * <code>arrayCopy()</code> do not
+ * work.</p>
+ *
+ * @property pixels[]
+ * @example
+ * <div>
+ * <code>
+ * var pink = color(255, 102, 204);
+ * loadPixels();
+ * var d = pixelDensity();
+ * var halfImage = 4 * (width * d) * (height/2 * d);
+ * for (var i = 0; i < halfImage; i+=4) {
+ * pixels[i] = red(pink);
+ * pixels[i+1] = green(pink);
+ * pixels[i+2] = blue(pink);
+ * pixels[i+3] = alpha(pink);
+ * }
+ * updatePixels();
+ * </code>
+ * </div>
+ */
+p5.prototype.pixels = [];
+
+/**
+ * Copies a region of pixels from one image to another, using a specified
+ * blend mode to do the operation.<br><br>
+ * Available blend modes are: BLEND | DARKEST | LIGHTEST | DIFFERENCE |
+ * MULTIPLY| EXCLUSION | SCREEN | REPLACE | OVERLAY | HARD_LIGHT |
+ * SOFT_LIGHT | DODGE | BURN | ADD | NORMAL
+ *
+ *
+ * @method blend
+ * @param {p5.Image|undefined} srcImage source image
+ * @param {Integer} sx X coordinate of the source's upper left corner
+ * @param {Integer} sy Y coordinate of the source's upper left corner
+ * @param {Integer} sw source image width
+ * @param {Integer} sh source image height
+ * @param {Integer} dx X coordinate of the destination's upper left corner
+ * @param {Integer} dy Y coordinate of the destination's upper left corner
+ * @param {Integer} dw destination image width
+ * @param {Integer} dh destination image height
+ * @param {Integer} blendMode the blend mode
+ *
+ * @example
+ * <div><code>
+ * var img0;
+ * var img1;
+ *
+ * function preload() {
+ * img0 = loadImage("assets/rockies.jpg");
+ * img1 = loadImage("assets/bricks_third.jpg");
+ * }
+ *
+ * function setup() {
+ * background(img0);
+ * image(img1, 0, 0);
+ * blend(img1, 0, 0, 33, 100, 67, 0, 33, 100, LIGHTEST);
+ * }
+ * </code></div>
+ * <div><code>
+ * var img0;
+ * var img1;
+ *
+ * function preload() {
+ * img0 = loadImage("assets/rockies.jpg");
+ * img1 = loadImage("assets/bricks_third.jpg");
+ * }
+ *
+ * function setup() {
+ * background(img0);
+ * image(img1, 0, 0);
+ * blend(img1, 0, 0, 33, 100, 67, 0, 33, 100, DARKEST);
+ * }
+ * </code></div>
+ * <div><code>
+ * var img0;
+ * var img1;
+ *
+ * function preload() {
+ * img0 = loadImage("assets/rockies.jpg");
+ * img1 = loadImage("assets/bricks_third.jpg");
+ * }
+ *
+ * function setup() {
+ * background(img0);
+ * image(img1, 0, 0);
+ * blend(img1, 0, 0, 33, 100, 67, 0, 33, 100, ADD);
+ * }
+ * </code></div>
+ */
+p5.prototype.blend = function() {
+ this._renderer.blend.apply(this._renderer, arguments);
+};
+
+/**
+ * Copies a region of the canvas to another region of the canvas
+ * and copies a region of pixels from an image used as the srcImg parameter
+ * into the canvas srcImage is specified this is used as the source. If
+ * the source and destination regions aren't the same size, it will
+ * automatically resize source pixels to fit the specified
+ * target region.
+ *
+ * @method copy
+ * @param {p5.Image|undefined} srcImage source image
+ * @param {Integer} sx X coordinate of the source's upper left corner
+ * @param {Integer} sy Y coordinate of the source's upper left corner
+ * @param {Integer} sw source image width
+ * @param {Integer} sh source image height
+ * @param {Integer} dx X coordinate of the destination's upper left corner
+ * @param {Integer} dy Y coordinate of the destination's upper left corner
+ * @param {Integer} dw destination image width
+ * @param {Integer} dh destination image height
+ *
+ * @example
+ * <div><code>
+ * var img;
+ *
+ * function preload() {
+ * img = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * background(img);
+ * copy(img, 7, 22, 10, 10, 35, 25, 50, 50);
+ * stroke(255);
+ * noFill();
+ * // Rectangle shows area being copied
+ * rect(7, 22, 10, 10);
+ * }
+ * </code></div>
+ */
+p5.prototype.copy = function () {
+ p5.Renderer2D._copyHelper.apply(this, arguments);
+};
+
+/**
+ * Applies a filter to the canvas.
+ * <br><br>
+ *
+ * The presets options are:
+ * <br><br>
+ *
+ * THRESHOLD
+ * Converts the image to black and white pixels depending if they are above or
+ * below the threshold defined by the level parameter. The parameter must be
+ * between 0.0 (black) and 1.0 (white). If no level is specified, 0.5 is used.
+ * <br><br>
+ *
+ * GRAY
+ * Converts any colors in the image to grayscale equivalents. No parameter
+ * is used.
+ * <br><br>
+ *
+ * OPAQUE
+ * Sets the alpha channel to entirely opaque. No parameter is used.
+ * <br><br>
+ *
+ * INVERT
+ * Sets each pixel to its inverse value. No parameter is used.
+ * <br><br>
+ *
+ * POSTERIZE
+ * Limits each channel of the image to the number of colors specified as the
+ * parameter. The parameter can be set to values between 2 and 255, but
+ * results are most noticeable in the lower ranges.
+ * <br><br>
+ *
+ * BLUR
+ * Executes a Guassian blur with the level parameter specifying the extent
+ * of the blurring. If no parameter is used, the blur is equivalent to
+ * Guassian blur of radius 1. Larger values increase the blur.
+ * <br><br>
+ *
+ * ERODE
+ * Reduces the light areas. No parameter is used.
+ * <br><br>
+ *
+ * DILATE
+ * Increases the light areas. No parameter is used.
+ *
+ * @method filter
+ * @param {String} filterType
+ * @param {Number} filterParam an optional parameter unique
+ * to each filter, see above
+ *
+ *
+ * @example
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * filter(THRESHOLD);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * filter(GRAY);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * filter(OPAQUE);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * filter(INVERT);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * filter(POSTERIZE,3);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * filter(DILATE);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * filter(BLUR,3);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/bricks.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * filter(ERODE);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.filter = function(operation, value) {
+ Filters.apply(this.canvas, Filters[operation.toLowerCase()], value);
+};
+
+/**
+ * Returns an array of [R,G,B,A] values for any pixel or grabs a section of
+ * an image. If no parameters are specified, the entire image is returned.
+ * Use the x and y parameters to get the value of one pixel. Get a section of
+ * the display window by specifying additional w and h parameters. When
+ * getting an image, the x and y parameters define the coordinates for the
+ * upper-left corner of the image, regardless of the current imageMode().
+ * <br><br>
+ * If the pixel requested is outside of the image window, [0,0,0,255] is
+ * returned. To get the numbers scaled according to the current color ranges
+ * and taking into account colorMode, use getColor instead of get.
+ * <br><br>
+ * Getting the color of a single pixel with get(x, y) is easy, but not as fast
+ * as grabbing the data directly from pixels[]. The equivalent statement to
+ * get(x, y) using pixels[] with pixel density d is
+ * <code>[pixels[(y*width*d+x)*d],
+ * pixels[(y*width*d+x)*d+1],
+ * pixels[(y*width*d+x)*d+2],
+ * pixels[(y*width*d+x)*d+3]]</code>.
+ * <br><br>
+ * See the reference for pixels[] for more information.
+ *
+ * @method get
+ * @param {Number} [x] x-coordinate of the pixel
+ * @param {Number} [y] y-coordinate of the pixel
+ * @param {Number} [w] width
+ * @param {Number} [h] height
+ * @return {Array|p5.Image} values of pixel at x,y in array format
+ * [R, G, B, A] or p5.Image
+ * @example
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/rockies.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * var c = get();
+ * image(c, width/2, 0);
+ * }
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/rockies.jpg");
+ * }
+ * function setup() {
+ * image(img, 0, 0);
+ * var c = get(50, 90);
+ * fill(c);
+ * noStroke();
+ * rect(25, 25, 50, 50);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.get = function(x, y, w, h){
+ return this._renderer.get(x, y, w, h);
+};
+
+/**
+ * Loads the pixel data for the display window into the pixels[] array. This
+ * function must always be called before reading from or writing to pixels[].
+ *
+ * @method loadPixels
+ * @example
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * image(img, 0, 0);
+ * var d = pixelDensity();
+ * var halfImage = 4 * (img.width * d) *
+ (img.height/2 * d);
+ * loadPixels();
+ * for (var i = 0; i < halfImage; i++) {
+ * pixels[i+halfImage] = pixels[i];
+ * }
+ * updatePixels();
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.loadPixels = function() {
+ this._renderer.loadPixels();
+};
+
+/**
+ * <p>Changes the color of any pixel, or writes an image directly to the
+ * display window.</p>
+ * <p>The x and y parameters specify the pixel to change and the c parameter
+ * specifies the color value. This can be a p5.Color object, or [R, G, B, A]
+ * pixel array. It can also be a single grayscale value.
+ * When setting an image, the x and y parameters define the coordinates for
+ * the upper-left corner of the image, regardless of the current imageMode().
+ * </p>
+ * <p>
+ * After using set(), you must call updatePixels() for your changes to
+ * appear. This should be called once all pixels have been set.
+ * </p>
+ * <p>Setting the color of a single pixel with set(x, y) is easy, but not as
+ * fast as putting the data directly into pixels[]. Setting the pixels[]
+ * values directly may be complicated when working with a retina display,
+ * but will perform better when lots of pixels need to be set directly on
+ * every loop.</p>
+ * <p>See the reference for pixels[] for more information.</p>
+ *
+ * @method set
+ * @param {Number} x x-coordinate of the pixel
+ * @param {Number} y y-coordinate of the pixel
+ * @param {Number|Array|Object} c insert a grayscale value | a pixel array |
+ * a p5.Color object | a p5.Image to copy
+ * @example
+ * <div>
+ * <code>
+ * var black = color(0);
+ * set(30, 20, black);
+ * set(85, 20, black);
+ * set(85, 75, black);
+ * set(30, 75, black);
+ * updatePixels();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * for (var i = 30; i < width-15; i++) {
+ * for (var j = 20; j < height-25; j++) {
+ * var c = color(204-j, 153-i, 0);
+ * set(i, j, c);
+ * }
+ * }
+ * updatePixels();
+ * </code>
+ * </div>
+ *
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * set(0, 0, img);
+ * updatePixels();
+ * line(0, 0, width, height);
+ * line(0, height, width, 0);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.set = function (x, y, imgOrCol) {
+ this._renderer.set(x, y, imgOrCol);
+};
+/**
+ * Updates the display window with the data in the pixels[] array.
+ * Use in conjunction with loadPixels(). If you're only reading pixels from
+ * the array, there's no need to call updatePixels() — updating is only
+ * necessary to apply changes. updatePixels() should be called anytime the
+ * pixels array is manipulated or set() is called.
+ *
+ * @method updatePixels
+ * @param {Number} [x] x-coordinate of the upper-left corner of region
+ * to update
+ * @param {Number} [y] y-coordinate of the upper-left corner of region
+ * to update
+ * @param {Number} [w] width of region to update
+ * @param {Number} [w] height of region to update
+ * @example
+ * <div>
+ * <code>
+ * var img;
+ * function preload() {
+ * img = loadImage("assets/rockies.jpg");
+ * }
+ *
+ * function setup() {
+ * image(img, 0, 0);
+ * var halfImage = 4 * (img.width * pixelDensity()) *
+ * (img.height * pixelDensity()/2);
+ * loadPixels();
+ * for (var i = 0; i < halfImage; i++) {
+ * pixels[i+halfImage] = pixels[i];
+ * }
+ * updatePixels();
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.updatePixels = function (x, y, w, h) {
+ // graceful fail - if loadPixels() or set() has not been called, pixel
+ // array will be empty, ignore call to updatePixels()
+ if (this.pixels.length === 0) {
+ return;
+ }
+ this._renderer.updatePixels(x, y, w, h);
+};
+
+module.exports = p5;
+
+},{"../color/p5.Color":42,"../core/core":48,"./filters":65}],70:[function(_dereq_,module,exports){
+/**
+ * @module IO
+ * @submodule Input
+ * @for p5
+ * @requires core
+ * @requires reqwest
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var reqwest = _dereq_('reqwest');
+var opentype = _dereq_('opentype.js');
+_dereq_('../core/error_helpers');
+
+/**
+ * Checks if we are in preload and returns the last arg which will be the
+ * _decrementPreload function if called from a loadX() function. Should
+ * only be used in loadX() functions.
+ * @private
+ */
+p5._getDecrementPreload = function () {
+ var decrementPreload = arguments[arguments.length - 1];
+
+ // when in preload decrementPreload will always be the last arg as it is set
+ // with args.push() before invocation in _wrapPreload
+ if ((window.preload || (this && this.preload)) &&
+ typeof decrementPreload === 'function') {
+ return decrementPreload;
+ } else {
+ return null;
+ }
+};
+
+/**
+ * Loads an opentype font file (.otf, .ttf) from a file or a URL,
+ * and returns a PFont Object. This method is asynchronous,
+ * meaning it may not finish before the next line in your sketch
+ * is executed.
+ * <br><br>
+ * The path to the font should be relative to the HTML file
+ * that links in your sketch. Loading an from a URL or other
+ * remote location may be blocked due to your browser's built-in
+ * security.
+ *
+ * @method loadFont
+ * @param {String} path name of the file or url to load
+ * @param {Function} [callback] function to be executed after
+ * loadFont()
+ * completes
+ * @return {Object} p5.Font object
+ * @example
+ *
+ * <p>Calling loadFont() inside preload() guarantees that the load
+ * operation will have completed before setup() and draw() are called.</p>
+ *
+ * <div><code>
+ * var myFont;
+ * function preload() {
+ * myFont = loadFont('assets/AvenirNextLTPro-Demi.otf');
+ * }
+ *
+ * function setup() {
+ * fill('#ED225D');
+ * textFont(myFont);
+ * textSize(36);
+ * text('p5*js', 10, 50);
+ * }
+ * </code></div>
+ *
+ * <p>Outside of preload(), you may supply a callback function to handle the
+ * object:</p>
+ *
+ * <div><code>
+ * function setup() {
+ * loadFont('assets/AvenirNextLTPro-Demi.otf', drawText);
+ * }
+ *
+ * function drawText(font) {
+ * fill('#ED225D');
+ * textFont(font, 36);
+ * text('p5*js', 10, 50);
+ * }
+ *
+ * </code></div>
+ *
+ * <p>You can also use the string name of the font to style other HTML
+ * elements.</p>
+ *
+ * <div><code>
+ * var myFont;
+ *
+ * function preload() {
+ * myFont = loadFont('assets/Avenir.otf');
+ * }
+ *
+ * function setup() {
+ * var myDiv = createDiv('hello there');
+ * myDiv.style('font-family', 'Avenir');
+ * }
+ * </code></div>
+ */
+p5.prototype.loadFont = function (path, onSuccess, onError) {
+
+ var p5Font = new p5.Font(this);
+ var decrementPreload = p5._getDecrementPreload.apply(this, arguments);
+
+ opentype.load(path, function (err, font) {
+
+ if (err) {
+
+ if ((typeof onError !== 'undefined') && (onError !== decrementPreload)) {
+ return onError(err);
+ }
+ throw err;
+ }
+
+ p5Font.font = font;
+
+ if (typeof onSuccess !== 'undefined') {
+ onSuccess(p5Font);
+ }
+
+ if (decrementPreload && (onSuccess !== decrementPreload)) {
+ decrementPreload();
+ }
+
+ // check that we have an acceptable font type
+ var validFontTypes = [ 'ttf', 'otf', 'woff', 'woff2' ],
+ fileNoPath = path.split('\\').pop().split('/').pop(),
+ lastDotIdx = fileNoPath.lastIndexOf('.'), fontFamily, newStyle,
+ fileExt = lastDotIdx < 1 ? null : fileNoPath.substr(lastDotIdx + 1);
+
+ // if so, add it to the DOM (name-only) for use with p5.dom
+ if (validFontTypes.indexOf(fileExt) > -1) {
+
+ fontFamily = fileNoPath.substr(0, lastDotIdx);
+ newStyle = document.createElement('style');
+ newStyle.appendChild(document.createTextNode('\n@font-face {' +
+ '\nfont-family: ' + fontFamily + ';\nsrc: url(' + path + ');\n}\n'));
+ document.head.appendChild(newStyle);
+ }
+
+ });
+
+ return p5Font;
+};
+
+//BufferedReader
+p5.prototype.createInput = function () {
+ // TODO
+ throw 'not yet implemented';
+};
+
+p5.prototype.createReader = function () {
+ // TODO
+ throw 'not yet implemented';
+};
+
+p5.prototype.loadBytes = function () {
+ // TODO
+ throw 'not yet implemented';
+};
+
+/**
+ * Loads a JSON file from a file or a URL, and returns an Object or Array.
+ * This method is asynchronous, meaning it may not finish before the next
+ * line in your sketch is executed.
+ *
+ * @method loadJSON
+ * @param {String} path name of the file or url to load
+ * @param {Function} [callback] function to be executed after
+ * loadJSON() completes, data is passed
+ * in as first argument
+ * @param {Function} [errorCallback] function to be executed if
+ * there is an error, response is passed
+ * in as first argument
+ * @param {String} [datatype] "json" or "jsonp"
+ * @return {Object|Array} JSON data
+ * @example
+ *
+ * <p>Calling loadJSON() inside preload() guarantees to complete the
+ * operation before setup() and draw() are called.</p>
+ *
+ * <div><code>
+ * var weather;
+ * function preload() {
+ * var url = 'http://api.openweathermap.org/data/2.5/weather?q=London,UK'+
+ * '&APPID=7bbbb47522848e8b9c26ba35c226c734';
+ * weather = loadJSON(url);
+ * }
+ *
+ * function setup() {
+ * noLoop();
+ * }
+ *
+ * function draw() {
+ * background(200);
+ * // get the humidity value out of the loaded JSON
+ * var humidity = weather.main.humidity;
+ * fill(0, humidity); // use the humidity value to set the alpha
+ * ellipse(width/2, height/2, 50, 50);
+ * }
+ * </code></div>
+ *
+ *
+ * <p>Outside of preload(), you may supply a callback function to handle the
+ * object:</p>
+ * <div><code>
+ * function setup() {
+ * noLoop();
+ * var url = 'http://api.openweathermap.org/data/2.5/weather?q=NewYork'+
+ * '&APPID=7bbbb47522848e8b9c26ba35c226c734';
+ * loadJSON(url, drawWeather);
+ * }
+ *
+ * function draw() {
+ * background(200);
+ * }
+ *
+ * function drawWeather(weather) {
+ * // get the humidity value out of the loaded JSON
+ * var humidity = weather.main.humidity;
+ * fill(0, humidity); // use the humidity value to set the alpha
+ * ellipse(width/2, height/2, 50, 50);
+ * }
+ * </code></div>
+ *
+ */
+p5.prototype.loadJSON = function () {
+ var path = arguments[0];
+ var callback = arguments[1];
+ var errorCallback;
+ var decrementPreload = p5._getDecrementPreload.apply(this, arguments);
+
+ var ret = []; // array needed for preload
+ // assume jsonp for URLs
+ var t = 'json'; //= path.indexOf('http') === -1 ? 'json' : 'jsonp';
+
+ // check for explicit data type argument
+ for (var i = 2; i < arguments.length; i++) {
+ var arg = arguments[i];
+ if (typeof arg === 'string') {
+ if (arg === 'jsonp' || arg === 'json') {
+ t = arg;
+ }
+ } else if (typeof arg === 'function') {
+ errorCallback = arg;
+ }
+ }
+
+ reqwest({
+ url: path,
+ type: t,
+ crossOrigin: true,
+ error: function (resp) {
+ // pass to error callback if defined
+ if (errorCallback) {
+ errorCallback(resp);
+ } else { // otherwise log error msg
+ console.log(resp.statusText);
+ }
+ },
+ success: function (resp) {
+ for (var k in resp) {
+ ret[k] = resp[k];
+ }
+ if (typeof callback !== 'undefined') {
+ callback(resp);
+ }
+ if (decrementPreload && (callback !== decrementPreload)) {
+ decrementPreload();
+ }
+ }
+ });
+
+ return ret;
+};
+
+/**
+ * Reads the contents of a file and creates a String array of its individual
+ * lines. If the name of the file is used as the parameter, as in the above
+ * example, the file must be located in the sketch directory/folder.
+ * <br><br>
+ * Alternatively, the file maybe be loaded from anywhere on the local
+ * computer using an absolute path (something that starts with / on Unix and
+ * Linux, or a drive letter on Windows), or the filename parameter can be a
+ * URL for a file found on a network.
+ * <br><br>
+ * This method is asynchronous, meaning it may not finish before the next
+ * line in your sketch is executed.
+ *
+ * @method loadStrings
+ * @param {String} filename name of the file or url to load
+ * @param {Function} [callback] function to be executed after loadStrings()
+ * completes, Array is passed in as first
+ * argument
+ * @param {Function} [errorCallback] function to be executed if
+ * there is an error, response is passed
+ * in as first argument
+ * @return {Array} Array of Strings
+ * @example
+ *
+ * <p>Calling loadStrings() inside preload() guarantees to complete the
+ * operation before setup() and draw() are called.</p>
+ *
+ * <div><code>
+ * var result;
+ * function preload() {
+ * result = loadStrings('assets/test.txt');
+ * }
+
+ * function setup() {
+ * background(200);
+ * var ind = floor(random(result.length));
+ * text(result[ind], 10, 10, 80, 80);
+ * }
+ * </code></div>
+ *
+ * <p>Outside of preload(), you may supply a callback function to handle the
+ * object:</p>
+ *
+ * <div><code>
+ * function setup() {
+ * loadStrings('assets/test.txt', pickString);
+ * }
+ *
+ * function pickString(result) {
+ * background(200);
+ * var ind = floor(random(result.length));
+ * text(result[ind], 10, 10, 80, 80);
+ * }
+ * </code></div>
+ */
+p5.prototype.loadStrings = function (path, callback, errorCallback) {
+ var ret = [];
+ var req = new XMLHttpRequest();
+ var decrementPreload = p5._getDecrementPreload.apply(this, arguments);
+
+ req.addEventListener('error', function (resp) {
+ if (errorCallback) {
+ errorCallback(resp);
+ } else {
+ console.log(resp.responseText);
+ }
+ });
+
+ req.open('GET', path, true);
+ req.onreadystatechange = function () {
+ if (req.readyState === 4) {
+ if (req.status === 200) {
+ var arr = req.responseText.match(/[^\r\n]+/g);
+ for (var k in arr) {
+ ret[k] = arr[k];
+ }
+ if (typeof callback !== 'undefined') {
+ callback(ret);
+ }
+ if (decrementPreload && (callback !== decrementPreload)) {
+ decrementPreload();
+ }
+ } else {
+ if (errorCallback) {
+ errorCallback(req);
+ } else {
+ console.log(req.statusText);
+ }
+ //p5._friendlyFileLoadError(3, path);
+ }
+ }
+ };
+ req.send(null);
+ return ret;
+};
+
+/**
+ * <p>Reads the contents of a file or URL and creates a p5.Table object with
+ * its values. If a file is specified, it must be located in the sketch's
+ * "data" folder. The filename parameter can also be a URL to a file found
+ * online. By default, the file is assumed to be comma-separated (in CSV
+ * format). Table only looks for a header row if the 'header' option is
+ * included.</p>
+ *
+ * <p>Possible options include:
+ * <ul>
+ * <li>csv - parse the table as comma-separated values</li>
+ * <li>tsv - parse the table as tab-separated values</li>
+ * <li>header - this table has a header (title) row</li>
+ * </ul>
+ * </p>
+ *
+ * <p>When passing in multiple options, pass them in as separate parameters,
+ * seperated by commas. For example:
+ * <br><br>
+ * <code>
+ * loadTable("my_csv_file.csv", "csv", "header")
+ * </code>
+ * </p>
+ *
+ * <p> All files loaded and saved use UTF-8 encoding.</p>
+ *
+ * <p>This method is asynchronous, meaning it may not finish before the next
+ * line in your sketch is executed. Calling loadTable() inside preload()
+ * guarantees to complete the operation before setup() and draw() are called.
+ * <p>Outside of preload(), you may supply a callback function to handle the
+ * object:</p>
+ * </p>
+ *
+ * @method loadTable
+ * @param {String} filename name of the file or URL to load
+ * @param {String|Strings} [options] "header" "csv" "tsv"
+ * @param {Function} [callback] function to be executed after
+ * loadTable() completes, Table object is
+ * passed in as first argument
+ * @return {Object} Table object containing data
+ *
+ * @example
+ * <div class="norender">
+ * <code>
+ * // Given the following CSV file called "mammals.csv"
+ * // located in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * //the file can be remote
+ * //table = loadTable("http://p5js.org/reference/assets/mammals.csv",
+ * // "csv", "header");
+ * }
+ *
+ * function setup() {
+ * //count the columns
+ * print(table.getRowCount() + " total rows in table");
+ * print(table.getColumnCount() + " total columns in table");
+ *
+ * print(table.getColumn("name"));
+ * //["Goat", "Leopard", "Zebra"]
+ *
+ * //cycle through the table
+ * for (var r = 0; r < table.getRowCount(); r++)
+ * for (var c = 0; c < table.getColumnCount(); c++) {
+ * print(table.getString(r, c));
+ * }
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.loadTable = function (path) {
+ var callback = null;
+ var options = [];
+ var header = false;
+ var sep = ',';
+ var separatorSet = false;
+ var decrementPreload = p5._getDecrementPreload.apply(this, arguments);
+
+ for (var i = 1; i < arguments.length; i++) {
+ if ((typeof (arguments[i]) === 'function') &&
+ (arguments[i] !== decrementPreload)) {
+ callback = arguments[i];
+ } else if (typeof (arguments[i]) === 'string') {
+ options.push(arguments[i]);
+ if (arguments[i] === 'header') {
+ header = true;
+ }
+ if (arguments[i] === 'csv') {
+ if (separatorSet) {
+ throw new Error('Cannot set multiple separator types.');
+ } else {
+ sep = ',';
+ separatorSet = true;
+ }
+ } else if (arguments[i] === 'tsv') {
+ if (separatorSet) {
+ throw new Error('Cannot set multiple separator types.');
+ } else {
+ sep = '\t';
+ separatorSet = true;
+ }
+ }
+ }
+ }
+
+ var t = new p5.Table();
+ reqwest({
+ url: path,
+ crossOrigin: true,
+ type: 'csv'
+ })
+ .then(function (resp) {
+ resp = resp.responseText;
+
+ var state = {};
+
+ // define constants
+ var PRE_TOKEN = 0,
+ MID_TOKEN = 1,
+ POST_TOKEN = 2,
+ POST_RECORD = 4;
+
+ var QUOTE = '\"',
+ CR = '\r',
+ LF = '\n';
+
+ var records = [];
+ var offset = 0;
+ var currentRecord = null;
+ var currentChar;
+
+ var recordBegin = function () {
+ state.escaped = false;
+ currentRecord = [];
+ tokenBegin();
+ };
+
+ var recordEnd = function () {
+ state.currentState = POST_RECORD;
+ records.push(currentRecord);
+ currentRecord = null;
+ };
+
+ var tokenBegin = function () {
+ state.currentState = PRE_TOKEN;
+ state.token = '';
+ };
+
+ var tokenEnd = function () {
+ currentRecord.push(state.token);
+ tokenBegin();
+ };
+
+ while (true) {
+ currentChar = resp[offset++];
+
+ // EOF
+ if (currentChar == null) {
+ if (state.escaped) {
+ throw new Error('Unclosed quote in file.');
+ }
+ if (currentRecord) {
+ tokenEnd();
+ recordEnd();
+ break;
+ }
+ }
+ if (currentRecord === null) {
+ recordBegin();
+ }
+
+ // Handle opening quote
+ if (state.currentState === PRE_TOKEN) {
+ if (currentChar === QUOTE) {
+ state.escaped = true;
+ state.currentState = MID_TOKEN;
+ continue;
+ }
+ state.currentState = MID_TOKEN;
+ }
+
+ // mid-token and escaped, look for sequences and end quote
+ if (state.currentState === MID_TOKEN && state.escaped) {
+ if (currentChar === QUOTE) {
+ if (resp[offset] === QUOTE) {
+ state.token += QUOTE;
+ offset++;
+ } else {
+ state.escaped = false;
+ state.currentState = POST_TOKEN;
+ }
+ } else {
+ state.token += currentChar;
+ }
+ continue;
+ }
+
+ // fall-through: mid-token or post-token, not escaped
+ if (currentChar === CR) {
+ if (resp[offset] === LF) {
+ offset++;
+ }
+ tokenEnd();
+ recordEnd();
+ } else if (currentChar === LF) {
+ tokenEnd();
+ recordEnd();
+ } else if (currentChar === sep) {
+ tokenEnd();
+ } else if (state.currentState === MID_TOKEN) {
+ state.token += currentChar;
+ }
+ }
+
+ // set up column names
+ if (header) {
+ t.columns = records.shift();
+ } else {
+ for (i = 0; i < records[0].length; i++) {
+ t.columns[i] = 'null';
+ }
+ }
+ var row;
+ for (i = 0; i < records.length; i++) {
+ //Handles row of 'undefined' at end of some CSVs
+ if (i === records.length - 1 && records[i].length === 1) {
+ if (records[i][0] === 'undefined') {
+ break;
+ }
+ }
+ row = new p5.TableRow();
+ row.arr = records[i];
+ row.obj = makeObject(records[i], t.columns);
+ t.addRow(row);
+ }
+ if (callback !== null) {
+ callback(t);
+ }
+ if (decrementPreload && (callback !== decrementPreload)) {
+ decrementPreload();
+ }
+ })
+ .fail(function (err, msg) {
+ p5._friendlyFileLoadError(2, path);
+ // don't get error callback mixed up with decrementPreload
+ if ((typeof callback !== 'undefined') &&
+ (callback !== decrementPreload)) {
+ callback(false);
+ }
+ });
+
+ return t;
+};
+
+// helper function to turn a row into a JSON object
+function makeObject(row, headers) {
+ var ret = {};
+ headers = headers || [];
+ if (typeof (headers) === 'undefined') {
+ for (var j = 0; j < row.length; j++) {
+ headers[j.toString()] = j;
+ }
+ }
+ for (var i = 0; i < headers.length; i++) {
+ var key = headers[i];
+ var val = row[i];
+ ret[key] = val;
+ }
+ return ret;
+}
+
+/**
+ * Reads the contents of a file and creates an XML object with its values.
+ * If the name of the file is used as the parameter, as in the above example,
+ * the file must be located in the sketch directory/folder.
+ * <br><br>
+ * Alternatively, the file maybe be loaded from anywhere on the local
+ * computer using an absolute path (something that starts with / on Unix and
+ * Linux, or a drive letter on Windows), or the filename parameter can be a
+ * URL for a file found on a network.
+ * <br><br>
+ * This method is asynchronous, meaning it may not finish before the next
+ * line in your sketch is executed. Calling loadXML() inside preload()
+ * guarantees to complete the operation before setup() and draw() are called.
+ * <br><br>
+ * Outside of preload(), you may supply a callback function to handle the
+ * object:
+ *
+ * @method loadXML
+ * @param {String} filename name of the file or URL to load
+ * @param {Function} [callback] function to be executed after loadXML()
+ * completes, XML object is passed in as
+ * first argument
+ * @param {Function} [errorCallback] function to be executed if
+ * there is an error, response is passed
+ * in as first argument
+ * @return {Object} XML object containing data
+ */
+p5.prototype.loadXML = function (path, callback, errorCallback) {
+ var ret = document.implementation.createDocument(null, null);
+ var decrementPreload = p5._getDecrementPreload.apply(this, arguments);
+
+ reqwest({
+ url: path,
+ type: 'xml',
+ crossOrigin: true,
+ error: function (resp) {
+ // pass to error callback if defined
+ if (errorCallback) {
+ errorCallback(resp);
+ } else { // otherwise log error msg
+ console.log(resp.statusText);
+ }
+ //p5._friendlyFileLoadError(1,path);
+ }
+ })
+ .then(function (resp) {
+ var x = resp.documentElement;
+ ret.appendChild(x);
+ if (typeof callback !== 'undefined') {
+ callback(ret);
+ }
+ if (decrementPreload && (callback !== decrementPreload)) {
+ decrementPreload();
+ }
+ });
+ return ret;
+};
+
+// name clash with window.open
+// p5.prototype.open = function() {
+// // TODO
+
+// };
+
+p5.prototype.parseXML = function () {
+ // TODO
+ throw 'not yet implemented';
+
+};
+
+p5.prototype.selectFolder = function () {
+ // TODO
+ throw 'not yet implemented';
+
+};
+
+p5.prototype.selectInput = function () {
+ // TODO
+ throw 'not yet implemented';
+
+};
+
+/**
+ * Method for executing an HTTP GET request. If data type is not specified,
+ * p5 will try to guess based on the URL, defaulting to text.
+ *
+ * @method httpGet
+ * @param {String} path name of the file or url to load
+ * @param {Object} [data] param data passed sent with request
+ * @param {String} [datatype] "json", "jsonp", "xml", or "text"
+ * @param {Function} [callback] function to be executed after
+ * httpGet() completes, data is passed in
+ * as first argument
+ * @param {Function} [errorCallback] function to be executed if
+ * there is an error, response is passed
+ * in as first argument
+ */
+p5.prototype.httpGet = function () {
+ var args = Array.prototype.slice.call(arguments);
+ args.push('GET');
+ p5.prototype.httpDo.apply(this, args);
+};
+
+/**
+ * Method for executing an HTTP POST request. If data type is not specified,
+ * p5 will try to guess based on the URL, defaulting to text.
+ *
+ * @method httpPost
+ * @param {String} path name of the file or url to load
+ * @param {Object} [data] param data passed sent with request
+ * @param {String} [datatype] "json", "jsonp", "xml", or "text"
+ * @param {Function} [callback] function to be executed after
+ * httpGet() completes, data is passed in
+ * as first argument
+ * @param {Function} [errorCallback] function to be executed if
+ * there is an error, response is passed
+ * in as first argument
+ */
+p5.prototype.httpPost = function () {
+ var args = Array.prototype.slice.call(arguments);
+ args.push('POST');
+ p5.prototype.httpDo.apply(this, args);
+};
+
+/**
+ * Method for executing an HTTP request. If data type is not specified,
+ * p5 will try to guess based on the URL, defaulting to text.<br><br>
+ * You may also pass a single object specifying all parameters for the
+ * request following the examples inside the reqwest() calls here:
+ * <a href='https://github.com/ded/reqwest#api'
+ * >https://github.com/ded/reqwest#api</a>
+ *
+ * @method httpDo
+ * @param {String} path name of the file or url to load
+ * @param {String} [method] either "GET", "POST", or "PUT",
+ * defaults to "GET"
+ * @param {Object} [data] param data passed sent with request
+ * @param {String} [datatype] "json", "jsonp", "xml", or "text"
+ * @param {Function} [callback] function to be executed after
+ * httpGet() completes, data is passed in
+ * as first argument
+ * @param {Function} [errorCallback] function to be executed if
+ * there is an error, response is passed
+ * in as first argument
+ */
+p5.prototype.httpDo = function () {
+ if (typeof arguments[0] === 'object') {
+ reqwest(arguments[0]);
+ } else {
+ var method = 'GET';
+ var path = arguments[0];
+ var data = {};
+ var type = '';
+ var callback;
+ var errorCallback;
+
+ for (var i = 1; i < arguments.length; i++) {
+ var a = arguments[i];
+ if (typeof a === 'string') {
+ if (a === 'GET' || a === 'POST' || a === 'PUT') {
+ method = a;
+ } else {
+ type = a;
+ }
+ } else if (typeof a === 'object') {
+ data = a;
+ } else if (typeof a === 'function') {
+ if (!callback) {
+ callback = a;
+ } else {
+ errorCallback = a;
+ }
+ }
+ }
+
+ // do some sort of smart type checking
+ if (type === '') {
+ if (path.indexOf('json') !== -1) {
+ type = 'json';
+ } else if (path.indexOf('xml') !== -1) {
+ type = 'xml';
+ } else {
+ type = 'text';
+ }
+ }
+
+ reqwest({
+ url: path,
+ method: method,
+ data: data,
+ type: type,
+ crossOrigin: true,
+ success: function (resp) {
+ if (typeof callback !== 'undefined') {
+ if (type === 'text') {
+ callback(resp.response);
+ } else {
+ callback(resp);
+ }
+ }
+ },
+ error: function (resp) {
+ if (errorCallback) {
+ errorCallback(resp);
+ } else {
+ console.log(resp.statusText);
+ }
+ }
+ });
+ }
+};
+
+/**
+ * @module IO
+ * @submodule Output
+ * @for p5
+ */
+
+window.URL = window.URL || window.webkitURL;
+
+// private array of p5.PrintWriter objects
+p5.prototype._pWriters = [];
+
+p5.prototype.beginRaw = function () {
+ // TODO
+ throw 'not yet implemented';
+
+};
+
+p5.prototype.beginRecord = function () {
+ // TODO
+ throw 'not yet implemented';
+
+};
+
+p5.prototype.createOutput = function () {
+ // TODO
+
+ throw 'not yet implemented';
+};
+
+p5.prototype.createWriter = function (name, extension) {
+ var newPW;
+ // check that it doesn't already exist
+ for (var i in p5.prototype._pWriters) {
+ if (p5.prototype._pWriters[i].name === name) {
+ // if a p5.PrintWriter w/ this name already exists...
+ // return p5.prototype._pWriters[i]; // return it w/ contents intact.
+ // or, could return a new, empty one with a unique name:
+ newPW = new p5.PrintWriter(name + window.millis(), extension);
+ p5.prototype._pWriters.push(newPW);
+ return newPW;
+ }
+ }
+ newPW = new p5.PrintWriter(name, extension);
+ p5.prototype._pWriters.push(newPW);
+ return newPW;
+};
+
+p5.prototype.endRaw = function () {
+ // TODO
+
+ throw 'not yet implemented';
+};
+
+p5.prototype.endRecord = function () {
+ // TODO
+ throw 'not yet implemented';
+
+};
+
+p5.PrintWriter = function (filename, extension) {
+ var self = this;
+ this.name = filename;
+ this.content = '';
+ this.print = function (data) {
+ this.content += data;
+ };
+ this.println = function (data) {
+ this.content += data + '\n';
+ };
+ this.flush = function () {
+ this.content = '';
+ };
+ this.close = function () {
+ // convert String to Array for the writeFile Blob
+ var arr = [];
+ arr.push(this.content);
+ p5.prototype.writeFile(arr, filename, extension);
+ // remove from _pWriters array and delete self
+ for (var i in p5.prototype._pWriters) {
+ if (p5.prototype._pWriters[i].name === this.name) {
+ // remove from _pWriters array
+ p5.prototype._pWriters.splice(i, 1);
+ }
+ }
+ self.flush();
+ self = {};
+ };
+};
+
+p5.prototype.saveBytes = function () {
+ // TODO
+ throw 'not yet implemented';
+
+};
+
+// object, filename, options --> saveJSON, saveStrings, saveTable
+// filename, [extension] [canvas] --> saveImage
+
+/**
+ * <p>Save an image, text, json, csv, wav, or html. Prompts download to
+ * the client's computer. <b>Note that it is not recommended to call save()
+ * within draw if it's looping, as the save() function will open a new save
+ * dialog every frame.</b></p>
+ * <p>The default behavior is to save the canvas as an image. You can
+ * optionally specify a filename.
+ * For example:</p>
+ * <pre class='language-javascript'><code>
+ * save();
+ * save('myCanvas.jpg'); // save a specific canvas with a filename
+ * </code></pre>
+ *
+ * <p>Alternately, the first parameter can be a pointer to a canvas
+ * p5.Element, an Array of Strings,
+ * an Array of JSON, a JSON object, a p5.Table, a p5.Image, or a
+ * p5.SoundFile (requires p5.sound). The second parameter is a filename
+ * (including extension). The third parameter is for options specific
+ * to this type of object. This method will save a file that fits the
+ * given paramaters. For example:</p>
+ *
+ * <pre class='language-javascript'><code>
+ *
+ * save('myCanvas.jpg'); // Saves canvas as an image
+ *
+ * var cnv = createCanvas(100, 100);
+ * save(cnv, 'myCanvas.jpg'); // Saves canvas as an image
+ *
+ * var gb = createGraphics(100, 100);
+ * save(gb, 'myGraphics.jpg'); // Saves p5.Renderer object as an image
+ *
+ * save(myTable, 'myTable.html'); // Saves table as html file
+ * save(myTable, 'myTable.csv',); // Comma Separated Values
+ * save(myTable, 'myTable.tsv'); // Tab Separated Values
+ *
+ * save(myJSON, 'my.json'); // Saves pretty JSON
+ * save(myJSON, 'my.json', true); // Optimizes JSON filesize
+ *
+ * save(img, 'my.png'); // Saves pImage as a png image
+ *
+ * save(arrayOfStrings, 'my.txt'); // Saves strings to a text file with line
+ * // breaks after each item in the array
+ * </code></pre>
+ *
+ * @method save
+ * @param {[Object|String]} objectOrFilename If filename is provided, will
+ * save canvas as an image with
+ * either png or jpg extension
+ * depending on the filename.
+ * If object is provided, will
+ * save depending on the object
+ * and filename (see examples
+ * above).
+ * @param {[String]} filename If an object is provided as the first
+ * parameter, then the second parameter
+ * indicates the filename,
+ * and should include an appropriate
+ * file extension (see examples above).
+ * @param {[Boolean/String]} options Additional options depend on
+ * filetype. For example, when saving JSON,
+ * <code>true</code> indicates that the
+ * output will be optimized for filesize,
+ * rather than readability.
+ */
+p5.prototype.save = function (object, _filename, _options) {
+ // parse the arguments and figure out which things we are saving
+ var args = arguments;
+ // =================================================
+ // OPTION 1: saveCanvas...
+
+ // if no arguments are provided, save canvas
+ var cnv = this._curElement.elt;
+ if (args.length === 0) {
+ p5.prototype.saveCanvas(cnv);
+ return;
+ }
+ // otherwise, parse the arguments
+
+ // if first param is a p5Graphics, then saveCanvas
+ else if (args[0] instanceof p5.Renderer ||
+ args[0] instanceof p5.Graphics) {
+ p5.prototype.saveCanvas(args[0].elt, args[1], args[2]);
+ return;
+ }
+
+ // if 1st param is String and only one arg, assume it is canvas filename
+ else if (args.length === 1 && typeof (args[0]) === 'string') {
+ p5.prototype.saveCanvas(cnv, args[0]);
+ }
+
+ // =================================================
+ // OPTION 2: extension clarifies saveStrings vs. saveJSON
+ else {
+ var extension = _checkFileExtension(args[1], args[2])[1];
+ switch (extension) {
+ case 'json':
+ p5.prototype.saveJSON(args[0], args[1], args[2]);
+ return;
+ case 'txt':
+ p5.prototype.saveStrings(args[0], args[1], args[2]);
+ return;
+ // =================================================
+ // OPTION 3: decide based on object...
+ default:
+ if (args[0] instanceof Array) {
+ p5.prototype.saveStrings(args[0], args[1], args[2]);
+ } else if (args[0] instanceof p5.Table) {
+ p5.prototype.saveTable(args[0], args[1], args[2], args[3]);
+ } else if (args[0] instanceof p5.Image) {
+ p5.prototype.saveCanvas(args[0].canvas, args[1]);
+ } else if (args[0] instanceof p5.SoundFile) {
+ p5.prototype.saveSound(args[0], args[1], args[2], args[3]);
+ }
+ }
+ }
+};
+
+/**
+ * Writes the contents of an Array or a JSON object to a .json file.
+ * The file saving process and location of the saved file will
+ * vary between web browsers.
+ *
+ * @method saveJSON
+ * @param {Array|Object} json
+ * @param {String} filename
+ * @param {Boolean} [optimize] If true, removes line breaks
+ * and spaces from the output
+ * file to optimize filesize
+ * (but not readability).
+ * @example
+ * <div><code>
+ * var json;
+ *
+ * function setup() {
+ *
+ * json = {}; // new JSON Object
+ *
+ * json.id = 0;
+ * json.species = 'Panthera leo';
+ * json.name = 'Lion';
+ *
+ * // To save, un-comment the line below, then click 'run'
+ * // saveJSONObject(json, 'lion.json');
+ * }
+ *
+ * // Saves the following to a file called "lion.json":
+ * // {
+ * // "id": 0,
+ * // "species": "Panthera leo",
+ * // "name": "Lion"
+ * // }
+ * </div></code>
+ */
+p5.prototype.saveJSON = function (json, filename, opt) {
+ var stringify;
+ if (opt) {
+ stringify = JSON.stringify(json);
+ } else {
+ stringify = JSON.stringify(json, undefined, 2);
+ }
+ console.log(stringify);
+ this.saveStrings(stringify.split('\n'), filename, 'json');
+};
+
+p5.prototype.saveJSONObject = p5.prototype.saveJSON;
+p5.prototype.saveJSONArray = p5.prototype.saveJSON;
+
+p5.prototype.saveStream = function () {
+ // TODO
+ throw 'not yet implemented';
+
+};
+
+/**
+ * Writes an array of Strings to a text file, one line per String.
+ * The file saving process and location of the saved file will
+ * vary between web browsers.
+ *
+ * @method saveStrings
+ * @param {Array} list string array to be written
+ * @param {String} filename filename for output
+ * @example
+ * <div><code>
+ * var words = 'apple bear cat dog';
+ *
+ * // .split() outputs an Array
+ * var list = split(words, ' ');
+ *
+ * // To save the file, un-comment next line and click 'run'
+ * // saveStrings(list, 'nouns.txt');
+ *
+ * // Saves the following to a file called 'nouns.txt':
+ * //
+ * // apple
+ * // bear
+ * // cat
+ * // dog
+ * </code></div>
+ */
+p5.prototype.saveStrings = function (list, filename, extension) {
+ var ext = extension || 'txt';
+ var pWriter = this.createWriter(filename, ext);
+ for (var i = 0; i < list.length; i++) {
+ if (i < list.length - 1) {
+ pWriter.println(list[i]);
+ } else {
+ pWriter.print(list[i]);
+ }
+ }
+ pWriter.close();
+ pWriter.flush();
+};
+
+p5.prototype.saveXML = function () {
+ // TODO
+ throw 'not yet implemented';
+
+};
+
+p5.prototype.selectOutput = function () {
+ // TODO
+ throw 'not yet implemented';
+
+};
+
+// =======
+// HELPERS
+// =======
+
+function escapeHelper(content) {
+ return content
+ .replace(/&/g, '&amp;')
+ .replace(/</g, '&lt;')
+ .replace(/>/g, '&gt;')
+ .replace(/"/g, '&quot;')
+ .replace(/'/g, '&#039;');
+}
+
+/**
+ * Writes the contents of a Table object to a file. Defaults to a
+ * text file with comma-separated-values ('csv') but can also
+ * use tab separation ('tsv'), or generate an HTML table ('html').
+ * The file saving process and location of the saved file will
+ * vary between web browsers.
+ *
+ * @method saveTable
+ * @param {p5.Table} Table the Table object to save to a file
+ * @param {String} filename the filename to which the Table should be saved
+ * @param {String} [options] can be one of "tsv", "csv", or "html"
+ * @example
+ * <div><code>
+ * var table;
+ *
+ * function setup() {
+ * table = new p5.Table();
+ *
+ * table.addColumn('id');
+ * table.addColumn('species');
+ * table.addColumn('name');
+ *
+ * var newRow = table.addRow();
+ * newRow.setNum('id', table.getRowCount() - 1);
+ * newRow.setString('species', 'Panthera leo');
+ * newRow.setString('name', 'Lion');
+ *
+ * // To save, un-comment next line then click 'run'
+ * // saveTable(table, 'new.csv');
+ * }
+ *
+ * // Saves the following to a file called 'new.csv':
+ * // id,species,name
+ * // 0,Panthera leo,Lion
+ * </code></div>
+ */
+p5.prototype.saveTable = function (table, filename, options) {
+ var pWriter = this.createWriter(filename, options);
+
+ var header = table.columns;
+
+ var sep = ','; // default to CSV
+ if (options === 'tsv') {
+ sep = '\t';
+ }
+ if (options !== 'html') {
+ // make header if it has values
+ if (header[0] !== '0') {
+ for (var h = 0; h < header.length; h++) {
+ if (h < header.length - 1) {
+ pWriter.print(header[h] + sep);
+ } else {
+ pWriter.println(header[h]);
+ }
+ }
+ }
+
+ // make rows
+ for (var i = 0; i < table.rows.length; i++) {
+ var j;
+ for (j = 0; j < table.rows[i].arr.length; j++) {
+ if (j < table.rows[i].arr.length - 1) {
+ pWriter.print(table.rows[i].arr[j] + sep);
+ } else if (i < table.rows.length - 1) {
+ pWriter.println(table.rows[i].arr[j]);
+ } else {
+ pWriter.print(table.rows[i].arr[j]); // no line break
+ }
+ }
+ }
+ }
+
+ // otherwise, make HTML
+ else {
+ pWriter.println('<html>');
+ pWriter.println('<head>');
+ var str = ' <meta http-equiv=\"content-type\" content';
+ str += '=\"text/html;charset=utf-8\" />';
+ pWriter.println(str);
+ pWriter.println('</head>');
+
+ pWriter.println('<body>');
+ pWriter.println(' <table>');
+
+ // make header if it has values
+ if (header[0] !== '0') {
+ pWriter.println(' <tr>');
+ for (var k = 0; k < header.length; k++) {
+ var e = escapeHelper(header[k]);
+ pWriter.println(' <td>' + e);
+ pWriter.println(' </td>');
+ }
+ pWriter.println(' </tr>');
+ }
+
+ // make rows
+ for (var row = 0; row < table.rows.length; row++) {
+ pWriter.println(' <tr>');
+ for (var col = 0; col < table.columns.length; col++) {
+ var entry = table.rows[row].getString(col);
+ var htmlEntry = escapeHelper(entry);
+ pWriter.println(' <td>' + htmlEntry);
+ pWriter.println(' </td>');
+ }
+ pWriter.println(' </tr>');
+ }
+ pWriter.println(' </table>');
+ pWriter.println('</body>');
+ pWriter.print('</html>');
+ }
+ // close and flush the pWriter
+ pWriter.close();
+ pWriter.flush();
+}; // end saveTable()
+
+/**
+ * Generate a blob of file data as a url to prepare for download.
+ * Accepts an array of data, a filename, and an extension (optional).
+ * This is a private function because it does not do any formatting,
+ * but it is used by saveStrings, saveJSON, saveTable etc.
+ *
+ * @param {Array} dataToDownload
+ * @param {String} filename
+ * @param {[String]} extension
+ * @private
+ */
+p5.prototype.writeFile = function (dataToDownload, filename, extension) {
+ var type = 'application\/octet-stream';
+ if (p5.prototype._isSafari()) {
+ type = 'text\/plain';
+ }
+ var blob = new Blob(dataToDownload, {
+ 'type': type
+ });
+ var href = window.URL.createObjectURL(blob);
+ p5.prototype.downloadFile(href, filename, extension);
+};
+
+/**
+ * Forces download. Accepts a url to filedata/blob, a filename,
+ * and an extension (optional).
+ * This is a private function because it does not do any formatting,
+ * but it is used by saveStrings, saveJSON, saveTable etc.
+ *
+ * @param {String} href i.e. an href generated by createObjectURL
+ * @param {[String]} filename
+ * @param {[String]} extension
+ */
+p5.prototype.downloadFile = function (href, fName, extension) {
+ var fx = _checkFileExtension(fName, extension);
+ var filename = fx[0];
+ var ext = fx[1];
+
+ var a = document.createElement('a');
+ a.href = href;
+ a.download = filename;
+
+ // Firefox requires the link to be added to the DOM before click()
+ a.onclick = destroyClickedElement;
+ a.style.display = 'none';
+ document.body.appendChild(a);
+
+ // Safari will open this file in the same page as a confusing Blob.
+ if (p5.prototype._isSafari()) {
+ var aText = 'Hello, Safari user! To download this file...\n';
+ aText += '1. Go to File --> Save As.\n';
+ aText += '2. Choose "Page Source" as the Format.\n';
+ aText += '3. Name it with this extension: .\"' + ext + '\"';
+ alert(aText);
+ }
+ a.click();
+ href = null;
+};
+
+/**
+ * Returns a file extension, or another string
+ * if the provided parameter has no extension.
+ *
+ * @param {String} filename
+ * @return {Array} [fileName, fileExtension]
+ *
+ * @private
+ */
+function _checkFileExtension(filename, extension) {
+ if (!extension || extension === true || extension === 'true') {
+ extension = '';
+ }
+ if (!filename) {
+ filename = 'untitled';
+ }
+ var ext = '';
+ // make sure the file will have a name, see if filename needs extension
+ if (filename && filename.indexOf('.') > -1) {
+ ext = filename.split('.').pop();
+ }
+ // append extension if it doesn't exist
+ if (extension) {
+ if (ext !== extension) {
+ ext = extension;
+ filename = filename + '.' + ext;
+ }
+ }
+ return [filename, ext];
+}
+p5.prototype._checkFileExtension = _checkFileExtension;
+
+/**
+ * Returns true if the browser is Safari, false if not.
+ * Safari makes trouble for downloading files.
+ *
+ * @return {Boolean} [description]
+ * @private
+ */
+p5.prototype._isSafari = function () {
+ var x = Object.prototype.toString.call(window.HTMLElement);
+ return x.indexOf('Constructor') > 0;
+};
+
+/**
+ * Helper function, a callback for download that deletes
+ * an invisible anchor element from the DOM once the file
+ * has been automatically downloaded.
+ *
+ * @private
+ */
+function destroyClickedElement(event) {
+ document.body.removeChild(event.target);
+}
+
+module.exports = p5;
+
+},{"../core/core":48,"../core/error_helpers":51,"opentype.js":8,"reqwest":27}],71:[function(_dereq_,module,exports){
+/**
+ * @module IO
+ * @submodule Table
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+
+/**
+ * Table Options
+ * <p>Generic class for handling tabular data, typically from a
+ * CSV, TSV, or other sort of spreadsheet file.</p>
+ * <p>CSV files are
+ * <a href="http://en.wikipedia.org/wiki/Comma-separated_values">
+ * comma separated values</a>, often with the data in quotes. TSV
+ * files use tabs as separators, and usually don't bother with the
+ * quotes.</p>
+ * <p>File names should end with .csv if they're comma separated.</p>
+ * <p>A rough "spec" for CSV can be found
+ * <a href="http://tools.ietf.org/html/rfc4180">here</a>.</p>
+ * <p>To load files, use the loadTable method.</p>
+ * <p>To save tables to your computer, use the save method
+ * or the saveTable method.</p>
+ *
+ * Possible options include:
+ * <ul>
+ * <li>csv - parse the table as comma-separated values
+ * <li>tsv - parse the table as tab-separated values
+ * <li>header - this table has a header (title) row
+ * </ul>
+ */
+
+/**
+ * Table objects store data with multiple rows and columns, much
+ * like in a traditional spreadsheet. Tables can be generated from
+ * scratch, dynamically, or using data from an existing file.
+ *
+ * @class p5.Table
+ * @constructor
+ * @param {Array} [rows] An array of p5.TableRow objects
+ * @return {p5.Table} p5.Table generated
+ */
+p5.Table = function (rows) {
+ /**
+ * @property columns
+ * @type {Array}
+ */
+ this.columns = [];
+
+ /**
+ * @property rows
+ * @type {Array}
+ */
+ this.rows = [];
+};
+
+/**
+ * Use addRow() to add a new row of data to a p5.Table object. By default,
+ * an empty row is created. Typically, you would store a reference to
+ * the new row in a TableRow object (see newRow in the example above),
+ * and then set individual values using set().
+ *
+ * If a p5.TableRow object is included as a parameter, then that row is
+ * duplicated and added to the table.
+ *
+ * @method addRow
+ * @param {p5.TableRow} [row] row to be added to the table
+ *
+ * @example
+ * <div class="norender">
+ * <code>
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * //add a row
+ * var newRow = table.addRow();
+ * newRow.setString("id", table.getRowCount() - 1);
+ * newRow.setString("species", "Canis Lupus");
+ * newRow.setString("name", "Wolf");
+ *
+ * //print the results
+ * for (var r = 0; r < table.getRowCount(); r++)
+ * for (var c = 0; c < table.getColumnCount(); c++)
+ * print(table.getString(r, c));
+ * }
+ * </code>
+ * </div>
+ */
+p5.Table.prototype.addRow = function(row) {
+ // make sure it is a valid TableRow
+ var r = row || new p5.TableRow();
+
+ if (typeof(r.arr) === 'undefined' || typeof(r.obj) === 'undefined') {
+ //r = new p5.prototype.TableRow(r);
+ throw 'invalid TableRow: ' + r;
+ }
+ r.table = this;
+ this.rows.push(r);
+ return r;
+};
+
+/**
+ * Removes a row from the table object.
+ *
+ * @method removeRow
+ * @param {Number} id ID number of the row to remove
+ *
+ * @example
+ * <div class="norender">
+ * <code>
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * //remove the first row
+ * var r = table.removeRow(0);
+ *
+ * //print the results
+ * for (var r = 0; r < table.getRowCount(); r++)
+ * for (var c = 0; c < table.getColumnCount(); c++)
+ * print(table.getString(r, c));
+ * }
+ * </code>
+ * </div>
+ */
+p5.Table.prototype.removeRow = function(id) {
+ this.rows[id].table = null; // remove reference to table
+ var chunk = this.rows.splice(id+1, this.rows.length);
+ this.rows.pop();
+ this.rows = this.rows.concat(chunk);
+};
+
+
+/**
+ * Returns a reference to the specified p5.TableRow. The reference
+ * can then be used to get and set values of the selected row.
+ *
+ * @method getRow
+ * @param {Number} rowID ID number of the row to get
+ * @return {TableRow} p5.TableRow object
+ *
+ * @example
+ * <div class="norender">
+ * <code>
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * var row = table.getRow(1);
+ * //print it column by column
+ * //note: a row is an object, not an array
+ * for (var c = 0; c < table.getColumnCount(); c++)
+ * print(row.getString(c));
+ * }
+ * </code>
+ * </div>
+ */
+p5.Table.prototype.getRow = function(r) {
+ return this.rows[r];
+};
+
+/**
+ * Gets all rows from the table. Returns an array of p5.TableRows.
+ *
+ * @method getRows
+ * @return {Array} Array of p5.TableRows
+ *
+ * @example
+ * <div class="norender">
+ * <code>
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * var rows = table.getRows();
+ *
+ * //warning: rows is an array of objects
+ * for (var r = 0; r < rows.length; r++)
+ * rows[r].set("name", "Unicorn");
+ *
+ * //print the results
+ * for (var r = 0; r < table.getRowCount(); r++)
+ * for (var c = 0; c < table.getColumnCount(); c++)
+ * print(table.getString(r, c));
+ * }
+ * </code>
+ * </div>
+ */
+p5.Table.prototype.getRows = function() {
+ return this.rows;
+};
+
+/**
+ * Finds the first row in the Table that contains the value
+ * provided, and returns a reference to that row. Even if
+ * multiple rows are possible matches, only the first matching
+ * row is returned. The column to search may be specified by
+ * either its ID or title.
+ *
+ * @method findRow
+ * @param {String} value The value to match
+ * @param {Number|String} column ID number or title of the
+ * column to search
+ * @return {TableRow}
+ *
+ * @example
+ * <div class="norender">
+ * <code>
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * //find the animal named zebra
+ * var row = table.findRow("Zebra", "name");
+ * //find the corresponding species
+ * print(row.getString("species"));
+ * }
+ * </code>
+ * </div>
+ */
+p5.Table.prototype.findRow = function(value, column) {
+ // try the Object
+ if (typeof(column) === 'string') {
+ for (var i = 0; i < this.rows.length; i++){
+ if (this.rows[i].obj[column] === value) {
+ return this.rows[i];
+ }
+ }
+ }
+ // try the Array
+ else {
+ for (var j = 0; j < this.rows.length; j++){
+ if (this.rows[j].arr[column] === value) {
+ return this.rows[j];
+ }
+ }
+ }
+ // otherwise...
+ return null;
+};
+
+/**
+ * Finds the rows in the Table that contain the value
+ * provided, and returns references to those rows. Returns an
+ * Array, so for must be used to iterate through all the rows,
+ * as shown in the example above. The column to search may be
+ * specified by either its ID or title.
+ *
+ * @method findRows
+ * @param {String} value The value to match
+ * @param {Number|String} column ID number or title of the
+ * column to search
+ * @return {Array} An Array of TableRow objects
+ *
+ * @example
+ * <div class="norender">
+ * <code>
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * //add another goat
+ * var newRow = table.addRow();
+ * newRow.setString("id", table.getRowCount() - 1);
+ * newRow.setString("species", "Scape Goat");
+ * newRow.setString("name", "Goat");
+ *
+ * //find the rows containing animals named Goat
+ * var rows = table.findRows("Goat", "name");
+ * print(rows.length + " Goats found");
+ * }
+ * </code>
+ * </div>
+ */
+p5.Table.prototype.findRows = function(value, column) {
+ var ret = [];
+ if (typeof(column) === 'string') {
+ for (var i = 0; i < this.rows.length; i++){
+ if (this.rows[i].obj[column] === value) {
+ ret.push( this.rows[i] );
+ }
+ }
+ }
+ // try the Array
+ else {
+ for (var j = 0; j < this.rows.length; j++){
+ if (this.rows[j].arr[column] === value) {
+ ret.push( this.rows[j] );
+ }
+ }
+ }
+ return ret;
+};
+
+/**
+ * Finds the first row in the Table that matches the regular
+ * expression provided, and returns a reference to that row.
+ * Even if multiple rows are possible matches, only the first
+ * matching row is returned. The column to search may be
+ * specified by either its ID or title.
+ *
+ * @method matchRow
+ * @param {String} regexp The regular expression to match
+ * @param {String|Number} column The column ID (number) or
+ * title (string)
+ * @return {TableRow} TableRow object
+ */
+p5.Table.prototype.matchRow = function(regexp, column) {
+ if (typeof(column) === 'number') {
+ for (var j = 0; j < this.rows.length; j++) {
+ if ( this.rows[j].arr[column].match(regexp) ) {
+ return this.rows[j];
+ }
+ }
+ }
+
+ else {
+ for (var i = 0; i < this.rows.length; i++) {
+ if ( this.rows[i].obj[column].match(regexp) ) {
+ return this.rows[i];
+ }
+ }
+ }
+ return null;
+};
+
+/**
+ * Finds the first row in the Table that matches the regular
+ * expression provided, and returns a reference to that row.
+ * Even if multiple rows are possible matches, only the first
+ * matching row is returned. The column to search may be specified
+ * by either its ID or title.
+ *
+ * @method matchRows
+ * @param {String} regexp The regular expression to match
+ * @param {String|Number} [column] The column ID (number) or
+ * title (string)
+ * @return {Array} An Array of TableRow objects
+ */
+p5.Table.prototype.matchRows = function(regexp, column) {
+ var ret = [];
+ if (typeof(column) === 'number') {
+ for (var j = 0; j < this.rows.length; j++) {
+ if ( this.rows[j].arr[column].match(regexp) ) {
+ ret.push( this.rows[j] );
+ }
+ }
+ }
+
+ else {
+ for (var i = 0; i < this.rows.length; i++) {
+ if ( this.rows[i].obj[column].match(regexp) ) {
+ ret.push( this.rows[i] );
+ }
+ }
+ }
+ return ret;
+};
+
+
+/**
+ * Retrieves all values in the specified column, and returns them
+ * as an array. The column may be specified by either its ID or title.
+ *
+ * @method getColumn
+ * @param {String|Number} column String or Number of the column to return
+ * @return {Array} Array of column values
+ *
+ * @example
+ * <div class="norender">
+ * <code>
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * //getColumn returns an array that can be printed directly
+ * print(table.getColumn("species"));
+ * //outputs ["Capra hircus", "Panthera pardus", "Equus zebra"]
+ * }
+ * </code>
+ * </div>
+ */
+p5.Table.prototype.getColumn = function(value) {
+ var ret = [];
+ if (typeof(value) === 'string'){
+ for (var i = 0; i < this.rows.length; i++){
+ ret.push (this.rows[i].obj[value]);
+ }
+ } else {
+ for (var j = 0; j < this.rows.length; j++){
+ ret.push (this.rows[j].arr[value]);
+ }
+ }
+ return ret;
+};
+
+/**
+ * Removes all rows from a Table. While all rows are removed,
+ * columns and column titles are maintained.
+ *
+ * @method clearRows
+ *
+ * @example
+ * <div class="norender">
+ * <code>
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * table.clearRows();
+ * print(table.getRowCount() + " total rows in table");
+ * print(table.getColumnCount() + " total columns in table");
+ * }
+ * </code>
+ * </div>
+ */
+p5.Table.prototype.clearRows = function() {
+ delete this.rows;
+ this.rows = [];
+};
+
+/**
+ * Use addColumn() to add a new column to a Table object.
+ * Typically, you will want to specify a title, so the column
+ * may be easily referenced later by name. (If no title is
+ * specified, the new column's title will be null.)
+ *
+ * @method addColumn
+ * @param {String} [title] title of the given column
+ *
+ * @example
+ * <div class="norender">
+ * <code>
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * table.addColumn("carnivore");
+ * table.set(0, "carnivore", "no");
+ * table.set(1, "carnivore", "yes");
+ * table.set(2, "carnivore", "no");
+ *
+ * //print the results
+ * for (var r = 0; r < table.getRowCount(); r++)
+ * for (var c = 0; c < table.getColumnCount(); c++)
+ * print(table.getString(r, c));
+ * }
+ * </code>
+ * </div>
+ */
+p5.Table.prototype.addColumn = function(title) {
+ var t = title || null;
+ this.columns.push(t);
+};
+
+/**
+ * Returns the total number of columns in a Table.
+ *
+ * @return {Number} Number of columns in this table
+ */
+p5.Table.prototype.getColumnCount = function() {
+ return this.columns.length;
+};
+
+/**
+ * Returns the total number of rows in a Table.
+ *
+ * @method getRowCount
+ * @return {Number} Number of rows in this table
+
+ */
+p5.Table.prototype.getRowCount = function() {
+ return this.rows.length;
+};
+
+/**
+ * <p>Removes any of the specified characters (or "tokens").</p>
+ *
+ * <p>If no column is specified, then the values in all columns and
+ * rows are processed. A specific column may be referenced by
+ * either its ID or title.</p>
+ *
+ * @method removeTokens
+ * @param {String} chars String listing characters to be removed
+ * @param {String|Number} [column] Column ID (number)
+ * or name (string)
+ */
+p5.Table.prototype.removeTokens = function(chars, column) {
+ var escape= function(s) {
+ return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
+ };
+ var charArray = [];
+ for (var i = 0; i < chars.length; i++) {
+ charArray.push( escape( chars.charAt(i) ) );
+ }
+ var regex = new RegExp(charArray.join('|'), 'g');
+
+ if (typeof(column) === 'undefined'){
+ for (var c = 0; c < this.columns.length; c++) {
+ for (var d = 0; d < this.rows.length; d++) {
+ var s = this.rows[d].arr[c];
+ s = s.replace(regex, '');
+ this.rows[d].arr[c] = s;
+ this.rows[d].obj[this.columns[c]] = s;
+ }
+ }
+ }
+ else if (typeof(column) === 'string'){
+ for (var j = 0; j < this.rows.length; j++) {
+ var val = this.rows[j].obj[column];
+ val = val.replace(regex, '');
+ this.rows[j].obj[column] = val;
+ var pos = this.columns.indexOf(column);
+ this.rows[j].arr[pos] = val;
+ }
+ }
+ else {
+ for (var k = 0; k < this.rows.length; k++) {
+ var str = this.rows[k].arr[column];
+ str = str.replace(regex, '');
+ this.rows[k].arr[column] = str;
+ this.rows[k].obj[this.columns[column]] = str;
+ }
+ }
+};
+
+/**
+ * Trims leading and trailing whitespace, such as spaces and tabs,
+ * from String table values. If no column is specified, then the
+ * values in all columns and rows are trimmed. A specific column
+ * may be referenced by either its ID or title.
+ *
+ * @method trim
+ * @param {String|Number} column Column ID (number)
+ * or name (string)
+ */
+p5.Table.prototype.trim = function(column) {
+ var regex = new RegExp( (' '), 'g');
+
+ if (typeof(column) === 'undefined'){
+ for (var c = 0; c < this.columns.length; c++) {
+ for (var d = 0; d < this.rows.length; d++) {
+ var s = this.rows[d].arr[c];
+ s = s.replace(regex, '');
+ this.rows[d].arr[c] = s;
+ this.rows[d].obj[this.columns[c]] = s;
+ }
+ }
+ }
+ else if (typeof(column) === 'string'){
+ for (var j = 0; j < this.rows.length; j++) {
+ var val = this.rows[j].obj[column];
+ val = val.replace(regex, '');
+ this.rows[j].obj[column] = val;
+ var pos = this.columns.indexOf(column);
+ this.rows[j].arr[pos] = val;
+ }
+ }
+ else {
+ for (var k = 0; k < this.rows.length; k++) {
+ var str = this.rows[k].arr[column];
+ str = str.replace(regex, '');
+ this.rows[k].arr[column] = str;
+ this.rows[k].obj[this.columns[column]] = str;
+ }
+ }
+};
+
+/**
+ * Use removeColumn() to remove an existing column from a Table
+ * object. The column to be removed may be identified by either
+ * its title (a String) or its index value (an int).
+ * removeColumn(0) would remove the first column, removeColumn(1)
+ * would remove the second column, and so on.
+ *
+ * @method removeColumn
+ * @param {String|Number} column columnName (string) or ID (number)
+ *
+ * @example
+ * <div class="norender">
+ * <code>
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * table.removeColumn("id");
+ * print(table.getColumnCount());
+ * }
+ * </code>
+ * </div>
+ */
+p5.Table.prototype.removeColumn = function(c) {
+ var cString;
+ var cNumber;
+ if (typeof(c) === 'string') {
+ // find the position of c in the columns
+ cString = c;
+ cNumber = this.columns.indexOf(c);
+ console.log('string');
+ }
+ else{
+ cNumber = c;
+ cString = this.columns[c];
+ }
+
+ var chunk = this.columns.splice(cNumber+1, this.columns.length);
+ this.columns.pop();
+ this.columns = this.columns.concat(chunk);
+
+ for (var i = 0; i < this.rows.length; i++){
+ var tempR = this.rows[i].arr;
+ var chip = tempR.splice(cNumber+1, tempR.length);
+ tempR.pop();
+ this.rows[i].arr = tempR.concat(chip);
+ delete this.rows[i].obj[cString];
+ }
+
+};
+
+
+/**
+ * Stores a value in the Table's specified row and column.
+ * The row is specified by its ID, while the column may be specified
+ * by either its ID or title.
+ *
+ * @method set
+ * @param {String|Number} column column ID (Number)
+ * or title (String)
+ * @param {String|Number} value value to assign
+ *
+ * @example
+ * <div class="norender">
+ * <code>
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * table.set(0, "species", "Canis Lupus");
+ * table.set(0, "name", "Wolf");
+ *
+ * //print the results
+ * for (var r = 0; r < table.getRowCount(); r++)
+ * for (var c = 0; c < table.getColumnCount(); c++)
+ * print(table.getString(r, c));
+ * }
+ * </code>
+ * </div>
+ */
+p5.Table.prototype.set = function(row, column, value) {
+ this.rows[row].set(column, value);
+};
+
+/**
+ * Stores a Float value in the Table's specified row and column.
+ * The row is specified by its ID, while the column may be specified
+ * by either its ID or title.
+ *
+ * @method setNum
+ * @param {Number} row row ID
+ * @param {String|Number} column column ID (Number)
+ * or title (String)
+ * @param {Number} value value to assign
+ *
+ * @example
+ * <div class="norender">
+ * <code>
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * table.setNum(1, "id", 1);
+ *
+ * print(table.getColumn(0));
+ * //["0", 1, "2"]
+ * }
+ * </code>
+ * </div>
+ */
+p5.Table.prototype.setNum = function(row, column, value){
+ this.rows[row].setNum(column, value);
+};
+
+
+/**
+ * Stores a String value in the Table's specified row and column.
+ * The row is specified by its ID, while the column may be specified
+ * by either its ID or title.
+ *
+ * @method setString
+ * @param {Number} row row ID
+ * @param {String|Number} column column ID (Number)
+ * or title (String)
+ * @param {String} value value to assign
+ */
+p5.Table.prototype.setString = function(row, column, value){
+ this.rows[row].setString(column, value);
+};
+
+/**
+ * Retrieves a value from the Table's specified row and column.
+ * The row is specified by its ID, while the column may be specified by
+ * either its ID or title.
+ *
+ * @method get
+ * @param {Number} row row ID
+ * @param {String|Number} column columnName (string) or
+ * ID (number)
+ * @return {String|Number}
+ *
+ * @example
+ * <div class="norender">
+ * <code>
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * print(table.get(0, 1));
+ * //Capra hircus
+ * print(table.get(0, "species"));
+ * //Capra hircus
+ * }
+ * </code>
+ * </div>
+ */
+p5.Table.prototype.get = function(row, column) {
+ return this.rows[row].get(column);
+};
+
+/**
+ * Retrieves a Float value from the Table's specified row and column.
+ * The row is specified by its ID, while the column may be specified by
+ * either its ID or title.
+ *
+ * @method getNum
+ * @param {Number} row row ID
+ * @param {String|Number} column columnName (string) or
+ * ID (number)
+ * @return {Number}
+ *
+ * @example
+ * <div class="norender">
+ * <code>
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * print(table.getNum(1, 0) + 100);
+ * //id 1 + 100 = 101
+ * }
+ * </code>
+ * </div>
+ */
+p5.Table.prototype.getNum = function(row, column) {
+ return this.rows[row].getNum(column);
+};
+
+/**
+ * Retrieves a String value from the Table's specified row and column.
+ * The row is specified by its ID, while the column may be specified by
+ * either its ID or title.
+ *
+ * @method getString
+ * @param {Number} row row ID
+ * @param {String|Number} column columnName (string) or
+ * ID (number)
+ * @return {String}
+ *
+ * @example
+ * <div class="norender">
+ * <code>
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * var tableArray = table.getArray();
+ *
+ * //output each row as array
+ * for (var i = 0; i < tableArray.length; i++)
+ * print(tableArray[i]);
+ * }
+ * </code>
+ * </div>
+ */
+p5.Table.prototype.getString = function(row, column) {
+ return this.rows[row].getString(column);
+};
+
+/**
+ * Retrieves all table data and returns as an object. If a column name is
+ * passed in, each row object will be stored with that attribute as its
+ * title.
+ *
+ * @method getObject
+ * @param {String} headerColumn Name of the column which should be used to
+ * title each row object (optional)
+ * @return {Object}
+ *
+ * @example
+ * <div class="norender">
+ * <code>
+ * // Given the CSV file "mammals.csv"
+ * // in the project's "assets" folder:
+ * //
+ * // id,species,name
+ * // 0,Capra hircus,Goat
+ * // 1,Panthera pardus,Leopard
+ * // 2,Equus zebra,Zebra
+ *
+ * var table;
+ *
+ * function preload() {
+ * //my table is comma separated value "csv"
+ * //and has a header specifying the columns labels
+ * table = loadTable("assets/mammals.csv", "csv", "header");
+ * }
+ *
+ * function setup() {
+ * var tableObject = table.getObject();
+ *
+ * print(tableObject);
+ * //outputs an object
+ * }
+ * </code>
+ * </div>
+
+ */
+p5.Table.prototype.getObject = function (headerColumn) {
+ var tableObject = {};
+ var obj, cPos, index;
+
+ for(var i = 0; i < this.rows.length; i++) {
+ obj = this.rows[i].obj;
+
+ if (typeof(headerColumn) === 'string'){
+ cPos = this.columns.indexOf(headerColumn); // index of columnID
+ if (cPos >= 0) {
+ index = obj[headerColumn];
+ tableObject[index] = obj;
+ } else {
+ throw 'This table has no column named "' + headerColumn +'"';
+ }
+ } else {
+ tableObject[i] = this.rows[i].obj;
+ }
+ }
+ return tableObject;
+};
+
+/**
+ * Retrieves all table data and returns it as a multidimensional array.
+ *
+ * @method getArray
+ * @return {Array}
+ */
+p5.Table.prototype.getArray = function () {
+ var tableArray = [];
+ for(var i = 0; i < this.rows.length; i++) {
+ tableArray.push(this.rows[i].arr);
+ }
+ return tableArray;
+};
+
+module.exports = p5.Table;
+
+},{"../core/core":48}],72:[function(_dereq_,module,exports){
+/**
+ * @module IO
+ * @submodule Table
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * A TableRow object represents a single row of data values,
+ * stored in columns, from a table.
+ *
+ * A Table Row contains both an ordered array, and an unordered
+ * JSON object.
+ *
+ * @class p5.TableRow
+ * @constructor
+ * @param {String} [str] optional: populate the row with a
+ * string of values, separated by the
+ * separator
+ * @param {String} [separator] comma separated values (csv) by default
+ */
+p5.TableRow = function (str, separator) {
+ var arr = [];
+ var obj = {};
+ if (str){
+ separator = separator || ',';
+ arr = str.split(separator);
+ }
+ for (var i = 0; i < arr.length; i++){
+ var key = i;
+ var val = arr[i];
+ obj[key] = val;
+ }
+ this.arr = arr;
+ this.obj = obj;
+ this.table = null;
+};
+
+/**
+ * Stores a value in the TableRow's specified column.
+ * The column may be specified by either its ID or title.
+ *
+ * @method set
+ * @param {String|Number} column Column ID (Number)
+ * or Title (String)
+ * @param {String|Number} value The value to be stored
+ */
+p5.TableRow.prototype.set = function(column, value) {
+ // if typeof column is string, use .obj
+ if (typeof(column) === 'string'){
+ var cPos = this.table.columns.indexOf(column); // index of columnID
+ if (cPos >= 0) {
+ this.obj[column] = value;
+ this.arr[cPos] = value;
+ }
+ else {
+ throw 'This table has no column named "' + column +'"';
+ }
+ }
+
+ // if typeof column is number, use .arr
+ else {
+ if (column < this.table.columns.length) {
+ this.arr[column] = value;
+ var cTitle = this.table.columns[column];
+ this.obj[cTitle] = value;
+ }
+ else {
+ throw 'Column #' + column + ' is out of the range of this table';
+ }
+ }
+};
+
+
+/**
+ * Stores a Float value in the TableRow's specified column.
+ * The column may be specified by either its ID or title.
+ *
+ * @method setNum
+ * @param {String|Number} column Column ID (Number)
+ * or Title (String)
+ * @param {Number} value The value to be stored
+ * as a Float
+ */
+p5.TableRow.prototype.setNum = function(column, value){
+ var floatVal = parseFloat(value, 10);
+ this.set(column, floatVal);
+};
+
+
+/**
+ * Stores a String value in the TableRow's specified column.
+ * The column may be specified by either its ID or title.
+ *
+ * @method setString
+ * @param {String|Number} column Column ID (Number)
+ * or Title (String)
+ * @param {String} value The value to be stored
+ * as a String
+ */
+p5.TableRow.prototype.setString = function(column, value){
+ var stringVal = value.toString();
+ this.set(column, stringVal);
+};
+
+/**
+ * Retrieves a value from the TableRow's specified column.
+ * The column may be specified by either its ID or title.
+ *
+ * @method get
+ * @param {String|Number} column columnName (string) or
+ * ID (number)
+ * @return {String|Number}
+ */
+p5.TableRow.prototype.get = function(column) {
+ if (typeof(column) === 'string'){
+ return this.obj[column];
+ } else {
+ return this.arr[column];
+ }
+};
+
+/**
+ * Retrieves a Float value from the TableRow's specified
+ * column. The column may be specified by either its ID or
+ * title.
+ *
+ * @method getNum
+ * @param {String|Number} column columnName (string) or
+ * ID (number)
+ * @return {Number} Float Floating point number
+ */
+p5.TableRow.prototype.getNum = function(column) {
+ var ret;
+ if (typeof(column) === 'string'){
+ ret = parseFloat(this.obj[column], 10);
+ } else {
+ ret = parseFloat(this.arr[column], 10);
+ }
+
+ if (ret.toString() === 'NaN') {
+ throw 'Error: ' + this.obj[column]+ ' is NaN (Not a Number)';
+ }
+ return ret;
+};
+
+/**
+ * Retrieves an String value from the TableRow's specified
+ * column. The column may be specified by either its ID or
+ * title.
+ *
+ * @method getString
+ * @param {String|Number} column columnName (string) or
+ * ID (number)
+ * @return {String} String
+ */
+p5.TableRow.prototype.getString = function(column) {
+ if (typeof(column) === 'string'){
+ return this.obj[column].toString();
+ } else {
+ return this.arr[column].toString();
+ }
+};
+
+module.exports = p5.TableRow;
+
+},{"../core/core":48}],73:[function(_dereq_,module,exports){
+/**
+ * @module Math
+ * @submodule Calculation
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * Calculates the absolute value (magnitude) of a number. Maps to Math.abs().
+ * The absolute value of a number is always positive.
+ *
+ * @method abs
+ * @param {Number} n number to compute
+ * @return {Number} absolute value of given number
+ * @example
+ * <div class = "norender"><code>
+ * function setup() {
+ * var x = -3;
+ * var y = abs(x);
+ *
+ * print(x); // -3
+ * print(y); // 3
+ * }
+ * </code></div>
+ */
+p5.prototype.abs = Math.abs;
+
+/**
+ * Calculates the closest int value that is greater than or equal to the
+ * value of the parameter. Maps to Math.ceil(). For example, ceil(9.03)
+ * returns the value 10.
+ *
+ * @method ceil
+ * @param {Number} n number to round up
+ * @return {Number} rounded up number
+ * @example
+ * <div><code>
+ * function draw() {
+ * background(200);
+ * // map, mouseX between 0 and 5.
+ * var ax = map(mouseX, 0, 100, 0, 5);
+ * var ay = 66;
+ *
+ * //Get the ceiling of the mapped number.
+ * var bx = ceil(map(mouseX, 0, 100, 0,5));
+ * var by = 33;
+ *
+ * // Multiply the mapped numbers by 20 to more easily
+ * // see the changes.
+ * stroke(0);
+ * fill(0);
+ * line(0, ay, ax * 20, ay);
+ * line(0, by, bx * 20, by);
+ *
+ * // Reformat the float returned by map and draw it.
+ * noStroke();
+ * text(nfc(ax, 2,2), ax, ay - 5);
+ * text(nfc(bx,1,1), bx, by - 5);
+ * }
+ * </code></div>
+ */
+p5.prototype.ceil = Math.ceil;
+
+/**
+ * Constrains a value between a minimum and maximum value.
+ *
+ * @method constrain
+ * @param {Number} n number to constrain
+ * @param {Number} low minimum limit
+ * @param {Number} high maximum limit
+ * @return {Number} constrained number
+ * @example
+ * <div><code>
+ * function draw() {
+ * background(200);
+ *
+ * var leftWall = 25;
+ * var rightWall = 75;
+ *
+ * // xm is just the mouseX, while
+ * // xc is the mouseX, but constrained
+ * // between the leftWall and rightWall!
+ * var xm = mouseX;
+ * var xc = constrain(mouseX, leftWall, rightWall);
+ *
+ * // Draw the walls.
+ * stroke(150);
+ * line(leftWall, 0, leftWall, height);
+ * line(rightWall, 0, rightWall, height);
+ *
+ * // Draw xm and xc as circles.
+ * noStroke();
+ * fill(150);
+ * ellipse(xm, 33, 9,9); // Not Constrained
+ * fill(0);
+ * ellipse(xc, 66, 9,9); // Constrained
+ * }
+ * </code></div>
+ */
+p5.prototype.constrain = function(n, low, high) {
+ return Math.max(Math.min(n, high), low);
+};
+
+/**
+ * Calculates the distance between two points.
+ *
+ * @method dist
+ * @param {Number} x1 x-coordinate of the first point
+ * @param {Number} y1 y-coordinate of the first point
+ * @param {Number} [z1] z-coordinate of the first point
+ * @param {Number} x2 x-coordinate of the second point
+ * @param {Number} y2 y-coordinate of the second point
+ * @param {Number} [z2] z-coordinate of the second point
+ * @return {Number} distance between the two points
+ * @example
+ * <div><code>
+ * // Move your mouse inside the canvas to see the
+ * // change in distance between two points!
+ * function draw() {
+ * background(200);
+ * fill(0);
+ *
+ * var x1 = 10;
+ * var y1 = 90;
+ * var x2 = mouseX;
+ * var y2 = mouseY;
+ *
+ * line(x1, y1, x2, y2);
+ * ellipse(x1, y1, 7, 7);
+ * ellipse(x2, y2, 7, 7);
+ *
+ * // d is the length of the line
+ * // the distance from point 1 to point 2.
+ * var d = int(dist(x1, y1, x2, y2));
+ *
+ * // Let's write d along the line we are drawing!
+ * push();
+ * translate( (x1+x2)/2, (y1+y2)/2 );
+ * rotate( atan2(y2-y1,x2-x1) );
+ * text(nfc(d,1,1), 0, -5);
+ * pop();
+ * // Fancy!
+ * }
+ * </code></div>
+ */
+p5.prototype.dist = function(x1, y1, z1, x2, y2, z2) {
+ if (arguments.length === 4) {
+ // In the case of 2d: z1 means x2 and x2 means y2
+ return Math.sqrt( (z1-x1)*(z1-x1) + (x2-y1)*(x2-y1) );
+ } else if (arguments.length === 6) {
+ return Math.sqrt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) + (z2-z1)*(z2-z1) );
+ }
+};
+
+/**
+ * Returns Euler's number e (2.71828...) raised to the power of the n
+ * parameter. Maps to Math.exp().
+ *
+ * @method exp
+ * @param {Number} n exponent to raise
+ * @return {Number} e^n
+ * @example
+ * <div><code>
+ * function draw() {
+ * background(200);
+ *
+ * // Compute the exp() function with a value between 0 and 2
+ * var xValue = map(mouseX, 0, width, 0, 2);
+ * var yValue = exp(xValue);
+ *
+ * var y = map(yValue, 0, 8, height, 0);
+ *
+ * var legend = "exp (" + nfc(xValue, 3) +")\n= " + nf(yValue, 1, 4);
+ * stroke(150);
+ * line(mouseX, y, mouseX, height);
+ * fill(0);
+ * text(legend, 5, 15);
+ * noStroke();
+ * ellipse (mouseX,y, 7, 7);
+ *
+ * // Draw the exp(x) curve,
+ * // over the domain of x from 0 to 2
+ * noFill();
+ * stroke(0);
+ * beginShape();
+ * for (var x = 0; x < width; x++) {
+ * xValue = map(x, 0, width, 0, 2);
+ * yValue = exp(xValue);
+ * y = map(yValue, 0, 8, height, 0);
+ * vertex(x, y);
+ * }
+ *
+ * endShape();
+ * line(0, 0, 0, height);
+ * line(0, height-1, width, height-1);
+ * }
+ * </code></div>
+ */
+p5.prototype.exp = Math.exp;
+
+/**
+ * Calculates the closest int value that is less than or equal to the
+ * value of the parameter. Maps to Math.floor().
+ *
+ * @method floor
+ * @param {Number} n number to round down
+ * @return {Number} rounded down number
+ * @example
+ * <div><code>
+ * function draw() {
+ * background(200);
+ * //map, mouseX between 0 and 5.
+ * var ax = map(mouseX, 0, 100, 0, 5);
+ * var ay = 66;
+ *
+ * //Get the floor of the mapped number.
+ * var bx = floor(map(mouseX, 0, 100, 0,5));
+ * var by = 33;
+ *
+ * // Multiply the mapped numbers by 20 to more easily
+ * // see the changes.
+ * stroke(0);
+ * fill(0);
+ * line(0, ay, ax * 20, ay);
+ * line(0, by, bx * 20, by);
+ *
+ * // Reformat the float returned by map and draw it.
+ * noStroke();
+ * text(nfc(ax, 2,2), ax, ay - 5);
+ * text(nfc(bx,1,1), bx, by - 5);
+ * }
+ * </code></div>
+ */
+p5.prototype.floor = Math.floor;
+
+/**
+ * Calculates a number between two numbers at a specific increment. The amt
+ * parameter is the amount to interpolate between the two values where 0.0
+ * equal to the first point, 0.1 is very near the first point, 0.5 is
+ * half-way in between, etc. The lerp function is convenient for creating
+ * motion along a straight path and for drawing dotted lines.
+ *
+ * @method lerp
+ * @param {Number} start first value
+ * @param {Number} stop second value
+ * @param {Number} amt number between 0.0 and 1.0
+ * @return {Number} lerped value
+ * @example
+ * <div><code>
+ * function setup() {
+ * background(200);
+ * var a = 20;
+ * var b = 80;
+ * var c = lerp(a,b, .2);
+ * var d = lerp(a,b, .5);
+ * var e = lerp(a,b, .8);
+ *
+ * var y = 50
+ *
+ * strokeWeight(5);
+ * stroke(0); // Draw the original points in black
+ * point(a, y);
+ * point(b, y);
+ *
+ * stroke(100); // Draw the lerp points in gray
+ * point(c, y);
+ * point(d, y);
+ * point(e, y);
+ * }
+ * </code></div>
+ */
+p5.prototype.lerp = function(start, stop, amt) {
+ return amt*(stop-start)+start;
+};
+
+/**
+ * Calculates the natural logarithm (the base-e logarithm) of a number. This
+ * function expects the n parameter to be a value greater than 0.0. Maps to
+ * Math.log().
+ *
+ * @method log
+ * @param {Number} n number greater than 0
+ * @return {Number} natural logarithm of n
+ * @example
+ * <div><code>
+ * function draw() {
+ * background(200);
+ * var maxX = 2.8;
+ * var maxY = 1.5;
+ *
+ * // Compute the natural log of a value between 0 and maxX
+ * var xValue = map(mouseX, 0, width, 0, maxX);
+ * if (xValue > 0) { // Cannot take the log of a negative number.
+ * var yValue = log(xValue);
+ * var y = map(yValue, -maxY, maxY, height, 0);
+ *
+ * // Display the calculation occurring.
+ * var legend = "log(" + nf(xValue, 1, 2) + ")\n= " + nf(yValue, 1, 3);
+ * stroke(150);
+ * line(mouseX, y, mouseX, height);
+ * fill(0);
+ * text (legend, 5, 15);
+ * noStroke();
+ * ellipse (mouseX, y, 7, 7);
+ * }
+ *
+ * // Draw the log(x) curve,
+ * // over the domain of x from 0 to maxX
+ * noFill();
+ * stroke(0);
+ * beginShape();
+ * for(var x=0; x < width; x++) {
+ * xValue = map(x, 0, width, 0, maxX);
+ * yValue = log(xValue);
+ * y = map(yValue, -maxY, maxY, height, 0);
+ * vertex(x, y);
+ * }
+ * endShape();
+ * line(0,0,0,height);
+ * line(0,height/2,width, height/2);
+ * }
+ * </code></div>
+ */
+p5.prototype.log = Math.log;
+
+/**
+ * Calculates the magnitude (or length) of a vector. A vector is a direction
+ * in space commonly used in computer graphics and linear algebra. Because it
+ * has no "start" position, the magnitude of a vector can be thought of as
+ * the distance from the coordinate 0,0 to its x,y value. Therefore, mag() is
+ * a shortcut for writing dist(0, 0, x, y).
+ *
+ * @method mag
+ * @param {Number} a first value
+ * @param {Number} b second value
+ * @return {Number} magnitude of vector from (0,0) to (a,b)
+ * @example
+ * <div><code>
+ * function setup() {
+ * var x1 = 20;
+ * var x2 = 80;
+ * var y1 = 30;
+ * var y2 = 70;
+ *
+ * line(0, 0, x1, y1);
+ * print(mag(x1, y1)); // Prints "36.05551"
+ * line(0, 0, x2, y1);
+ * print(mag(x2, y1)); // Prints "85.44004"
+ * line(0, 0, x1, y2);
+ * print(mag(x1, y2)); // Prints "72.8011"
+ * line(0, 0, x2, y2);
+ * print(mag(x2, y2)); // Prints "106.30146"
+ * }
+ * </code></div>
+ */
+p5.prototype.mag = function(x, y) {
+ return Math.sqrt(x*x+y*y);
+};
+
+/**
+ * Re-maps a number from one range to another.
+ * <br><br>
+ * In the first example above, the number 25 is converted from a value in the
+ * range of 0 to 100 into a value that ranges from the left edge of the
+ * window (0) to the right edge (width).
+ *
+ * @method map
+ * @param {Number} value the incoming value to be converted
+ * @param {Number} start1 lower bound of the value's current range
+ * @param {Number} stop1 upper bound of the value's current range
+ * @param {Number} start2 lower bound of the value's target range
+ * @param {Number} stop upper bound of the value's target range
+ * @return {Number} remapped number
+ * @example
+ * <div><code>
+ * var value = 25;
+ * var m = map(value, 0, 100, 0, width);
+ * ellipse(m, 50, 10, 10);
+ * </code></div>
+ *
+ * <div><code>
+ * function setup() {
+ * noStroke();
+ * }
+ *
+ * function draw() {
+ * background(204);
+ * var x1 = map(mouseX, 0, width, 25, 75);
+ * ellipse(x1, 25, 25, 25);
+ * var x2 = map(mouseX, 0, width, 0, 100);
+ * ellipse(x2, 75, 25, 25);
+ * }
+ * </code></div>
+ */
+p5.prototype.map = function(n, start1, stop1, start2, stop2) {
+ return ((n-start1)/(stop1-start1))*(stop2-start2)+start2;
+};
+
+/**
+ * Determines the largest value in a sequence of numbers, and then returns
+ * that value. max() accepts any number of Number parameters, or an Array
+ * of any length.
+ *
+ * @method max
+ * @param {Number|Array} n0 Numbers to compare
+ * @return {Number} maximum Number
+ * @example
+ * <div><code>
+ * function setup() {
+ * // Change the elements in the array and run the sketch
+ * // to show how max() works!
+ * numArray = new Array(2,1,5,4,8,9);
+ * fill(0);
+ * noStroke();
+ * text("Array Elements", 0, 10);
+ * // Draw all numbers in the array
+ * var spacing = 15;
+ * var elemsY = 25;
+ * for(var i = 0; i < numArray.length; i++) {
+ * text(numArray[i], i * spacing, elemsY);
+ * }
+ * maxX = 33;
+ * maxY = 80;
+ * // Draw the Maximum value in the array.
+ * textSize(32);
+ * text(max(numArray), maxX, maxY);
+ * }
+ * </code></div>
+ */
+p5.prototype.max = function() {
+ if (arguments[0] instanceof Array) {
+ return Math.max.apply(null,arguments[0]);
+ } else {
+ return Math.max.apply(null,arguments);
+ }
+};
+
+/**
+ * Determines the smallest value in a sequence of numbers, and then returns
+ * that value. min() accepts any number of Number parameters, or an Array
+ * of any length.
+ *
+ * @method min
+ * @param {Number|Array} n0 Numbers to compare
+ * @return {Number} minimum Number
+ * @example
+ * <div><code>
+ * function setup() {
+ * // Change the elements in the array and run the sketch
+ * // to show how min() works!
+ * numArray = new Array(2,1,5,4,8,9);
+ * fill(0);
+ * noStroke();
+ * text("Array Elements", 0, 10);
+ * // Draw all numbers in the array
+ * var spacing = 15;
+ * var elemsY = 25;
+ * for(var i = 0; i < numArray.length; i++) {
+ * text(numArray[i], i * spacing, elemsY);
+ * }
+ * maxX = 33;
+ * maxY = 80;
+ * // Draw the Minimum value in the array.
+ * textSize(32);
+ * text(min(numArray), maxX, maxY);
+ * }
+ * </code></div>
+ */
+p5.prototype.min = function() {
+ if (arguments[0] instanceof Array) {
+ return Math.min.apply(null,arguments[0]);
+ } else {
+ return Math.min.apply(null,arguments);
+ }
+};
+
+/**
+ * Normalizes a number from another range into a value between 0 and 1.
+ * Identical to map(value, low, high, 0, 1).
+ * Numbers outside of the range are not clamped to 0 and 1, because
+ * out-of-range values are often intentional and useful. (See the second
+ * example above.)
+ *
+ * @method norm
+ * @param {Number} value incoming value to be normalized
+ * @param {Number} start lower bound of the value's current range
+ * @param {Number} stop upper bound of the value's current range
+ * @return {Number} normalized number
+ * @example
+ * <div><code>
+ * function draw() {
+ * background(200);
+ * currentNum = mouseX;
+ * lowerBound = 0;
+ * upperBound = width; //100;
+ * normalized = norm(currentNum, lowerBound, upperBound);
+ * lineY = 70
+ * line(0, lineY, width, lineY);
+ * //Draw an ellipse mapped to the non-normalized value.
+ * noStroke();
+ * fill(50)
+ * var s = 7; // ellipse size
+ * ellipse(currentNum, lineY, s, s);
+ *
+ * // Draw the guide
+ * guideY = lineY + 15;
+ * text("0", 0, guideY);
+ * textAlign(RIGHT);
+ * text("100", width, guideY);
+ *
+ * // Draw the normalized value
+ * textAlign(LEFT);
+ * fill(0);
+ * textSize(32);
+ * normalY = 40;
+ * normalX = 20;
+ * text(normalized, normalX, normalY);
+ * }
+ * </code></div>
+ */
+p5.prototype.norm = function(n, start, stop) {
+ return this.map(n, start, stop, 0, 1);
+};
+
+/**
+ * Facilitates exponential expressions. The pow() function is an efficient
+ * way of multiplying numbers by themselves (or their reciprocals) in large
+ * quantities. For example, pow(3, 5) is equivalent to the expression
+ * 3*3*3*3*3 and pow(3, -5) is equivalent to 1 / 3*3*3*3*3. Maps to
+ * Math.pow().
+ *
+ * @method pow
+ * @param {Number} n base of the exponential expression
+ * @param {Number} e power by which to raise the base
+ * @return {Number} n^e
+ * @example
+ * <div><code>
+ * function setup() {
+ * //Exponentially increase the size of an ellipse.
+ * eSize = 3; // Original Size
+ * eLoc = 10; // Original Location
+ *
+ * ellipse(eLoc, eLoc, eSize, eSize);
+ *
+ * ellipse(eLoc*2, eLoc*2, pow(eSize, 2), pow(eSize, 2));
+ *
+ * ellipse(eLoc*4, eLoc*4, pow(eSize, 3), pow(eSize, 3));
+ *
+ * ellipse(eLoc*8, eLoc*8, pow(eSize, 4), pow(eSize, 4));
+ * }
+ * </code></div>
+ */
+p5.prototype.pow = Math.pow;
+
+/**
+ * Calculates the integer closest to the n parameter. For example,
+ * round(133.8) returns the value 134. Maps to Math.round().
+ *
+ * @method round
+ * @param {Number} n number to round
+ * @return {Number} rounded number
+ * @example
+ * <div><code>
+ * function draw() {
+ * background(200);
+ * //map, mouseX between 0 and 5.
+ * var ax = map(mouseX, 0, 100, 0, 5);
+ * var ay = 66;
+ *
+ * // Round the mapped number.
+ * var bx = round(map(mouseX, 0, 100, 0,5));
+ * var by = 33;
+ *
+ * // Multiply the mapped numbers by 20 to more easily
+ * // see the changes.
+ * stroke(0);
+ * fill(0);
+ * line(0, ay, ax * 20, ay);
+ * line(0, by, bx * 20, by);
+ *
+ * // Reformat the float returned by map and draw it.
+ * noStroke();
+ * text(nfc(ax, 2,2), ax, ay - 5);
+ * text(nfc(bx,1,1), bx, by - 5);
+ * }
+ * </code></div>
+ */
+p5.prototype.round = Math.round;
+
+/**
+ * Squares a number (multiplies a number by itself). The result is always a
+ * positive number, as multiplying two negative numbers always yields a
+ * positive result. For example, -1 * -1 = 1.
+ *
+ * @method sq
+ * @param {Number} n number to square
+ * @return {Number} squared number
+ * @example
+ * <div><code>
+ * function draw() {
+ * background(200);
+ * eSize = 7;
+ * x1 = map(mouseX, 0, width, 0, 10);
+ * y1 = 80;
+ * x2 = sq(x1);
+ * y2 = 20;
+ *
+ * // Draw the non-squared.
+ * line(0, y1, width, y1);
+ * ellipse(x1, y1, eSize, eSize);
+ *
+ * // Draw the squared.
+ * line(0, y2, width, y2);
+ * ellipse(x2, y2, eSize, eSize);
+ *
+ * // Draw dividing line.
+ * stroke(100)
+ * line(0, height/2, width, height/2);
+ *
+ * // Draw text.
+ * var spacing = 15;
+ * noStroke();
+ * fill(0);
+ * text("x = " + x1, 0, y1 + spacing);
+ * text("sq(x) = " + x2, 0, y2 + spacing);
+ * }
+ * </code></div>
+ */
+p5.prototype.sq = function(n) { return n*n; };
+
+/**
+ * Calculates the square root of a number. The square root of a number is
+ * always positive, even though there may be a valid negative root. The
+ * square root s of number a is such that s*s = a. It is the opposite of
+ * squaring. Maps to Math.sqrt().
+ *
+ * @method sqrt
+ * @param {Number} n non-negative number to square root
+ * @return {Number} square root of number
+ * @example
+ * <div><code>
+ * function draw() {
+ * background(200);
+ * eSize = 7;
+ * x1 = mouseX;
+ * y1 = 80;
+ * x2 = sqrt(x1);
+ * y2 = 20;
+ *
+ * // Draw the non-squared.
+ * line(0, y1, width, y1);
+ * ellipse(x1, y1, eSize, eSize);
+ *
+ * // Draw the squared.
+ * line(0, y2, width, y2);
+ * ellipse(x2, y2, eSize, eSize);
+ *
+ * // Draw dividing line.
+ * stroke(100)
+ * line(0, height/2, width, height/2);
+ *
+ * // Draw text.
+ * noStroke();
+ * fill(0);
+ * var spacing = 15;
+ * text("x = " + x1, 0, y1 + spacing);
+ * text("sqrt(x) = " + x2, 0, y2 + spacing);
+ * }
+ * </code></div>
+ */
+p5.prototype.sqrt = Math.sqrt;
+
+module.exports = p5;
+
+},{"../core/core":48}],74:[function(_dereq_,module,exports){
+/**
+ * @module Math
+ * @submodule Math
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+
+/**
+ * Creates a new p5.Vector (the datatype for storing vectors). This provides a
+ * two or three dimensional vector, specifically a Euclidean (also known as
+ * geometric) vector. A vector is an entity that has both magnitude and
+ * direction.
+ *
+ * @method createVector
+ * @param {Number} [x] x component of the vector
+ * @param {Number} [y] y component of the vector
+ * @param {Number} [z] z component of the vector
+ */
+p5.prototype.createVector = function (x, y, z) {
+ if (this instanceof p5) {
+ return new p5.Vector(this, arguments);
+ } else {
+ return new p5.Vector(x, y, z);
+ }
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],75:[function(_dereq_,module,exports){
+//////////////////////////////////////////////////////////////
+
+// http://mrl.nyu.edu/~perlin/noise/
+// Adapting from PApplet.java
+// which was adapted from toxi
+// which was adapted from the german demo group farbrausch
+// as used in their demo "art": http://www.farb-rausch.de/fr010src.zip
+
+// someday we might consider using "improved noise"
+// http://mrl.nyu.edu/~perlin/paper445.pdf
+// See: https://github.com/shiffman/The-Nature-of-Code-Examples-p5.js/
+// blob/master/introduction/Noise1D/noise.js
+
+/**
+ * @module Math
+ * @submodule Noise
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+var PERLIN_YWRAPB = 4;
+var PERLIN_YWRAP = 1<<PERLIN_YWRAPB;
+var PERLIN_ZWRAPB = 8;
+var PERLIN_ZWRAP = 1<<PERLIN_ZWRAPB;
+var PERLIN_SIZE = 4095;
+
+var perlin_octaves = 4; // default to medium smooth
+var perlin_amp_falloff = 0.5; // 50% reduction/octave
+
+var scaled_cosine = function(i) {
+ return 0.5*(1.0-Math.cos(i*Math.PI));
+};
+
+var perlin; // will be initialized lazily by noise() or noiseSeed()
+
+
+/**
+ * Returns the Perlin noise value at specified coordinates. Perlin noise is
+ * a random sequence generator producing a more natural ordered, harmonic
+ * succession of numbers compared to the standard <b>random()</b> function.
+ * It was invented by Ken Perlin in the 1980s and been used since in
+ * graphical applications to produce procedural textures, natural motion,
+ * shapes, terrains etc.<br /><br /> The main difference to the
+ * <b>random()</b> function is that Perlin noise is defined in an infinite
+ * n-dimensional space where each pair of coordinates corresponds to a
+ * fixed semi-random value (fixed only for the lifespan of the program; see
+ * the noiseSeed() function). p5.js can compute 1D, 2D and 3D noise,
+ * depending on the number of coordinates given. The resulting value will
+ * always be between 0.0 and 1.0. The noise value can be animated by moving
+ * through the noise space as demonstrated in the example above. The 2nd
+ * and 3rd dimension can also be interpreted as time.<br /><br />The actual
+ * noise is structured similar to an audio signal, in respect to the
+ * function's use of frequencies. Similar to the concept of harmonics in
+ * physics, perlin noise is computed over several octaves which are added
+ * together for the final result. <br /><br />Another way to adjust the
+ * character of the resulting sequence is the scale of the input
+ * coordinates. As the function works within an infinite space the value of
+ * the coordinates doesn't matter as such, only the distance between
+ * successive coordinates does (eg. when using <b>noise()</b> within a
+ * loop). As a general rule the smaller the difference between coordinates,
+ * the smoother the resulting noise sequence will be. Steps of 0.005-0.03
+ * work best for most applications, but this will differ depending on use.
+ *
+ *
+ * @method noise
+ * @param {Number} x x-coordinate in noise space
+ * @param {Number} y y-coordinate in noise space
+ * @param {Number} z z-coordinate in noise space
+ * @return {Number} Perlin noise value (between 0 and 1) at specified
+ * coordinates
+ * @example
+ * <div>
+ * <code>var xoff = 0.0;
+ *
+ * function draw() {
+ * background(204);
+ * xoff = xoff + .01;
+ * var n = noise(xoff) * width;
+ * line(n, 0, n, height);
+ * }
+ * </code>
+ * </div>
+ * <div>
+ * <code>var noiseScale=0.02;
+ *
+ * function draw() {
+ * background(0);
+ * for (var x=0; x < width; x++) {
+ * var noiseVal = noise((mouseX+x)*noiseScale, mouseY*noiseScale);
+ * stroke(noiseVal*255);
+ * line(x, mouseY+noiseVal*80, x, height);
+ * }
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.noise = function(x,y,z) {
+ y = y || 0;
+ z = z || 0;
+
+ if (perlin == null) {
+ perlin = new Array(PERLIN_SIZE + 1);
+ for (var i = 0; i < PERLIN_SIZE + 1; i++) {
+ perlin[i] = Math.random();
+ }
+ }
+
+ if (x<0) { x=-x; }
+ if (y<0) { y=-y; }
+ if (z<0) { z=-z; }
+
+ var xi=Math.floor(x), yi=Math.floor(y), zi=Math.floor(z);
+ var xf = x - xi;
+ var yf = y - yi;
+ var zf = z - zi;
+ var rxf, ryf;
+
+ var r=0;
+ var ampl=0.5;
+
+ var n1,n2,n3;
+
+ for (var o=0; o<perlin_octaves; o++) {
+ var of=xi+(yi<<PERLIN_YWRAPB)+(zi<<PERLIN_ZWRAPB);
+
+ rxf = scaled_cosine(xf);
+ ryf = scaled_cosine(yf);
+
+ n1 = perlin[of&PERLIN_SIZE];
+ n1 += rxf*(perlin[(of+1)&PERLIN_SIZE]-n1);
+ n2 = perlin[(of+PERLIN_YWRAP)&PERLIN_SIZE];
+ n2 += rxf*(perlin[(of+PERLIN_YWRAP+1)&PERLIN_SIZE]-n2);
+ n1 += ryf*(n2-n1);
+
+ of += PERLIN_ZWRAP;
+ n2 = perlin[of&PERLIN_SIZE];
+ n2 += rxf*(perlin[(of+1)&PERLIN_SIZE]-n2);
+ n3 = perlin[(of+PERLIN_YWRAP)&PERLIN_SIZE];
+ n3 += rxf*(perlin[(of+PERLIN_YWRAP+1)&PERLIN_SIZE]-n3);
+ n2 += ryf*(n3-n2);
+
+ n1 += scaled_cosine(zf)*(n2-n1);
+
+ r += n1*ampl;
+ ampl *= perlin_amp_falloff;
+ xi<<=1;
+ xf*=2;
+ yi<<=1;
+ yf*=2;
+ zi<<=1;
+ zf*=2;
+
+ if (xf>=1.0) { xi++; xf--; }
+ if (yf>=1.0) { yi++; yf--; }
+ if (zf>=1.0) { zi++; zf--; }
+ }
+ return r;
+};
+
+
+/**
+ *
+ * Adjusts the character and level of detail produced by the Perlin noise
+ * function. Similar to harmonics in physics, noise is computed over
+ * several octaves. Lower octaves contribute more to the output signal and
+ * as such define the overall intensity of the noise, whereas higher octaves
+ * create finer grained details in the noise sequence.
+ * <br><br>
+ * By default, noise is computed over 4 octaves with each octave contributing
+ * exactly half than its predecessor, starting at 50% strength for the 1st
+ * octave. This falloff amount can be changed by adding an additional function
+ * parameter. Eg. a falloff factor of 0.75 means each octave will now have
+ * 75% impact (25% less) of the previous lower octave. Any value between
+ * 0.0 and 1.0 is valid, however note that values greater than 0.5 might
+ * result in greater than 1.0 values returned by <b>noise()</b>.
+ * <br><br>
+ * By changing these parameters, the signal created by the <b>noise()</b>
+ * function can be adapted to fit very specific needs and characteristics.
+ *
+ * @method noiseDetail
+ * @param {Number} lod number of octaves to be used by the noise
+ * @param {Number} falloff falloff factor for each octave
+ * @example
+ * <div>
+ * <code>
+ *
+ * var noiseVal;
+ * var noiseScale=0.02;
+ *
+ * function setup() {
+ * createCanvas(100,100);
+ * }
+ *
+ * function draw() {
+ * background(0);
+ * for (var y = 0; y < height; y++) {
+ * for (var x = 0; x < width/2; x++) {
+ * noiseDetail(2,0.2);
+ * noiseVal = noise((mouseX+x) * noiseScale,
+ * (mouseY+y) * noiseScale);
+ * stroke(noiseVal*255);
+ * point(x,y);
+ * noiseDetail(8,0.65);
+ * noiseVal = noise((mouseX + x + width/2) * noiseScale,
+ * (mouseY + y) * noiseScale);
+ * stroke(noiseVal*255);
+ * point(x + width/2, y);
+ * }
+ * }
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.noiseDetail = function(lod, falloff) {
+ if (lod>0) { perlin_octaves=lod; }
+ if (falloff>0) { perlin_amp_falloff=falloff; }
+};
+
+/**
+ * Sets the seed value for <b>noise()</b>. By default, <b>noise()</b>
+ * produces different results each time the program is run. Set the
+ * <b>value</b> parameter to a constant to return the same pseudo-random
+ * numbers each time the software is run.
+ *
+ * @method noiseSeed
+ * @param {Number} seed the seed value
+ * @example
+ * <div>
+ * <code>var xoff = 0.0;
+ *
+ * function setup() {
+ * noiseSeed(99);
+ * stroke(0, 10);
+ * }
+ *
+ * function draw() {
+ * xoff = xoff + .01;
+ * var n = noise(xoff) * width;
+ * line(n, 0, n, height);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.noiseSeed = function(seed) {
+ // Linear Congruential Generator
+ // Variant of a Lehman Generator
+ var lcg = (function() {
+ // Set to values from http://en.wikipedia.org/wiki/Numerical_Recipes
+ // m is basically chosen to be large (as it is the max period)
+ // and for its relationships to a and c
+ var m = 4294967296,
+ // a - 1 should be divisible by m's prime factors
+ a = 1664525,
+ // c and m should be co-prime
+ c = 1013904223,
+ seed, z;
+ return {
+ setSeed : function(val) {
+ // pick a random seed if val is undefined or null
+ // the >>> 0 casts the seed to an unsigned 32-bit integer
+ z = seed = (val == null ? Math.random() * m : val) >>> 0;
+ },
+ getSeed : function() {
+ return seed;
+ },
+ rand : function() {
+ // define the recurrence relationship
+ z = (a * z + c) % m;
+ // return a float in [0, 1)
+ // if z = m then z / m = 0 therefore (z % m) / m < 1 always
+ return z / m;
+ }
+ };
+ }());
+
+ lcg.setSeed(seed);
+ perlin = new Array(PERLIN_SIZE + 1);
+ for (var i = 0; i < PERLIN_SIZE + 1; i++) {
+ perlin[i] = lcg.rand();
+ }
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],76:[function(_dereq_,module,exports){
+/**
+ * @module Math
+ * @submodule Math
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var polarGeometry = _dereq_('./polargeometry');
+var constants = _dereq_('../core/constants');
+
+/**
+ * A class to describe a two or three dimensional vector, specifically
+ * a Euclidean (also known as geometric) vector. A vector is an entity
+ * that has both magnitude and direction. The datatype, however, stores
+ * the components of the vector (x, y for 2D, and x, y, z for 3D). The magnitude
+ * and direction can be accessed via the methods mag() and heading().
+ * <br><br>
+ * In many of the p5.js examples, you will see p5.Vector used to describe a
+ * position, velocity, or acceleration. For example, if you consider a rectangle
+ * moving across the screen, at any given instant it has a position (a vector
+ * that points from the origin to its location), a velocity (the rate at which
+ * the object's position changes per time unit, expressed as a vector), and
+ * acceleration (the rate at which the object's velocity changes per time
+ * unit, expressed as a vector).
+ * <br><br>
+ * Since vectors represent groupings of values, we cannot simply use
+ * traditional addition/multiplication/etc. Instead, we'll need to do some
+ * "vector" math, which is made easy by the methods inside the p5.Vector class.
+ *
+ * @class p5.Vector
+ * @constructor
+ * @param {Number} [x] x component of the vector
+ * @param {Number} [y] y component of the vector
+ * @param {Number} [z] z component of the vector
+ * @example
+ * <div>
+ * <code>
+ * var v1 = createVector(40, 50);
+ * var v2 = createVector(40, 50);
+ *
+ * ellipse(v1.x, v1.y, 50, 50);
+ * ellipse(v2.x, v2.y, 50, 50);
+ * v1.add(v2);
+ * ellipse(v1.x, v1.y, 50, 50);
+ * </code>
+ * </div>
+ */
+p5.Vector = function() {
+ var x,y,z;
+ // This is how it comes in with createVector()
+ if(arguments[0] instanceof p5) {
+ // save reference to p5 if passed in
+ this.p5 = arguments[0];
+ x = arguments[1][0] || 0;
+ y = arguments[1][1] || 0;
+ z = arguments[1][2] || 0;
+ // This is what we'll get with new p5.Vector()
+ } else {
+ x = arguments[0] || 0;
+ y = arguments[1] || 0;
+ z = arguments[2] || 0;
+ }
+ /**
+ * The x component of the vector
+ * @property x
+ * @type {Number}
+ */
+ this.x = x;
+ /**
+ * The y component of the vector
+ * @property y
+ * @type {Number}
+ */
+ this.y = y;
+ /**
+ * The z component of the vector
+ * @property z
+ * @type {Number}
+ */
+ this.z = z;
+};
+
+/**
+ * Returns a string representation of a vector v by calling String(v)
+ * or v.toString(). This method is useful for logging vectors in the
+ * console.
+ * @method toString
+ * @example
+ * <div class = "norender"><code>
+ * function setup() {
+ * var v = createVector(20,30);
+ * print(String(v)); // prints "p5.Vector Object : [20, 30, 0]"
+ * }
+ * </div></code>
+ *
+ */
+p5.Vector.prototype.toString = function p5VectorToString() {
+ return 'p5.Vector Object : ['+ this.x +', '+ this.y +', '+ this.z + ']';
+};
+
+/**
+ * Sets the x, y, and z component of the vector using two or three separate
+ * variables, the data from a p5.Vector, or the values from a float array.
+ * @method set
+ *
+ * @param {Number|p5.Vector|Array} [x] the x component of the vector or a
+ * p5.Vector or an Array
+ * @param {Number} [y] the y component of the vector
+ * @param {Number} [z] the z component of the vector
+ * @example
+ * <div class="norender">
+ * <code>
+ * function setup() {
+ * var v = createVector(1, 2, 3);
+ * v.set(4,5,6); // Sets vector to [4, 5, 6]
+ *
+ * var v1 = createVector(0, 0, 0);
+ * var arr = [1, 2, 3];
+ * v1.set(arr); // Sets vector to [1, 2, 3]
+ * }
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.set = function (x, y, z) {
+ if (x instanceof p5.Vector) {
+ this.x = x.x || 0;
+ this.y = x.y || 0;
+ this.z = x.z || 0;
+ return this;
+ }
+ if (x instanceof Array) {
+ this.x = x[0] || 0;
+ this.y = x[1] || 0;
+ this.z = x[2] || 0;
+ return this;
+ }
+ this.x = x || 0;
+ this.y = y || 0;
+ this.z = z || 0;
+ return this;
+};
+
+/**
+ * Gets a copy of the vector, returns a p5.Vector object.
+ *
+ * @method copy
+ * @return {p5.Vector} the copy of the p5.Vector object
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v1 = createVector(1, 2, 3);
+ * var v2 = v.copy();
+ * print(v1.x == v2.x && v1.y == v2.y && v1.z == v2.z);
+ * // Prints "true"
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.copy = function () {
+ if (this.p5) {
+ return new p5.Vector(this.p5,[this.x, this.y, this.z]);
+ } else {
+ return new p5.Vector(this.x,this.y,this.z);
+ }
+};
+
+/**
+ * Adds x, y, and z components to a vector, adds one vector to another, or
+ * adds two independent vectors together. The version of the method that adds
+ * two vectors together is a static method and returns a p5.Vector, the others
+ * acts directly on the vector. See the examples for more context.
+ *
+ * @method add
+ * @chainable
+ * @param {Number|p5.Vector|Array} x the x component of the vector to be
+ * added or a p5.Vector or an Array
+ * @param {Number} [y] the y component of the vector to be
+ * added
+ * @param {Number} [z] the z component of the vector to be
+ * added
+ * @return {p5.Vector} the p5.Vector object.
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = createVector(1, 2, 3);
+ * v.add(4,5,6);
+ * // v's compnents are set to [5, 7, 9]
+ * </code>
+ * </div>
+ * <div class="norender">
+ * <code>
+ * // Static method
+ * var v1 = createVector(1, 2, 3);
+ * var v2 = createVector(2, 3, 4);
+ *
+ * var v3 = p5.Vector.add(v1, v2);
+ * // v3 has components [3, 5, 7]
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.add = function (x, y, z) {
+ if (x instanceof p5.Vector) {
+ this.x += x.x || 0;
+ this.y += x.y || 0;
+ this.z += x.z || 0;
+ return this;
+ }
+ if (x instanceof Array) {
+ this.x += x[0] || 0;
+ this.y += x[1] || 0;
+ this.z += x[2] || 0;
+ return this;
+ }
+ this.x += x || 0;
+ this.y += y || 0;
+ this.z += z || 0;
+ return this;
+};
+
+/**
+ * Subtracts x, y, and z components from a vector, subtracts one vector from
+ * another, or subtracts two independent vectors. The version of the method
+ * that subtracts two vectors is a static method and returns a p5.Vector, the
+ * other acts directly on the vector. See the examples for more context.
+ *
+ * @method sub
+ * @chainable
+ * @param {Number|p5.Vector|Array} x the x component of the vector or a
+ * p5.Vector or an Array
+ * @param {Number} [y] the y component of the vector
+ * @param {Number} [z] the z component of the vector
+ * @return {p5.Vector} p5.Vector object.
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = createVector(4, 5, 6);
+ * v.sub(1, 1, 1);
+ * // v's compnents are set to [3, 4, 5]
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * // Static method
+ * var v1 = createVector(2, 3, 4);
+ * var v2 = createVector(1, 2, 3);
+ *
+ * var v3 = p5.Vector.sub(v1, v2);
+ * // v3 has compnents [1, 1, 1]
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.sub = function (x, y, z) {
+ if (x instanceof p5.Vector) {
+ this.x -= x.x || 0;
+ this.y -= x.y || 0;
+ this.z -= x.z || 0;
+ return this;
+ }
+ if (x instanceof Array) {
+ this.x -= x[0] || 0;
+ this.y -= x[1] || 0;
+ this.z -= x[2] || 0;
+ return this;
+ }
+ this.x -= x || 0;
+ this.y -= y || 0;
+ this.z -= z || 0;
+ return this;
+};
+
+/**
+ * Multiply the vector by a scalar. The static version of this method
+ * creates a new p5.Vector while the non static version acts on the vector
+ * directly. See the examples for more context.
+ *
+ * @method mult
+ * @chainable
+ * @param {Number} n the number to multiply with the vector
+ * @return {p5.Vector} a reference to the p5.Vector object (allow chaining)
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = createVector(1, 2, 3);
+ * v.mult(2);
+ * // v's compnents are set to [2, 4, 6]
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * // Static method
+ * var v1 = createVector(1, 2, 3);
+ * var v2 = p5.Vector.mult(v1, 2);
+ * // v2 has compnents [2, 4, 6]
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.mult = function (n) {
+ this.x *= n || 0;
+ this.y *= n || 0;
+ this.z *= n || 0;
+ return this;
+};
+
+/**
+ * Divide the vector by a scalar. The static version of this method creates a
+ * new p5.Vector while the non static version acts on the vector directly.
+ * See the examples for more context.
+ *
+ * @method div
+ * @chainable
+ * @param {number} n the number to divide the vector by
+ * @return {p5.Vector} a reference to the p5.Vector object (allow chaining)
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = createVector(6, 4, 2);
+ * v.div(2); //v's compnents are set to [3, 2, 1]
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * // Static method
+ * var v1 = createVector(6, 4, 2);
+ * var v2 = p5.Vector.div(v, 2);
+ * // v2 has compnents [3, 2, 1]
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.div = function (n) {
+ this.x /= n;
+ this.y /= n;
+ this.z /= n;
+ return this;
+};
+
+/**
+ * Calculates the magnitude (length) of the vector and returns the result as
+ * a float (this is simply the equation sqrt(x*x + y*y + z*z).)
+ *
+ * @method mag
+ * @return {Number} magnitude of the vector
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = createVector(20.0, 30.0, 40.0);
+ * var m = v.mag(10);
+ * print(m); // Prints "53.85164807134504"
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.mag = function () {
+ return Math.sqrt(this.magSq());
+};
+
+/**
+ * Calculates the squared magnitude of the vector and returns the result
+ * as a float (this is simply the equation <em>(x*x + y*y + z*z)</em>.)
+ * Faster if the real length is not required in the
+ * case of comparing vectors, etc.
+ *
+ * @method magSq
+ * @return {number} squared magnitude of the vector
+ * @example
+ * <div class="norender">
+ * <code>
+ * // Static method
+ * var v1 = createVector(6, 4, 2);
+ * print(v1.magSq()); // Prints "56"
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.magSq = function () {
+ var x = this.x, y = this.y, z = this.z;
+ return (x * x + y * y + z * z);
+};
+
+/**
+ * Calculates the dot product of two vectors. The version of the method
+ * that computes the dot product of two independent vectors is a static
+ * method. See the examples for more context.
+ *
+ *
+ * @method dot
+ * @param {Number|p5.Vector} x x component of the vector or a p5.Vector
+ * @param {Number} [y] y component of the vector
+ * @param {Number} [z] z component of the vector
+ * @return {Number} the dot product
+ *
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v1 = createVector(1, 2, 3);
+ * var v2 = createVector(2, 3, 4);
+ *
+ * print(v1.dot(v2)); // Prints "20"
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * //Static method
+ * var v1 = createVector(1, 2, 3);
+ * var v2 = createVector(3, 2, 1);
+ * print (p5.Vector.dot(v1, v2)); // Prints "10"
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.dot = function (x, y, z) {
+ if (x instanceof p5.Vector) {
+ return this.dot(x.x, x.y, x.z);
+ }
+ return this.x * (x || 0) +
+ this.y * (y || 0) +
+ this.z * (z || 0);
+};
+
+/**
+ * Calculates and returns a vector composed of the cross product between
+ * two vectors. Both the static and non static methods return a new p5.Vector.
+ * See the examples for more context.
+ *
+ * @method cross
+ * @param {p5.Vector} v p5.Vector to be crossed
+ * @return {p5.Vector} p5.Vector composed of cross product
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v1 = createVector(1, 2, 3);
+ * var v2 = createVector(1, 2, 3);
+ *
+ * v1.cross(v2); // v's components are [0, 0, 0]
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * // Static method
+ * var v1 = createVector(1, 0, 0);
+ * var v2 = createVector(0, 1, 0);
+ *
+ * var crossProduct = p5.Vector.cross(v1, v2);
+ * // crossProduct has components [0, 0, 1]
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.cross = function (v) {
+ var x = this.y * v.z - this.z * v.y;
+ var y = this.z * v.x - this.x * v.z;
+ var z = this.x * v.y - this.y * v.x;
+ if (this.p5) {
+ return new p5.Vector(this.p5,[x,y,z]);
+ } else {
+ return new p5.Vector(x,y,z);
+ }
+};
+
+/**
+ * Calculates the Euclidean distance between two points (considering a
+ * point as a vector object).
+ *
+ * @method dist
+ * @param {p5.Vector} v the x, y, and z coordinates of a p5.Vector
+ * @return {Number} the distance
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v1 = createVector(1, 0, 0);
+ * var v2 = createVector(0, 1, 0);
+ *
+ * var distance = v1.dist(v2); // distance is 1.4142...
+ * </code>
+ * </div>
+ * <div class="norender">
+ * <code>
+ * // Static method
+ * var v1 = createVector(1, 0, 0);
+ * var v2 = createVector(0, 1, 0);
+ *
+ * var distance = p5.Vector.dist(v1,v2);
+ * // distance is 1.4142...
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.dist = function (v) {
+ var d = v.copy().sub(this);
+ return d.mag();
+};
+
+/**
+ * Normalize the vector to length 1 (make it a unit vector).
+ *
+ * @method normalize
+ * @return {p5.Vector} normalized p5.Vector
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = createVector(10, 20, 2);
+ * // v has compnents [10.0, 20.0, 2.0]
+ * v.normalize();
+ * // v's compnents are set to
+ * // [0.4454354, 0.8908708, 0.089087084]
+ * </code>
+ * </div>
+ *
+ */
+p5.Vector.prototype.normalize = function () {
+ return this.div(this.mag());
+};
+
+/**
+ * Limit the magnitude of this vector to the value used for the <b>max</b>
+ * parameter.
+ *
+ * @method limit
+ * @param {Number} max the maximum magnitude for the vector
+ * @return {p5.Vector} the modified p5.Vector
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = createVector(10, 20, 2);
+ * // v has compnents [10.0, 20.0, 2.0]
+ * v.limit(5);
+ * // v's compnents are set to
+ * // [2.2271771, 4.4543543, 0.4454354]
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.limit = function (l) {
+ var mSq = this.magSq();
+ if(mSq > l*l) {
+ this.div(Math.sqrt(mSq)); //normalize it
+ this.mult(l);
+ }
+ return this;
+};
+
+/**
+ * Set the magnitude of this vector to the value used for the <b>len</b>
+ * parameter.
+ *
+ * @method setMag
+ * @param {number} len the new length for this vector
+ * @return {p5.Vector} the modified p5.Vector
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v1 = createVector(10, 20, 2);
+ * // v has compnents [10.0, 20.0, 2.0]
+ * v1.setMag(10);
+ * // v's compnents are set to [6.0, 8.0, 0.0]
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.setMag = function (n) {
+ return this.normalize().mult(n);
+};
+
+/**
+ * Calculate the angle of rotation for this vector (only 2D vectors)
+ *
+ * @method heading
+ * @return {Number} the angle of rotation
+ * @example
+ * <div class = "norender"><code>
+ * function setup() {
+ * var v1 = createVector(30,50);
+ * print(v1.heading()); // 1.0303768265243125
+ *
+ * var v1 = createVector(40,50);
+ * print(v1.heading()); // 0.8960553845713439
+ *
+ * var v1 = createVector(30,70);
+ * print(v1.heading()); // 1.1659045405098132
+ * }
+ * </div></code>
+ */
+p5.Vector.prototype.heading = function () {
+ var h = Math.atan2(this.y, this.x);
+ if (this.p5) {
+ if (this.p5._angleMode === constants.RADIANS) {
+ return h;
+ } else {
+ return polarGeometry.radiansToDegrees(h);
+ }
+ } else {
+ return h;
+ }
+};
+
+/**
+ * Rotate the vector by an angle (only 2D vectors), magnitude remains the
+ * same
+ *
+ * @method rotate
+ * @param {number} angle the angle of rotation
+ * @return {p5.Vector} the modified p5.Vector
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = createVector(10.0, 20.0);
+ * // v has compnents [10.0, 20.0, 0.0]
+ * v.rotate(HALF_PI);
+ * // v's compnents are set to [-20.0, 9.999999, 0.0]
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.rotate = function (a) {
+ if (this.p5) {
+ if (this.p5._angleMode === constants.DEGREES) {
+ a = polarGeometry.degreesToRadians(a);
+ }
+ }
+ var newHeading = this.heading() + a;
+ var mag = this.mag();
+ this.x = Math.cos(newHeading) * mag;
+ this.y = Math.sin(newHeading) * mag;
+ return this;
+};
+
+/**
+ * Linear interpolate the vector to another vector
+ *
+ * @method lerp
+ * @param {p5.Vector} x the x component or the p5.Vector to lerp to
+ * @param {p5.Vector} [y] y the y component
+ * @param {p5.Vector} [z] z the z component
+ * @param {Number} amt the amount of interpolation; some value between 0.0
+ * (old vector) and 1.0 (new vector). 0.1 is very near
+ * the new vector. 0.5 is halfway in between.
+ * @return {p5.Vector} the modified p5.Vector
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = createVector(1, 1, 0);
+ *
+ * v.lerp(3, 3, 0, 0.5); // v now has components [2,2,0]
+ * </code>
+ * </div>
+ *
+ * <div class="norender">
+ * <code>
+ * var v1 = createVector(0, 0, 0);
+ * var v2 = createVector(100, 100, 0);
+ *
+ * var v3 = p5.Vector.lerp(v1, v2, 0.5);
+ * // v3 has components [50,50,0]
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.lerp = function (x, y, z, amt) {
+ if (x instanceof p5.Vector) {
+ return this.lerp(x.x, x.y, x.z, y);
+ }
+ this.x += (x - this.x) * amt || 0;
+ this.y += (y - this.y) * amt || 0;
+ this.z += (z - this.z) * amt || 0;
+ return this;
+};
+
+/**
+ * Return a representation of this vector as a float array. This is only
+ * for temporary use. If used in any other fashion, the contents should be
+ * copied by using the <b>p5.Vector.copy()</b> method to copy into your own
+ * array.
+ *
+ * @method array
+ * @return {Array} an Array with the 3 values
+ * @example
+ * <div class = "norender"><code>
+ * function setup() {
+ * var v = createVector(20,30);
+ * print(v.array()); // Prints : Array [20, 30, 0]
+ * }
+ * </div></code>
+ * <div class="norender">
+ * <code>
+ * var v = createVector(10.0, 20.0, 30.0);
+ * var f = v.array();
+ * print(f[0]); // Prints "10.0"
+ * print(f[1]); // Prints "20.0"
+ * print(f[2]); // Prints "30.0"
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.array = function () {
+ return [this.x || 0, this.y || 0, this.z || 0];
+};
+
+/**
+ * Equality check against a p5.Vector
+ *
+ * @method equals
+ * @param {Number|p5.Vector|Array} [x] the x component of the vector or a
+ * p5.Vector or an Array
+ * @param {Number} [y] the y component of the vector
+ * @param {Number} [z] the z component of the vector
+ * @return {Boolean} whether the vectors are equals
+ * @example
+ * <div class = "norender"><code>
+ * v1 = createVector(5,10,20);
+ * v2 = createVector(5,10,20);
+ * v3 = createVector(13,10,19);
+ *
+ * print(v1.equals(v2.x,v2.y,v2.z)); // true
+ * print(v1.equals(v3.x,v3.y,v3.z)); // false
+ * </div></code>
+ * <div class="norender">
+ * <code>
+ * var v1 = createVector(10.0, 20.0, 30.0);
+ * var v2 = createVector(10.0, 20.0, 30.0);
+ * var v3 = createVector(0.0, 0.0, 0.0);
+ * print (v1.equals(v2)) // true
+ * print (v1.equals(v3)) // false
+ * </code>
+ * </div>
+ */
+p5.Vector.prototype.equals = function (x, y, z) {
+ var a, b, c;
+ if (x instanceof p5.Vector) {
+ a = x.x || 0;
+ b = x.y || 0;
+ c = x.z || 0;
+ } else if (x instanceof Array) {
+ a = x[0] || 0;
+ b = x[1] || 0;
+ c = x[2] || 0;
+ } else {
+ a = x || 0;
+ b = y || 0;
+ c = z || 0;
+ }
+ return this.x === a && this.y === b && this.z === c;
+};
+
+
+// Static Methods
+
+
+/**
+ * Make a new 2D unit vector from an angle
+ *
+ * @method fromAngle
+ * @static
+ * @param {Number} angle the desired angle
+ * @return {p5.Vector} the new p5.Vector object
+ * @example
+ * <div>
+ * <code>
+ * function draw() {
+ * background (200);
+ *
+ * // Create a variable, proportional to the mouseX,
+ * // varying from 0-360, to represent an angle in degrees.
+ * angleMode(DEGREES);
+ * var myDegrees = map(mouseX, 0,width, 0,360);
+ *
+ * // Display that variable in an onscreen text.
+ * // (Note the nfc() function to truncate additional decimal places,
+ * // and the "\xB0" character for the degree symbol.)
+ * var readout = "angle = " + nfc(myDegrees,1,1) + "\xB0"
+ * noStroke();
+ * fill (0);
+ * text (readout, 5, 15);
+ *
+ * // Create a p5.Vector using the fromAngle function,
+ * // and extract its x and y components.
+ * var v = p5.Vector.fromAngle(radians(myDegrees));
+ * var vx = v.x;
+ * var vy = v.y;
+ *
+ * push();
+ * translate (width/2, height/2);
+ * noFill();
+ * stroke (150);
+ * line (0,0, 30,0);
+ * stroke (0);
+ * line (0,0, 30*vx, 30*vy);
+ * pop()
+ * }
+ * </code>
+ * </div>
+ */
+p5.Vector.fromAngle = function(angle) {
+ if (this.p5) {
+ if (this.p5._angleMode === constants.DEGREES) {
+ angle = polarGeometry.degreesToRadians(angle);
+ }
+ }
+ if (this.p5) {
+ return new p5.Vector(this.p5,[Math.cos(angle),Math.sin(angle),0]);
+ } else {
+ return new p5.Vector(Math.cos(angle),Math.sin(angle),0);
+ }
+};
+
+/**
+ * Make a new 2D unit vector from a random angle
+ *
+ * @method random2D
+ * @static
+ * @return {p5.Vector} the new p5.Vector object
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = p5.Vector.random2D();
+ * // May make v's attributes something like:
+ * // [0.61554617, -0.51195765, 0.0] or
+ * // [-0.4695841, -0.14366731, 0.0] or
+ * // [0.6091097, -0.22805278, 0.0]
+ * </code>
+ * </div>
+ */
+p5.Vector.random2D = function () {
+ var angle;
+ // A lot of nonsense to determine if we know about a
+ // p5 sketch and whether we should make a random angle in degrees or radians
+ if (this.p5) {
+ if (this.p5._angleMode === constants.DEGREES) {
+ angle = this.p5.random(360);
+ } else {
+ angle = this.p5.random(constants.TWO_PI);
+ }
+ } else {
+ angle = Math.random()*Math.PI*2;
+ }
+ return this.fromAngle(angle);
+};
+
+/**
+ * Make a new random 3D unit vector.
+ *
+ * @method random3D
+ * @static
+ * @return {p5.Vector} the new p5.Vector object
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v = p5.Vector.random3D();
+ * // May make v's attributes something like:
+ * // [0.61554617, -0.51195765, 0.599168] or
+ * // [-0.4695841, -0.14366731, -0.8711202] or
+ * // [0.6091097, -0.22805278, -0.7595902]
+ * </code>
+ * </div>
+ */
+p5.Vector.random3D = function () {
+ var angle,vz;
+ // If we know about p5
+ if (this.p5) {
+ angle = this.p5.random(0,constants.TWO_PI);
+ vz = this.p5.random(-1,1);
+ } else {
+ angle = Math.random()*Math.PI*2;
+ vz = Math.random()*2-1;
+ }
+ var vx = Math.sqrt(1-vz*vz)*Math.cos(angle);
+ var vy = Math.sqrt(1-vz*vz)*Math.sin(angle);
+ if (this.p5) {
+ return new p5.Vector(this.p5,[vx,vy,vz]);
+ } else {
+ return new p5.Vector(vx,vy,vz);
+ }
+};
+
+
+/**
+ * Adds two vectors together and returns a new one.
+ *
+ * @static
+ * @param {p5.Vector} v1 a p5.Vector to add
+ * @param {p5.Vector} v2 a p5.Vector to add
+ * @param {p5.Vector} target if undefined a new vector will be created
+ * @return {p5.Vector} the resulting p5.Vector
+ *
+ */
+
+p5.Vector.add = function (v1, v2, target) {
+ if (!target) {
+ target = v1.copy();
+ } else {
+ target.set(v1);
+ }
+ target.add(v2);
+ return target;
+};
+
+/**
+ * Subtracts one p5.Vector from another and returns a new one. The second
+ * vector (v2) is subtracted from the first (v1), resulting in v1-v2.
+ *
+ * @static
+ * @param {p5.Vector} v1 a p5.Vector to subtract from
+ * @param {p5.Vector} v2 a p5.Vector to subtract
+ * @param {p5.Vector} target if undefined a new vector will be created
+ * @return {p5.Vector} the resulting p5.Vector
+ */
+
+p5.Vector.sub = function (v1, v2, target) {
+ if (!target) {
+ target = v1.copy();
+ } else {
+ target.set(v1);
+ }
+ target.sub(v2);
+ return target;
+};
+
+
+/**
+ * Multiplies a vector by a scalar and returns a new vector.
+ *
+ * @static
+ * @param {p5.Vector} v the p5.Vector to multiply
+ * @param {Number} n the scalar
+ * @param {p5.Vector} target if undefined a new vector will be created
+ * @return {p5.Vector} the resulting new p5.Vector
+ */
+p5.Vector.mult = function (v, n, target) {
+ if (!target) {
+ target = v.copy();
+ } else {
+ target.set(v);
+ }
+ target.mult(n);
+ return target;
+};
+
+/**
+ * Divides a vector by a scalar and returns a new vector.
+ *
+ * @static
+ * @param {p5.Vector} v the p5.Vector to divide
+ * @param {Number} n the scalar
+ * @param {p5.Vector} target if undefined a new vector will be created
+ * @return {p5.Vector} the resulting new p5.Vector
+ */
+p5.Vector.div = function (v, n, target) {
+ if (!target) {
+ target = v.copy();
+ } else {
+ target.set(v);
+ }
+ target.div(n);
+ return target;
+};
+
+
+/**
+ * Calculates the dot product of two vectors.
+ *
+ * @static
+ * @param {p5.Vector} v1 the first p5.Vector
+ * @param {p5.Vector} v2 the second p5.Vector
+ * @return {Number} the dot product
+ */
+p5.Vector.dot = function (v1, v2) {
+ return v1.dot(v2);
+};
+
+/**
+ * Calculates the cross product of two vectors.
+ *
+ * @static
+ * @param {p5.Vector} v1 the first p5.Vector
+ * @param {p5.Vector} v2 the second p5.Vector
+ * @return {Number} the cross product
+ */
+p5.Vector.cross = function (v1, v2) {
+ return v1.cross(v2);
+};
+
+/**
+ * Calculates the Euclidean distance between two points (considering a
+ * point as a vector object).
+ *
+ * @static
+ * @param {p5.Vector} v1 the first p5.Vector
+ * @param {p5.Vector} v2 the second p5.Vector
+ * @return {Number} the distance
+ */
+p5.Vector.dist = function (v1,v2) {
+ return v1.dist(v2);
+};
+
+/**
+ * Linear interpolate a vector to another vector and return the result as a
+ * new vector.
+ *
+ * @static
+ * @param {p5.Vector} v1 a starting p5.Vector
+ * @param {p5.Vector} v2 the p5.Vector to lerp to
+ * @param {Number} the amount of interpolation; some value between 0.0
+ * (old vector) and 1.0 (new vector). 0.1 is very near
+ * the new vector. 0.5 is halfway in between.
+ */
+p5.Vector.lerp = function (v1, v2, amt, target) {
+ if (!target) {
+ target = v1.copy();
+ } else {
+ target.set(v1);
+ }
+ target.lerp(v2, amt);
+ return target;
+};
+
+/**
+ * Calculates and returns the angle (in radians) between two vectors.
+ * @method angleBetween
+ * @static
+ * @param {p5.Vector} v1 the x, y, and z components of a p5.Vector
+ * @param {p5.Vector} v2 the x, y, and z components of a p5.Vector
+ * @return {Number} the angle between (in radians)
+ * @example
+ * <div class="norender">
+ * <code>
+ * var v1 = createVector(1, 0, 0);
+ * var v2 = createVector(0, 1, 0);
+ *
+ * var angle = p5.Vector.angleBetween(v1, v2);
+ * // angle is PI/2
+ * </code>
+ * </div>
+ */
+p5.Vector.angleBetween = function (v1, v2) {
+ var angle = Math.acos(v1.dot(v2) / (v1.mag() * v2.mag()));
+ if (this.p5) {
+ if (this.p5._angleMode === constants.DEGREES) {
+ angle = polarGeometry.radiansToDegrees(angle);
+ }
+ }
+ return angle;
+};
+
+module.exports = p5.Vector;
+
+},{"../core/constants":47,"../core/core":48,"./polargeometry":77}],77:[function(_dereq_,module,exports){
+
+module.exports = {
+
+ degreesToRadians: function(x) {
+ return 2 * Math.PI * x / 360;
+ },
+
+ radiansToDegrees: function(x) {
+ return 360 * x / (2 * Math.PI);
+ }
+
+};
+
+},{}],78:[function(_dereq_,module,exports){
+/**
+ * @module Math
+ * @submodule Random
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+var seeded = false;
+
+// Linear Congruential Generator
+// Variant of a Lehman Generator
+var lcg = (function() {
+ // Set to values from http://en.wikipedia.org/wiki/Numerical_Recipes
+ // m is basically chosen to be large (as it is the max period)
+ // and for its relationships to a and c
+ var m = 4294967296,
+ // a - 1 should be divisible by m's prime factors
+ a = 1664525,
+ // c and m should be co-prime
+ c = 1013904223,
+ seed, z;
+ return {
+ setSeed : function(val) {
+ // pick a random seed if val is undefined or null
+ // the >>> 0 casts the seed to an unsigned 32-bit integer
+ z = seed = (val == null ? Math.random() * m : val) >>> 0;
+ },
+ getSeed : function() {
+ return seed;
+ },
+ rand : function() {
+ // define the recurrence relationship
+ z = (a * z + c) % m;
+ // return a float in [0, 1)
+ // if z = m then z / m = 0 therefore (z % m) / m < 1 always
+ return z / m;
+ }
+ };
+}());
+
+/**
+ * Sets the seed value for random().
+ *
+ * By default, random() produces different results each time the program
+ * is run. Set the seed parameter to a constant to return the same
+ * pseudo-random numbers each time the software is run.
+ *
+ * @method randomSeed
+ * @param {Number} seed the seed value
+ * @example
+ * <div>
+ * <code>
+ * randomSeed(99);
+ * for (var i=0; i < 100; i++) {
+ * var r = random(0, 255);
+ * stroke(r);
+ * line(i, 0, i, 100);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.randomSeed = function(seed) {
+ lcg.setSeed(seed);
+ seeded = true;
+};
+
+/**
+ * Return a random number.
+ *
+ * Takes either 0, 1 or 2 arguments.
+ * If no argument is given, returns a random number between 0 and 1.
+ * If one argument is given, returns a random number between 0 and the number.
+ * If two arguments are given, returns a random number between them,
+ * inclusive.
+ *
+ * @method random
+ * @param {Number} min the lower bound
+ * @param {Number} max the upper bound
+ * @return {Number} the random number
+ * @example
+ * <div>
+ * <code>
+ * for (var i = 0; i < 100; i++) {
+ * var r = random(50);
+ * stroke(r*5);
+ * line(50, i, 50+r, i);
+ * }
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * for (var i = 0; i < 100; i++) {
+ * var r = random(-50, 50);
+ * line(50,i,50+r,i);
+ * }
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * // Get a random element from an array
+ * var words = [ "apple", "bear", "cat", "dog" ];
+ * var index = floor(random(words.length)); // Convert to integer
+ * text(words[index],10,50); // Displays one of the four words
+ * </code>
+ * </div>
+ */
+p5.prototype.random = function (min, max) {
+
+ var rand;
+
+ if (seeded) {
+ rand = lcg.rand();
+ } else {
+ rand = Math.random();
+ }
+
+ if (arguments.length === 0) {
+ return rand;
+ } else
+ if (arguments.length === 1) {
+ return rand * min;
+ } else {
+ if (min > max) {
+ var tmp = min;
+ min = max;
+ max = tmp;
+ }
+
+ return rand * (max-min) + min;
+ }
+};
+
+
+/**
+ *
+ * Returns a random number fitting a Gaussian, or
+ * normal, distribution. There is theoretically no minimum or maximum
+ * value that randomGaussian() might return. Rather, there is
+ * just a very low probability that values far from the mean will be
+ * returned; and a higher probability that numbers near the mean will
+ * be returned.
+ * <br><br>
+ * Takes either 0, 1 or 2 arguments.<br>
+ * If no args, returns a mean of 0 and standard deviation of 1.<br>
+ * If one arg, that arg is the mean (standard deviation is 1).<br>
+ * If two args, first is mean, second is standard deviation.
+ *
+ * @method randomGaussian
+ * @param {Number} mean the mean
+ * @param {Number} sd the standard deviation
+ * @return {Number} the random number
+ * @example
+ * <div>
+ * <code>for (var y = 0; y < 100; y++) {
+ * var x = randomGaussian(50,15);
+ * line(50, y, x, y);
+ *}
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ *var distribution = new Array(360);
+ *
+ *function setup() {
+ * createCanvas(100, 100);
+ * for (var i = 0; i < distribution.length; i++) {
+ * distribution[i] = floor(randomGaussian(0,15));
+ * }
+ *}
+ *
+ *function draw() {
+ * background(204);
+ *
+ * translate(width/2, width/2);
+ *
+ * for (var i = 0; i < distribution.length; i++) {
+ * rotate(TWO_PI/distribution.length);
+ * stroke(0);
+ * var dist = abs(distribution[i]);
+ * line(0, 0, dist, 0);
+ * }
+ *}
+ * </code>
+ * </div>
+ */
+var y2;
+var previous = false;
+p5.prototype.randomGaussian = function(mean, sd) {
+ var y1,x1,x2,w;
+ if (previous) {
+ y1 = y2;
+ previous = false;
+ } else {
+ do {
+ x1 = this.random(2) - 1;
+ x2 = this.random(2) - 1;
+ w = x1 * x1 + x2 * x2;
+ } while (w >= 1);
+ w = Math.sqrt((-2 * Math.log(w))/w);
+ y1 = x1 * w;
+ y2 = x2 * w;
+ previous = true;
+ }
+
+ var m = mean || 0;
+ var s = sd || 1;
+ return y1*s + m;
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],79:[function(_dereq_,module,exports){
+/**
+ * @module Math
+ * @submodule Trigonometry
+ * @for p5
+ * @requires core
+ * @requires polargeometry
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var polarGeometry = _dereq_('./polargeometry');
+var constants = _dereq_('../core/constants');
+
+p5.prototype._angleMode = constants.RADIANS;
+
+/**
+ * The inverse of cos(), returns the arc cosine of a value. This function
+ * expects the values in the range of -1 to 1 and values are returned in
+ * the range 0 to PI (3.1415927).
+ *
+ * @method acos
+ * @param {Number} value the value whose arc cosine is to be returned
+ * @return {Number} the arc cosine of the given value
+ *
+ * @example
+ * <div class= “norender">
+ * <code>
+ * var a = PI;
+ * var c = cos(a);
+ * var ac = acos(c);
+ * // Prints: "3.1415927 : -1.0 : 3.1415927"
+ * println(a + " : " + c + " : " + ac);
+ * </code>
+ * </div>
+ *
+ * <div class= “norender">
+ * <code>
+ * var a = PI + PI/4.0;
+ * var c = cos(a);
+ * var ac = acos(c);
+ * // Prints: "3.926991 : -0.70710665 : 2.3561943"
+ * println(a + " : " + c + " : " + ac);
+ * </code>
+ * </div>
+ */
+p5.prototype.acos = function(ratio) {
+ if (this._angleMode === constants.RADIANS) {
+ return Math.acos(ratio);
+ } else {
+ return polarGeometry.radiansToDegrees(Math.acos(ratio));
+ }
+};
+
+/**
+ * The inverse of sin(), returns the arc sine of a value. This function
+ * expects the values in the range of -1 to 1 and values are returned
+ * in the range -PI/2 to PI/2.
+ *
+ * @method asin
+ * @param {Number} value the value whose arc sine is to be returned
+ * @return {Number} the arc sine of the given value
+ *
+ * @example
+ * <div class= “norender">
+ * <code>
+ * var a = PI + PI/3;
+ * var s = sin(a);
+ * var as = asin(s);
+ * // Prints: "1.0471976 : 0.86602545 : 1.0471976"
+ * println(a + " : " + s + " : " + as);
+ * </code>
+ * </div>
+ *
+ * <div class= “norender">
+ * <code>
+ * var a = PI + PI/3.0;
+ * var s = sin(a);
+ * var as = asin(s);
+ * // Prints: "4.1887903 : -0.86602545 : -1.0471976"
+ * println(a + " : " + s + " : " + as);
+ * </code>
+ * </div>
+ *
+ */
+p5.prototype.asin = function(ratio) {
+ if (this._angleMode === constants.RADIANS) {
+ return Math.asin(ratio);
+ } else {
+ return polarGeometry.radiansToDegrees(Math.asin(ratio));
+ }
+};
+
+/**
+ * The inverse of tan(), returns the arc tangent of a value. This function
+ * expects the values in the range of -Infinity to Infinity (exclusive) and
+ * values are returned in the range -PI/2 to PI/2.
+ *
+ * @method atan
+ * @param {Number} value the value whose arc tangent is to be returned
+ * @return {Number} the arc tangent of the given value
+ *
+ * @example
+ * <div class= “norender">
+ * <code>
+ * var a = PI + PI/3;
+ * var t = tan(a);
+ * var at = atan(t);
+ * // Prints: "1.0471976 : 1.7320509 : 1.0471976"
+ * println(a + " : " + t + " : " + at);
+ * </code>
+ * </div>
+ *
+ * <div class= “norender">
+ * <code>
+ * var a = PI + PI/3.0;
+ * var t = tan(a);
+ * var at = atan(t);
+ * // Prints: "4.1887903 : 1.7320513 : 1.0471977"
+ * println(a + " : " + t + " : " + at);
+ * </code>
+ * </div>
+ *
+ */
+p5.prototype.atan = function(ratio) {
+ if (this._angleMode === constants.RADIANS) {
+ return Math.atan(ratio);
+ } else {
+ return polarGeometry.radiansToDegrees(Math.atan(ratio));
+ }
+};
+
+/**
+ * Calculates the angle (in radians) from a specified point to the coordinate
+ * origin as measured from the positive x-axis. Values are returned as a
+ * float in the range from PI to -PI. The atan2() function is most often used
+ * for orienting geometry to the position of the cursor.
+ * <br><br>
+ * Note: The y-coordinate of the point is the first parameter, and the
+ * x-coordinate is the second parameter, due the the structure of calculating
+ * the tangent.
+ *
+ * @method atan2
+ * @param {Number} y y-coordinate of the point
+ * @param {Number} x x-coordinate of the point
+ * @return {Number} the arc tangent of the given point
+ *
+ * @example
+ * <div>
+ * <code>
+ * function draw() {
+ * background(204);
+ * translate(width/2, height/2);
+ * var a = atan2(mouseY-height/2, mouseX-width/2);
+ * rotate(a);
+ * rect(-30, -5, 60, 10);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.atan2 = function (y, x) {
+ if (this._angleMode === constants.RADIANS) {
+ return Math.atan2(y, x);
+ } else {
+ return polarGeometry.radiansToDegrees(Math.atan2(y, x));
+ }
+};
+
+/**
+ * Calculates the cosine of an angle. This function takes into account the
+ * current angleMode. Values are returned in the range -1 to 1.
+ *
+ * @method cos
+ * @param {Number} angle the angle
+ * @return {Number} the cosine of the angle
+ *
+ * @example
+ * <div>
+ * <code>
+ * var a = 0.0;
+ * var inc = TWO_PI/25.0;
+ * for (var i = 0; i < 25; i++) {
+ * line(i*4, 50, i*4, 50+cos(a)*40.0);
+ * a = a + inc;
+ * }
+ * </code>
+ * </div>
+ *
+ */
+p5.prototype.cos = function(angle) {
+ if (this._angleMode === constants.RADIANS) {
+ return Math.cos(angle);
+ } else {
+ return Math.cos(this.radians(angle));
+ }
+};
+
+/**
+ * Calculates the sine of an angle. This function takes into account the
+ * current angleMode. Values are returned in the range -1 to 1.
+ *
+ * @method sin
+ * @param {Number} angle the angle
+ * @return {Number} the sine of the angle
+ *
+ * @example
+ * <div>
+ * <code>
+ * var a = 0.0;
+ * var inc = TWO_PI/25.0;
+ * for (var i = 0; i < 25; i++) {
+ * line(i*4, 50, i*4, 50+sin(a)*40.0);
+ * a = a + inc;
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.sin = function(angle) {
+ if (this._angleMode === constants.RADIANS) {
+ return Math.sin(angle);
+ } else {
+ return Math.sin(this.radians(angle));
+ }
+};
+
+/**
+ * Calculates the tangent of an angle. This function takes into account
+ * the current angleMode. Values are returned in the range -1 to 1.
+ *
+ * @method tan
+ * @param {Number} angle the angle
+ * @return {Number} the tangent of the angle
+ *
+ * @example
+ * <div>
+ * <code>
+ * var a = 0.0;
+ * var inc = TWO_PI/50.0;
+ * for (var i = 0; i < 100; i = i+2) {
+ * line(i, 50, i, 50+tan(a)*2.0);
+ * a = a + inc;
+ * }
+ * </code>
+ * </div>
+ *
+ */
+p5.prototype.tan = function(angle) {
+ if (this._angleMode === constants.RADIANS) {
+ return Math.tan(angle);
+ } else {
+ return Math.tan(this.radians(angle));
+ }
+};
+
+/**
+ * Converts a radian measurement to its corresponding value in degrees.
+ * Radians and degrees are two ways of measuring the same thing. There are
+ * 360 degrees in a circle and 2*PI radians in a circle. For example,
+ * 90° = PI/2 = 1.5707964.
+ *
+ * @method degrees
+ * @param {Number} radians the radians value to convert to degrees
+ * @return {Number} the converted angle
+ *
+ *
+ * @example
+ * <div class= “norender">
+ * <code>
+ * var rad = PI/4;
+ * var deg = degrees(rad);
+ * println(rad + " radians is " + deg + " degrees");
+ * // Prints: 45 degrees is 0.7853981633974483 radians
+ * </code>
+ * </div>
+ *
+ */
+p5.prototype.degrees = function(angle) {
+ return polarGeometry.radiansToDegrees(angle);
+};
+
+/**
+ * Converts a degree measurement to its corresponding value in radians.
+ * Radians and degrees are two ways of measuring the same thing. There are
+ * 360 degrees in a circle and 2*PI radians in a circle. For example,
+ * 90° = PI/2 = 1.5707964.
+ *
+ * @method radians
+ * @param {Number} degrees the degree value to convert to radians
+ * @return {Number} the converted angle
+ *
+ * @example
+ * <div class= “norender">
+ * <code>
+ * var deg = 45.0;
+ * var rad = radians(deg);
+ * println(deg + " degrees is " + rad + " radians");
+ * // Prints: 45 degrees is 0.7853981633974483 radians
+ * </code>
+ * </div>
+ */
+p5.prototype.radians = function(angle) {
+ return polarGeometry.degreesToRadians(angle);
+};
+
+/**
+ * Sets the current mode of p5 to given mode. Default mode is RADIANS.
+ *
+ * @method angleMode
+ * @param {Number/Constant} mode either RADIANS or DEGREES
+ *
+ * @example
+ * <div>
+ * <code>
+ * function draw(){
+ * background(204);
+ * angleMode(DEGREES); // Change the mode to DEGREES
+ * var a = atan2(mouseY-height/2, mouseX-width/2);
+ * translate(width/2, height/2);
+ * push();
+ * rotate(a);
+ * rect(-20, -5, 40, 10); // Larger rectangle is rotating in degrees
+ * pop();
+ * angleMode(RADIANS); // Change the mode to RADIANS
+ * rotate(a); // var a stays the same
+ * rect(-40, -5, 20, 10); // Smaller rectangle is rotating in radians
+ * }
+ * </code>
+ * </div>
+ *
+ */
+p5.prototype.angleMode = function(mode) {
+ if (mode === constants.DEGREES || mode === constants.RADIANS) {
+ this._angleMode = mode;
+ }
+};
+
+module.exports = p5;
+
+},{"../core/constants":47,"../core/core":48,"./polargeometry":77}],80:[function(_dereq_,module,exports){
+/**
+ * @module Typography
+ * @submodule Attributes
+ * @for p5
+ * @requires core
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * Sets the current alignment for drawing text. The parameters LEFT, CENTER,
+ * and RIGHT set the alignment of text in relation to the values for
+ * the x and y parameters of the text() function.
+ *
+ * @method textAlign
+ * @param {Number/Constant} horizAlign horizontal alignment, either LEFT,
+ * CENTER, or RIGHT
+ * @param {Number/Constant} vertAlign vertical alignment, either TOP,
+ * BOTTOM, CENTER, or BASELINE
+ * @return {Number}
+ * @example
+ * <div>
+ * <code>
+ * textSize(16);
+ * textAlign(RIGHT);
+ * text("ABCD", 50, 30);
+ * textAlign(CENTER);
+ * text("EFGH", 50, 50);
+ * textAlign(LEFT);
+ * text("IJKL", 50, 70);
+ * </code>
+ * </div>
+ */
+p5.prototype.textAlign = function(horizAlign, vertAlign) {
+ return this._renderer.textAlign.apply(this._renderer, arguments);
+};
+
+/**
+ * Sets/gets the spacing, in pixels, between lines of text. This
+ * setting will be used in all subsequent calls to the text() function.
+ *
+ * @method textLeading
+ * @param {Number} leading the size in pixels for spacing between lines
+ * @return {Object|Number}
+ * @example
+ * <div>
+ * <code>
+ * // Text to display. The "\n" is a "new line" character
+ * lines = "L1\nL2\nL3";
+ * textSize(12);
+ *
+ * textLeading(10); // Set leading to 10
+ * text(lines, 10, 25);
+ *
+ * textLeading(20); // Set leading to 20
+ * text(lines, 40, 25);
+ *
+ * textLeading(30); // Set leading to 30
+ * text(lines, 70, 25);
+ * </code>
+ * </div>
+ */
+p5.prototype.textLeading = function(theLeading) {
+ return this._renderer.textLeading.apply(this._renderer, arguments);
+};
+
+/**
+ * Sets/gets the current font size. This size will be used in all subsequent
+ * calls to the text() function. Font size is measured in pixels.
+ *
+ * @method textSize
+ * @param {Number} theSize the size of the letters in units of pixels
+ * @return {Object|Number}
+ * @example
+ * <div>
+ * <code>
+ * textSize(12);
+ * text("Font Size 12", 10, 30);
+ * textSize(14);
+ * text("Font Size 14", 10, 60);
+ * textSize(16);
+ * text("Font Size 16", 10, 90);
+ * </code>
+ * </div>
+ */
+p5.prototype.textSize = function(theSize) {
+ return this._renderer.textSize.apply(this._renderer, arguments);
+};
+
+/**
+ * Sets/gets the style of the text for system fonts to NORMAL, ITALIC, or BOLD.
+ * Note: this may be is overridden by CSS styling. For non-system fonts
+ * (opentype, truetype, etc.) please load styled fonts instead.
+ *
+ * @method textStyle
+ * @param {Number/Constant} theStyle styling for text, either NORMAL,
+ * ITALIC, or BOLD
+ * @return {Object|String}
+ * @example
+ * <div>
+ * <code>
+ * strokeWeight(0);
+ * textSize(12);
+ * textStyle(NORMAL);
+ * text("Font Style Normal", 10, 30);
+ * textStyle(ITALIC);
+ * text("Font Style Italic", 10, 60);
+ * textStyle(BOLD);
+ * text("Font Style Bold", 10, 90);
+ * </code>
+ * </div>
+ */
+p5.prototype.textStyle = function(theStyle) {
+ return this._renderer.textStyle.apply(this._renderer, arguments);
+};
+
+/**
+ * Calculates and returns the width of any character or text string.
+ *
+ * @method textWidth
+ * @param {String} theText the String of characters to measure
+ * @return {Number}
+ * @example
+ * <div>
+ * <code>
+ * textSize(28);
+ *
+ * var aChar = 'P';
+ * var cWidth = textWidth(aChar);
+ * text(aChar, 0, 40);
+ * line(cWidth, 0, cWidth, 50);
+ *
+ * var aString = "p5.js";
+ * var sWidth = textWidth(aString);
+ * text(aString, 0, 85);
+ * line(sWidth, 50, sWidth, 100);
+ * </code>
+ * </div>
+ */
+p5.prototype.textWidth = function(theText) {
+ return this._renderer.textWidth.apply(this._renderer, arguments);
+};
+
+/**
+ * Returns the ascent of the current font at its current size. The ascent
+ * represents the distance, in pixels, of the tallest character above
+ * the baseline.
+ *
+ * @return {Number}
+ * @example
+ * <div>
+ * <code>
+ * var base = height * 0.75;
+ * var scalar = 0.8; // Different for each font
+ *
+ * textSize(32); // Set initial text size
+ * var asc = textAscent() * scalar; // Calc ascent
+ * line(0, base - asc, width, base - asc);
+ * text("dp", 0, base); // Draw text on baseline
+ *
+ * textSize(64); // Increase text size
+ * asc = textAscent() * scalar; // Recalc ascent
+ * line(40, base - asc, width, base - asc);
+ * text("dp", 40, base); // Draw text on baseline
+ * </code>
+ * </div>
+ */
+p5.prototype.textAscent = function() {
+ return this._renderer.textAscent();
+};
+
+/**
+ * Returns the descent of the current font at its current size. The descent
+ * represents the distance, in pixels, of the character with the longest
+ * descender below the baseline.
+ *
+ * @return {Number}
+ * @example
+ * <div>
+ * <code>
+ * var base = height * 0.75;
+ * var scalar = 0.8; // Different for each font
+ *
+ * textSize(32); // Set initial text size
+ * var desc = textDescent() * scalar; // Calc ascent
+ * line(0, base+desc, width, base+desc);
+ * text("dp", 0, base); // Draw text on baseline
+ *
+ * textSize(64); // Increase text size
+ * desc = textDescent() * scalar; // Recalc ascent
+ * line(40, base + desc, width, base + desc);
+ * text("dp", 40, base); // Draw text on baseline
+ * </code>
+ * </div>
+ */
+p5.prototype.textDescent = function() {
+ return this._renderer.textDescent();
+};
+
+/**
+ * Helper function to measure ascent and descent.
+ */
+p5.prototype._updateTextMetrics = function() {
+ return this._renderer._updateTextMetrics();
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],81:[function(_dereq_,module,exports){
+/**
+ * @module Typography
+ * @submodule Loading & Displaying
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var constants = _dereq_('../core/constants');
+
+_dereq_('../core/error_helpers');
+
+
+/**
+ * Draws text to the screen. Displays the information specified in the first
+ * parameter on the screen in the position specified by the additional
+ * parameters. A default font will be used unless a font is set with the
+ * textFont() function and a default size will be used unless a font is set
+ * with textSize(). Change the color of the text with the fill() function.
+ * Change the outline of the text with the stroke() and strokeWeight()
+ * functions.
+ * <br><br>
+ * The text displays in relation to the textAlign() function, which gives the
+ * option to draw to the left, right, and center of the coordinates.
+ * <br><br>
+ * The x2 and y2 parameters define a rectangular area to display within and
+ * may only be used with string data. When these parameters are specified,
+ * they are interpreted based on the current rectMode() setting. Text that
+ * does not fit completely within the rectangle specified will not be drawn
+ * to the screen.
+ *
+ * @method text
+ * @param {String} str the alphanumeric symbols to be displayed
+ * @param {Number} x x-coordinate of text
+ * @param {Number} y y-coordinate of text
+ * @param {Number} x2 by default, the width of the text box,
+ * see rectMode() for more info
+ * @param {Number} y2 by default, the height of the text box,
+ * see rectMode() for more info
+ * @return {Object} this
+ * @example
+ * <div>
+ * <code>
+ * textSize(32);
+ * text("word", 10, 30);
+ * fill(0, 102, 153);
+ * text("word", 10, 60);
+ * fill(0, 102, 153, 51);
+ * text("word", 10, 90);
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * s = "The quick brown fox jumped over the lazy dog.";
+ * fill(50);
+ * text(s, 10, 10, 70, 80); // Text wraps within text box
+ * </code>
+ * </div>
+ */
+p5.prototype.text = function(str, x, y, maxWidth, maxHeight) {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ this._validateParameters(
+ 'text',
+ args,
+ [
+ ['*', 'Number', 'Number'],
+ ['*', 'Number', 'Number', 'Number', 'Number']
+ ]
+ );
+
+ return (!(this._renderer._doFill || this._renderer._doStroke)) ? this :
+ this._renderer.text.apply(this._renderer, arguments);
+};
+
+/**
+ * Sets the current font that will be drawn with the text() function.
+ *
+ * @method textFont
+ * @param {Object|String} f a font loaded via loadFont(), or a String
+ * representing a browser-based dfault font.
+ * @return {Object} this
+ * @example
+ * <div>
+ * <code>
+ * fill(0);
+ * textSize(12);
+ * textFont("Georgia");
+ * text("Georgia", 12, 30);
+ * textFont("Helvetica");
+ * text("Helvetica", 12, 60);
+ * </code>
+ * </div>
+ * <div>
+ * <code>
+ * var fontRegular, fontItalic, fontBold;
+ * function preload() {
+ * fontRegular = loadFont("assets/Regular.otf");
+ * fontItalic = loadFont("assets/Italic.ttf");
+ * fontBold = loadFont("assets/Bold.ttf");
+ * }
+ * function setup() {
+ * background(210);
+ * fill(0).strokeWeight(0).textSize(10);
+ * textFont(fontRegular);
+ * text("Font Style Normal", 10, 30);
+ * textFont(fontItalic);
+ * text("Font Style Italic", 10, 50);
+ * textFont(fontBold);
+ * text("Font Style Bold", 10, 70);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.textFont = function(theFont, theSize) {
+
+ if (arguments.length) {
+
+ if (!theFont) {
+
+ throw Error('null font passed to textFont');
+ }
+
+ this._renderer._setProperty('_textFont', theFont);
+
+ if (theSize) {
+
+ this._renderer._setProperty('_textSize', theSize);
+ this._renderer._setProperty('_textLeading',
+ theSize * constants._DEFAULT_LEADMULT);
+ }
+
+ return this._renderer._applyTextProperties();
+ }
+
+ return this;
+};
+
+module.exports = p5;
+
+},{"../core/constants":47,"../core/core":48,"../core/error_helpers":51}],82:[function(_dereq_,module,exports){
+/**
+ * This module defines the p5.Font class and functions for
+ * drawing text to the display canvas.
+ * @module Typography
+ * @submodule Font
+ * @requires core
+ * @requires constants
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+var constants = _dereq_('../core/constants');
+
+/*
+ * TODO:
+ *
+ * API:
+ * -- textBounds()
+ * -- getPath()
+ * -- getPoints()
+ *
+ * ===========================================
+ * -- PFont functions:
+ * PFont.list()
+ *
+ * -- kerning
+ * -- alignment: justified?
+ * -- integrate p5.dom? (later)
+ */
+
+/**
+ * Base class for font handling
+ * @class p5.Font
+ * @constructor
+ * @param {Object} [pInst] pointer to p5 instance
+ */
+p5.Font = function(p) {
+
+ this.parent = p;
+
+ this.cache = {};
+
+ /**
+ * Underlying opentype font implementation
+ * @property font
+ */
+ this.font = undefined;
+};
+
+p5.Font.prototype.list = function() {
+
+ // TODO
+ throw 'not yet implemented';
+};
+
+/**
+ * Returns a tight bounding box for the given text string using this
+ * font (currently only supports single lines)
+ *
+ * @method textBounds
+ * @param {String} line a line of text
+ * @param {Number} x x-position
+ * @param {Number} y y-position
+ * @param {Number} fontSize font size to use (optional)
+ * @param {Object} options opentype options (optional)
+ *
+ * @return {Object} a rectangle object with properties: x, y, w, h
+ *
+ * @example
+ * <div>
+ * <code>
+ * var font;
+ * var textString = 'Lorem ipsum dolor sit amet.';
+ * function preload() {
+ * font = loadFont('./assets/Regular.otf');
+ * };
+ * function setup() {
+ * background(210);
+ *
+ * var bbox = font.textBounds(textString, 10, 30, 12);
+ * fill(255);
+ * stroke(0);
+ * rect(bbox.x, bbox.y, bbox.w, bbox.h);
+ * fill(0);
+ * noStroke();
+ *
+ * textFont(font);
+ * textSize(12);
+ * text(textString, 10, 30);
+ * };
+ * </code>
+ * </div>
+ */
+p5.Font.prototype.textBounds = function(str, x, y, fontSize, options) {
+
+ x = x !== undefined ? x : 0;
+ y = y !== undefined ? y : 0;
+ fontSize = fontSize || this.parent._renderer._textSize;
+
+ var result = this.cache[cacheKey('textBounds', str, x, y, fontSize)];
+ if (!result) {
+
+ var xCoords = [], yCoords = [], self = this,
+ scale = this._scale(fontSize), minX, minY, maxX, maxY;
+
+ this.font.forEachGlyph(str, x, y, fontSize, options,
+ function(glyph, gX, gY, gFontSize) {
+
+ xCoords.push(gX);
+ yCoords.push(gY);
+
+ var gm = glyph.getMetrics();
+
+ if (glyph.name !== 'space') {
+
+ xCoords.push(gX + (gm.xMax * scale));
+ yCoords.push(gY + (-gm.yMin * scale));
+ yCoords.push(gY + (-gm.yMax * scale));
+
+ } else { // NOTE: deals with broken metrics for spaces in opentype.js
+
+ xCoords.push(gX + self.font.charToGlyph(' ').advanceWidth *
+ self._scale(fontSize));
+ }
+ });
+
+ minX = Math.max(0, Math.min.apply(null, xCoords));
+ minY = Math.max(0, Math.min.apply(null, yCoords));
+ maxX = Math.max(0, Math.max.apply(null, xCoords));
+ maxY = Math.max(0, Math.max.apply(null, yCoords));
+
+ result = {
+ x: minX,
+ y: minY,
+ h: maxY - minY,
+ w: maxX - minX,
+ advance: minX - x
+ };
+
+ this.cache[cacheKey('textBounds', str, x, y, fontSize)] = result;
+ }
+ //else console.log('cache-hit');
+
+ return result;
+};
+
+
+/**
+ * Computes an array of points following the path for specified text
+ *
+ * @param {String} txt a line of text
+ * @param {Number} x x-position
+ * @param {Number} y y-position
+ * @param {Number} fontSize font size to use (optional)
+ * @param {Object} options an (optional) object that can contain:
+ *
+ * <br>sampleFactor - the ratio of path-length to number of samples
+ * (default=.25); higher values yield more points and are therefore
+ * more precise
+ *
+ * <br>simplifyThreshold - if set to a non-zero value, collinear points will be
+ * be removed from the polygon; the value represents the threshold angle to use
+ * when determining whether two edges are collinear
+ *
+ * @return {Array} an array of points, each with x, y, alpha (the path angle)
+ */
+p5.Font.prototype.textToPoints = function(txt, x, y, fontSize, options) {
+
+ var xoff = 0, result = [], glyphs = this._getGlyphs(txt);
+
+ fontSize = fontSize || this.parent._renderer._textSize;
+
+ for (var i = 0; i < glyphs.length; i++) {
+
+ var gpath = glyphs[i].getPath(x, y, fontSize),
+ paths = splitPaths(gpath.commands);
+
+ for (var j = 0; j < paths.length; j++) {
+
+ var pts = pathToPoints(paths[j], options);
+
+ for (var k = 0; k < pts.length; k++) {
+ pts[k].x += xoff;
+ result.push(pts[k]);
+ }
+ }
+
+ xoff += glyphs[i].advanceWidth * this._scale(fontSize);
+ }
+
+ return result;
+};
+
+// ----------------------------- End API ------------------------------
+
+/**
+ * Returns the set of opentype glyphs for the supplied string.
+ *
+ * Note that there is not a strict one-to-one mapping between characters
+ * and glyphs, so the list of returned glyphs can be larger or smaller
+ * than the length of the given string.
+ *
+ * @param {String} str the string to be converted
+ * @return {array} the opentype glyphs
+ */
+p5.Font.prototype._getGlyphs = function(str) {
+
+ return this.font.stringToGlyphs(str);
+};
+
+/**
+ * Returns an opentype path for the supplied string and position.
+ *
+ * @param {String} line a line of text
+ * @param {Number} x x-position
+ * @param {Number} y y-position
+ * @param {Object} options opentype options (optional)
+ * @return {Object} the opentype path
+ */
+p5.Font.prototype._getPath = function(line, x, y, options) {
+
+ var p = this.parent,
+ ctx = p._renderer.drawingContext,
+ pos = this._handleAlignment(p, ctx, line, x, y);
+
+ return this.font.getPath(line, pos.x, pos.y, p._renderer._textSize, options);
+};
+
+/*
+ * Creates an SVG-formatted path-data string
+ * (See http://www.w3.org/TR/SVG/paths.html#PathData)
+ * from the given opentype path or string/position
+ *
+ * @param {Object} path an opentype path, OR the following:
+ *
+ * @param {String} line a line of text
+ * @param {Number} x x-position
+ * @param {Number} y y-position
+ * @param {Object} options opentype options (optional), set options.decimals
+ * to set the decimal precision of the path-data
+ *
+ * @return {Object} this p5.Font object
+ */
+p5.Font.prototype._getPathData = function(line, x, y, options) {
+
+ var decimals = 3;
+
+ // create path from string/position
+ if (typeof line === 'string' && arguments.length > 2) {
+
+ line = this._getPath(line, x, y, options);
+ }
+ // handle options specified in 2nd arg
+ else if (typeof x === 'object') {
+
+ options = x;
+ }
+
+ // handle svg arguments
+ if (options && typeof options.decimals === 'number') {
+
+ decimals = options.decimals;
+ }
+
+ return line.toPathData(decimals);
+};
+
+/*
+ * Creates an SVG <path> element, as a string,
+ * from the given opentype path or string/position
+ *
+ * @param {Object} path an opentype path, OR the following:
+ *
+ * @param {String} line a line of text
+ * @param {Number} x x-position
+ * @param {Number} y y-position
+ * @param {Object} options opentype options (optional), set options.decimals
+ * to set the decimal precision of the path-data in the <path> element,
+ * options.fill to set the fill color for the <path> element,
+ * options.stroke to set the stroke color for the <path> element,
+ * options.strokeWidth to set the strokeWidth for the <path> element.
+ *
+ * @return {Object} this p5.Font object
+ */
+p5.Font.prototype._getSVG = function(line, x, y, options) {
+
+ var decimals = 3;
+
+ // create path from string/position
+ if (typeof line === 'string' && arguments.length > 2) {
+
+ line = this._getPath(line, x, y, options);
+ }
+ // handle options specified in 2nd arg
+ else if (typeof x === 'object') {
+
+ options = x;
+ }
+
+ // handle svg arguments
+ if (options) {
+ if (typeof options.decimals === 'number') {
+ decimals = options.decimals;
+ }
+ if (typeof options.strokeWidth === 'number') {
+ line.strokeWidth = options.strokeWidth;
+ }
+ if (typeof options.fill !== 'undefined') {
+ line.fill = options.fill;
+ }
+ if (typeof options.stroke !== 'undefined') {
+ line.stroke = options.stroke;
+ }
+ }
+
+ return line.toSVG(decimals);
+};
+
+/*
+ * Renders an opentype path or string/position
+ * to the current graphics context
+ *
+ * @param {Object} path an opentype path, OR the following:
+ *
+ * @param {String} line a line of text
+ * @param {Number} x x-position
+ * @param {Number} y y-position
+ * @param {Object} options opentype options (optional)
+ *
+ * @return {Object} this p5.Font object
+ */
+p5.Font.prototype._renderPath = function(line, x, y, options) {
+
+ var pdata, pg = (options && options.renderer) || this.parent._renderer,
+ ctx = pg.drawingContext;
+
+ if (typeof line === 'object' && line.commands) {
+
+ pdata = line.commands;
+ } else {
+
+ //pos = handleAlignment(p, ctx, line, x, y);
+ pdata = this._getPath(line, x, y, pg._textSize, options).commands;
+ }
+
+ ctx.beginPath();
+ for (var i = 0; i < pdata.length; i += 1) {
+
+ var cmd = pdata[i];
+ if (cmd.type === 'M') {
+ ctx.moveTo(cmd.x, cmd.y);
+ } else if (cmd.type === 'L') {
+ ctx.lineTo(cmd.x, cmd.y);
+ } else if (cmd.type === 'C') {
+ ctx.bezierCurveTo(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.x, cmd.y);
+ } else if (cmd.type === 'Q') {
+ ctx.quadraticCurveTo(cmd.x1, cmd.y1, cmd.x, cmd.y);
+ } else if (cmd.type === 'Z') {
+ ctx.closePath();
+ }
+ }
+
+ // only draw stroke if manually set by user
+ if (pg._doStroke && pg._strokeSet) {
+
+ ctx.stroke();
+ }
+
+ if (pg._doFill) {
+
+ // if fill hasn't been set by user, use default-text-fill
+ ctx.fillStyle = pg._fillSet ? ctx.fillStyle : constants._DEFAULT_TEXT_FILL;
+ ctx.fill();
+ }
+
+ return this;
+};
+
+p5.Font.prototype._textWidth = function(str, fontSize) {
+
+ if (str === ' ') { // special case for now
+
+ return this.font.charToGlyph(' ').advanceWidth * this._scale(fontSize);
+ }
+
+ var bounds = this.textBounds(str, 0, 0, fontSize);
+ return bounds.w + bounds.advance;
+};
+
+p5.Font.prototype._textAscent = function(fontSize) {
+
+ return this.font.ascender * this._scale(fontSize);
+};
+
+p5.Font.prototype._textDescent = function(fontSize) {
+
+ return -this.font.descender * this._scale(fontSize);
+};
+
+p5.Font.prototype._scale = function(fontSize) {
+
+ return (1 / this.font.unitsPerEm) * (fontSize ||
+ this.parent._renderer._textSize);
+};
+
+p5.Font.prototype._handleAlignment = function(p, ctx, line, x, y) {
+
+ var textWidth = this._textWidth(line),
+ textAscent = this._textAscent(),
+ textDescent = this._textDescent(),
+ textHeight = textAscent + textDescent;
+
+ if (ctx.textAlign === constants.CENTER) {
+ x -= textWidth / 2;
+ } else if (ctx.textAlign === constants.RIGHT) {
+ x -= textWidth;
+ }
+
+ if (ctx.textBaseline === constants.TOP) {
+ y += textHeight;
+ } else if (ctx.textBaseline === constants._CTX_MIDDLE) {
+ y += textHeight / 2 - textDescent;
+ } else if (ctx.textBaseline === constants.BOTTOM) {
+ y -= textDescent;
+ }
+
+ return { x: x, y: y };
+};
+
+// path-utils
+
+function pathToPoints(cmds, options) {
+
+ var opts = parseOpts(options, {
+ sampleFactor: 0.1,
+ simplifyThreshold: 0,
+ });
+
+ var len = pointAtLength(cmds,0,1), // total-length
+ t = len / (len * opts.sampleFactor),
+ pts = [];
+
+ for (var i = 0; i < len; i += t) {
+ pts.push(pointAtLength(cmds, i));
+ }
+
+ if (opts.simplifyThreshold) {
+ /*var count = */simplify(pts, opts.simplifyThreshold);
+ //console.log('Simplify: removed ' + count + ' pts');
+ }
+
+ return pts;
+}
+
+function simplify(pts, angle) {
+
+ angle = (typeof angle === 'undefined') ? 0 : angle;
+
+ var num = 0;
+ for (var i = pts.length - 1; pts.length > 3 && i >= 0; --i) {
+
+ if (collinear(at(pts, i - 1), at(pts, i), at(pts, i + 1), angle)) {
+
+ // Remove the middle point
+ pts.splice(i % pts.length, 1);
+ num++;
+ }
+ }
+ return num;
+}
+
+function splitPaths(cmds) {
+
+ var paths = [], current;
+ for (var i = 0; i < cmds.length; i++) {
+ if (cmds[i].type === 'M') {
+ if (current) {
+ paths.push(current);
+ }
+ current = [];
+ }
+ current.push(cmdToArr(cmds[i]));
+ }
+ paths.push(current);
+
+ return paths;
+}
+
+function cmdToArr(cmd) {
+
+ var arr = [ cmd.type ];
+ if (cmd.type === 'M' || cmd.type === 'L') { // moveto or lineto
+ arr.push(cmd.x, cmd.y);
+ } else if (cmd.type === 'C') {
+ arr.push(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.x, cmd.y);
+ } else if (cmd.type === 'Q') {
+ arr.push(cmd.x1, cmd.y1, cmd.x, cmd.y);
+ }
+ // else if (cmd.type === 'Z') { /* no-op */ }
+ return arr;
+}
+
+function parseOpts(options, defaults) {
+
+ if (typeof options !== 'object') {
+ options = defaults;
+ }
+ else {
+ for (var key in defaults) {
+ if (typeof options[key] === 'undefined') {
+ options[key] = defaults[key];
+ }
+ }
+ }
+ return options;
+}
+
+//////////////////////// Helpers ////////////////////////////
+
+function at(v, i) {
+ var s = v.length;
+ return v[i < 0 ? i % s + s : i % s];
+}
+
+function collinear(a, b, c, thresholdAngle) {
+
+ if (!thresholdAngle) {
+ return areaTriangle(a, b, c) === 0;
+ }
+
+ if (typeof collinear.tmpPoint1 === 'undefined') {
+ collinear.tmpPoint1 = [];
+ collinear.tmpPoint2 = [];
+ }
+
+ var ab = collinear.tmpPoint1, bc = collinear.tmpPoint2;
+ ab.x = b.x - a.x;
+ ab.y = b.y - a.y;
+ bc.x = c.x - b.x;
+ bc.y = c.y - b.y;
+
+ var dot = ab.x * bc.x + ab.y * bc.y,
+ magA = Math.sqrt(ab.x * ab.x + ab.y * ab.y),
+ magB = Math.sqrt(bc.x * bc.x + bc.y * bc.y),
+ angle = Math.acos(dot / (magA * magB));
+
+ return angle < thresholdAngle;
+}
+
+function areaTriangle(a, b, c) {
+ return (((b[0] - a[0]) * (c[1] - a[1])) - ((c[0] - a[0]) * (b[1] - a[1])));
+}
+
+// Portions of below code copyright 2008 Dmitry Baranovskiy (via MIT license)
+
+function findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
+
+ var t1 = 1 - t, t13 = Math.pow(t1, 3), t12 = Math.pow(t1, 2), t2 = t * t,
+ t3 = t2 * t, x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x +
+ t3 * p2x, y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y +
+ t3 * p2y, mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x),
+ my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y),
+ nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x),
+ ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y),
+ ax = t1 * p1x + t * c1x, ay = t1 * p1y + t * c1y,
+ cx = t1 * c2x + t * p2x, cy = t1 * c2y + t * p2y,
+ alpha = (90 - Math.atan2(mx - nx, my - ny) * 180 / Math.PI);
+
+ if (mx > nx || my < ny) { alpha += 180; }
+
+ return { x: x, y: y, m: { x: mx, y: my }, n: { x: nx, y: ny },
+ start: { x: ax, y: ay }, end: { x: cx, y: cy }, alpha: alpha
+ };
+}
+
+function getPointAtSegmentLength(p1x,p1y,c1x,c1y,c2x,c2y,p2x,p2y,length) {
+ return (length == null) ? bezlen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) :
+ findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y,
+ getTatLen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length));
+}
+
+function pointAtLength(path, length, istotal) {
+ path = path2curve(path);
+ var x, y, p, l, sp = '', subpaths = {}, point, len = 0;
+ for (var i = 0, ii = path.length; i < ii; i++) {
+ p = path[i];
+ if (p[0] === 'M') {
+ x = +p[1];
+ y = +p[2];
+ } else {
+ l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
+ if (len + l > length) {
+ if (!istotal) {
+ point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5],
+ p[6], length - len);
+ return { x: point.x, y: point.y, alpha: point.alpha };
+ }
+ }
+ len += l;
+ x = +p[5];
+ y = +p[6];
+ }
+ sp += p.shift() + p;
+ }
+ subpaths.end = sp;
+
+ point = istotal ? len : findDotsAtSegment
+ (x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1);
+
+ if (point.alpha) {
+ point = { x: point.x, y: point.y, alpha: point.alpha };
+ }
+
+ return point;
+}
+
+function pathToAbsolute(pathArray) {
+
+ var res = [], x = 0, y = 0, mx = 0, my = 0, start = 0;
+ if (pathArray[0][0] === 'M') {
+ x = +pathArray[0][1];
+ y = +pathArray[0][2];
+ mx = x;
+ my = y;
+ start++;
+ res[0] = ['M', x, y];
+ }
+
+ var dots,crz = pathArray.length===3 && pathArray[0][0]==='M' &&
+ pathArray[1][0].toUpperCase()==='R' && pathArray[2][0].toUpperCase()==='Z';
+
+ for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) {
+ res.push(r = []);
+ pa = pathArray[i];
+ if (pa[0] !== String.prototype.toUpperCase.call(pa[0])) {
+ r[0] = String.prototype.toUpperCase.call(pa[0]);
+ switch (r[0]) {
+ case 'A':
+ r[1] = pa[1];
+ r[2] = pa[2];
+ r[3] = pa[3];
+ r[4] = pa[4];
+ r[5] = pa[5];
+ r[6] = +(pa[6] + x);
+ r[7] = +(pa[7] + y);
+ break;
+ case 'V':
+ r[1] = +pa[1] + y;
+ break;
+ case 'H':
+ r[1] = +pa[1] + x;
+ break;
+ case 'R':
+ dots = [x, y].concat(pa.slice(1));
+ for (var j = 2, jj = dots.length; j < jj; j++) {
+ dots[j] = +dots[j] + x;
+ dots[++j] = +dots[j] + y;
+ }
+ res.pop();
+ res = res.concat(catmullRom2bezier(dots, crz));
+ break;
+ case 'M':
+ mx = +pa[1] + x;
+ my = +pa[2] + y;
+ break;
+ default:
+ for (j = 1, jj = pa.length; j < jj; j++) {
+ r[j] = +pa[j] + ((j % 2) ? x : y);
+ }
+ }
+ } else if (pa[0] === 'R') {
+ dots = [x, y].concat(pa.slice(1));
+ res.pop();
+ res = res.concat(catmullRom2bezier(dots, crz));
+ r = ['R'].concat(pa.slice(-2));
+ } else {
+ for (var k = 0, kk = pa.length; k < kk; k++) {
+ r[k] = pa[k];
+ }
+ }
+ switch (r[0]) {
+ case 'Z':
+ x = mx;
+ y = my;
+ break;
+ case 'H':
+ x = r[1];
+ break;
+ case 'V':
+ y = r[1];
+ break;
+ case 'M':
+ mx = r[r.length - 2];
+ my = r[r.length - 1];
+ break;
+ default:
+ x = r[r.length - 2];
+ y = r[r.length - 1];
+ }
+ }
+ return res;
+}
+
+function path2curve(path, path2) {
+
+ var p = pathToAbsolute(path), p2 = path2 && pathToAbsolute(path2),
+ attrs = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null },
+ attrs2 = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null },
+
+ processPath = function(path, d, pcom) {
+ var nx, ny, tq = { T: 1, Q: 1 };
+ if (!path) { return ['C', d.x, d.y, d.x, d.y, d.x, d.y]; }
+ if (!(path[0] in tq)) { d.qx = d.qy = null; }
+ switch (path[0]) {
+ case 'M':
+ d.X = path[1];
+ d.Y = path[2];
+ break;
+ case 'A':
+ path = ['C'].concat(a2c.apply(0, [d.x, d.y].concat(path.slice(1))));
+ break;
+ case 'S':
+ if (pcom === 'C' || pcom === 'S') {
+ nx = d.x * 2 - d.bx;
+ ny = d.y * 2 - d.by;
+ } else {
+ nx = d.x;
+ ny = d.y;
+ }
+ path = ['C', nx, ny].concat(path.slice(1));
+ break;
+ case 'T':
+ if (pcom === 'Q' || pcom === 'T') {
+ d.qx = d.x * 2 - d.qx;
+ d.qy = d.y * 2 - d.qy;
+ } else {
+ d.qx = d.x;
+ d.qy = d.y;
+ }
+ path = ['C'].concat(q2c(d.x, d.y, d.qx, d.qy, path[1], path[2]));
+ break;
+ case 'Q':
+ d.qx = path[1];
+ d.qy = path[2];
+ path = ['C'].concat(q2c(d.x,d.y,path[1],path[2],path[3],path[4]));
+ break;
+ case 'L':
+ path = ['C'].concat(l2c(d.x, d.y, path[1], path[2]));
+ break;
+ case 'H':
+ path = ['C'].concat(l2c(d.x, d.y, path[1], d.y));
+ break;
+ case 'V':
+ path = ['C'].concat(l2c(d.x, d.y, d.x, path[1]));
+ break;
+ case 'Z':
+ path = ['C'].concat(l2c(d.x, d.y, d.X, d.Y));
+ break;
+ }
+ return path;
+ },
+
+ fixArc = function(pp, i) {
+ if (pp[i].length > 7) {
+ pp[i].shift();
+ var pi = pp[i];
+ while (pi.length) {
+ pcoms1[i] = 'A';
+ if (p2) { pcoms2[i] = 'A'; }
+ pp.splice(i++, 0, ['C'].concat(pi.splice(0, 6)));
+ }
+ pp.splice(i, 1);
+ ii = Math.max(p.length, p2 && p2.length || 0);
+ }
+ },
+
+ fixM = function(path1, path2, a1, a2, i) {
+ if (path1 && path2 && path1[i][0] === 'M' && path2[i][0] !== 'M') {
+ path2.splice(i, 0, ['M', a2.x, a2.y]);
+ a1.bx = 0;
+ a1.by = 0;
+ a1.x = path1[i][1];
+ a1.y = path1[i][2];
+ ii = Math.max(p.length, p2 && p2.length || 0);
+ }
+ },
+
+ pcoms1 = [], // path commands of original path p
+ pcoms2 = [], // path commands of original path p2
+ pfirst = '', // temporary holder for original path command
+ pcom = ''; // holder for previous path command of original path
+
+ for (var i = 0, ii = Math.max(p.length, p2 && p2.length || 0); i < ii; i++) {
+ if (p[i]) { pfirst = p[i][0]; } // save current path command
+
+ if (pfirst !== 'C') {
+ pcoms1[i] = pfirst; // Save current path command
+ if (i) { pcom = pcoms1[i - 1]; } // Get previous path command pcom
+ }
+ p[i] = processPath(p[i], attrs, pcom);
+
+ if (pcoms1[i] !== 'A' && pfirst === 'C') { pcoms1[i] = 'C'; }
+
+ fixArc(p, i); // fixArc adds also the right amount of A:s to pcoms1
+
+ if (p2) { // the same procedures is done to p2
+ if (p2[i]) { pfirst = p2[i][0]; }
+ if (pfirst !== 'C') {
+ pcoms2[i] = pfirst;
+ if (i) { pcom = pcoms2[i - 1]; }
+ }
+ p2[i] = processPath(p2[i], attrs2, pcom);
+
+ if (pcoms2[i] !== 'A' && pfirst === 'C') { pcoms2[i] = 'C'; }
+
+ fixArc(p2, i);
+ }
+ fixM(p, p2, attrs, attrs2, i);
+ fixM(p2, p, attrs2, attrs, i);
+ var seg = p[i], seg2 = p2 && p2[i], seglen = seg.length,
+ seg2len = p2 && seg2.length;
+ attrs.x = seg[seglen - 2];
+ attrs.y = seg[seglen - 1];
+ attrs.bx = parseFloat(seg[seglen - 4]) || attrs.x;
+ attrs.by = parseFloat(seg[seglen - 3]) || attrs.y;
+ attrs2.bx = p2 && (parseFloat(seg2[seg2len - 4]) || attrs2.x);
+ attrs2.by = p2 && (parseFloat(seg2[seg2len - 3]) || attrs2.y);
+ attrs2.x = p2 && seg2[seg2len - 2];
+ attrs2.y = p2 && seg2[seg2len - 1];
+ }
+
+ return p2 ? [p, p2] : p;
+}
+
+function a2c(x1, y1, rx, ry, angle, lac, sweep_flag, x2, y2, recursive) {
+ // for more information of where this Math came from visit:
+ // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
+ var PI = Math.PI, _120 = PI * 120 / 180, f1, f2, cx, cy,
+ rad = PI / 180 * (+angle || 0), res = [], xy,
+ rotate = function (x, y, rad) {
+ var X = x * Math.cos(rad) - y * Math.sin(rad),
+ Y = x * Math.sin(rad) + y * Math.cos(rad);
+ return { x: X, y: Y };
+ };
+ if (!recursive) {
+ xy = rotate(x1, y1, -rad);
+ x1 = xy.x;
+ y1 = xy.y;
+ xy = rotate(x2, y2, -rad);
+ x2 = xy.x;
+ y2 = xy.y;
+ var x = (x1 - x2) / 2, y = (y1 - y2) / 2,
+ h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
+ if (h > 1) {
+ h = Math.sqrt(h);
+ rx = h * rx;
+ ry = h * ry;
+ }
+ var rx2 = rx * rx, ry2 = ry * ry,
+ k = (lac === sweep_flag ? -1 : 1) * Math.sqrt(Math.abs
+ ((rx2 * ry2 - rx2 * y * y - ry2 * x * x)/(rx2 * y * y + ry2 * x * x)));
+
+ cx = k * rx * y / ry + (x1 + x2) / 2;
+ cy = k * -ry * x / rx + (y1 + y2) / 2;
+ f1 = Math.asin(((y1 - cy) / ry).toFixed(9));
+ f2 = Math.asin(((y2 - cy) / ry).toFixed(9));
+
+ f1 = x1 < cx ? PI - f1 : f1;
+ f2 = x2 < cx ? PI - f2 : f2;
+
+ if (f1 < 0) { f1 = PI * 2 + f1; }
+ if (f2 < 0) { f2 = PI * 2 + f2; }
+
+ if (sweep_flag && f1 > f2) {
+ f1 = f1 - PI * 2;
+ }
+ if (!sweep_flag && f2 > f1) {
+ f2 = f2 - PI * 2;
+ }
+ } else {
+ f1 = recursive[0];
+ f2 = recursive[1];
+ cx = recursive[2];
+ cy = recursive[3];
+ }
+ var df = f2 - f1;
+ if (Math.abs(df) > _120) {
+ var f2old = f2, x2old = x2, y2old = y2;
+ f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
+ x2 = cx + rx * Math.cos(f2);
+ y2 = cy + ry * Math.sin(f2);
+ res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old,
+ [f2, f2old, cx, cy]);
+ }
+ df = f2 - f1;
+ var c1 = Math.cos(f1),
+ s1 = Math.sin(f1),
+ c2 = Math.cos(f2),
+ s2 = Math.sin(f2),
+ t = Math.tan(df / 4),
+ hx = 4 / 3 * rx * t,
+ hy = 4 / 3 * ry * t,
+ m1 = [x1, y1],
+ m2 = [x1 + hx * s1, y1 - hy * c1],
+ m3 = [x2 + hx * s2, y2 - hy * c2],
+ m4 = [x2, y2];
+ m2[0] = 2 * m1[0] - m2[0];
+ m2[1] = 2 * m1[1] - m2[1];
+ if (recursive) {
+ return [m2, m3, m4].concat(res);
+ } else {
+ res = [m2, m3, m4].concat(res).join().split(',');
+ var newres = [];
+ for (var i = 0, ii = res.length; i < ii; i++) {
+ newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i],
+ res[i + 1], rad).x;
+ }
+ return newres;
+ }
+}
+
+// http://schepers.cc/getting-to-the-point
+function catmullRom2bezier(crp, z) {
+ var d = [];
+ for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) {
+ var p = [{
+ x: +crp[i - 2],
+ y: +crp[i - 1]
+ }, {
+ x: +crp[i],
+ y: +crp[i + 1]
+ }, {
+ x: +crp[i + 2],
+ y: +crp[i + 3]
+ }, {
+ x: +crp[i + 4],
+ y: +crp[i + 5]
+ }];
+ if (z) {
+ if (!i) {
+ p[0] = {
+ x: +crp[iLen - 2],
+ y: +crp[iLen - 1]
+ };
+ } else if (iLen - 4 === i) {
+ p[3] = {
+ x: +crp[0],
+ y: +crp[1]
+ };
+ } else if (iLen - 2 === i) {
+ p[2] = {
+ x: +crp[0],
+ y: +crp[1]
+ };
+ p[3] = {
+ x: +crp[2],
+ y: +crp[3]
+ };
+ }
+ } else {
+ if (iLen - 4 === i) {
+ p[3] = p[2];
+ } else if (!i) {
+ p[0] = {
+ x: +crp[i],
+ y: +crp[i + 1]
+ };
+ }
+ }
+ d.push(['C', (-p[0].x + 6 * p[1].x + p[2].x) / 6, (-p[0].y + 6 * p[1].y +
+ p[2].y) / 6, (p[1].x + 6 * p[2].x - p[3].x) / 6, (p[1].y + 6 * p[2].y -
+ p[3].y) / 6, p[2].x, p[2].y ]);
+ }
+
+ return d;
+}
+
+function l2c(x1, y1, x2, y2) { return [x1, y1, x2, y2, x2, y2]; }
+
+function q2c(x1, y1, ax, ay, x2, y2) {
+ var _13 = 1 / 3, _23 = 2 / 3;
+ return [
+ _13 * x1 + _23 * ax, _13 * y1 + _23 * ay,
+ _13 * x2 + _23 * ax, _13 * y2 + _23 * ay, x2, y2
+ ];
+}
+
+function bezlen(x1, y1, x2, y2, x3, y3, x4, y4, z) {
+ if (z == null) { z = 1; }
+ z = z > 1 ? 1 : z < 0 ? 0 : z;
+ var z2 = z / 2,
+ n = 12, Tvalues = [-0.1252, 0.1252, -0.3678, 0.3678, -0.5873, 0.5873,
+ -0.7699, 0.7699, -0.9041, 0.9041, -0.9816, 0.9816],
+ sum = 0, Cvalues = [0.2491, 0.2491, 0.2335, 0.2335, 0.2032, 0.2032,
+ 0.1601, 0.1601, 0.1069, 0.1069, 0.0472, 0.0472 ];
+ for (var i = 0; i < n; i++) {
+ var ct = z2 * Tvalues[i] + z2,
+ xbase = base3(ct, x1, x2, x3, x4),
+ ybase = base3(ct, y1, y2, y3, y4),
+ comb = xbase * xbase + ybase * ybase;
+ sum += Cvalues[i] * Math.sqrt(comb);
+ }
+ return z2 * sum;
+}
+
+function getTatLen(x1, y1, x2, y2, x3, y3, x4, y4, ll) {
+ if (ll < 0 || bezlen(x1, y1, x2, y2, x3, y3, x4, y4) < ll) {
+ return;
+ }
+ var t = 1, step = t / 2, t2 = t - step, l, e = 0.01;
+ l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2);
+ while (Math.abs(l - ll) > e) {
+ step /= 2;
+ t2 += (l < ll ? 1 : -1) * step;
+ l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2);
+ }
+ return t2;
+}
+
+function base3(t, p1, p2, p3, p4) {
+ var t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4,
+ t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3;
+ return t * t2 - 3 * p1 + 3 * p2;
+}
+
+function cacheKey() {
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; ++i) {
+ args[i] = arguments[i];
+ }
+ i = args.length;
+ var hash = '';
+ while (i--) {
+ hash += (args[i] === Object(args[i])) ?
+ JSON.stringify(args[i]) : args[i];
+ }
+ return hash;
+}
+
+module.exports = p5.Font;
+
+},{"../core/constants":47,"../core/core":48}],83:[function(_dereq_,module,exports){
+/**
+ * @module Data
+ * @submodule Array Functions
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * Adds a value to the end of an array. Extends the length of
+ * the array by one. Maps to Array.push().
+ *
+ * @method append
+ * @param {Array} array Array to append
+ * @param {any} value to be added to the Array
+ * @example
+ * <div class = "norender"><code>
+ * function setup() {
+ *
+ * var myArray = new Array("Mango", "Apple", "Papaya")
+ * print(myArray) // ["Mango", "Apple", "Papaya"]
+ *
+ * append(myArray, "Peach")
+ * print(myArray) // ["Mango", "Apple", "Papaya", "Peach"]
+ *
+ * }
+ * </div></code>
+ */
+p5.prototype.append = function(array, value) {
+ array.push(value);
+ return array;
+};
+
+/**
+ * Copies an array (or part of an array) to another array. The src array is
+ * copied to the dst array, beginning at the position specified by
+ * srcPosition and into the position specified by dstPosition. The number of
+ * elements to copy is determined by length. Note that copying values
+ * overwrites existing values in the destination array. To append values
+ * instead of overwriting them, use concat().
+ * <br><br>
+ * The simplified version with only two arguments, arrayCopy(src, dst),
+ * copies an entire array to another of the same size. It is equivalent to
+ * arrayCopy(src, 0, dst, 0, src.length).
+ * <br><br>
+ * Using this function is far more efficient for copying array data than
+ * iterating through a for() loop and copying each element individually.
+ *
+ * @method arrayCopy
+ * @param {Array} src the source Array
+ * @param {Number} [srcPosition] starting position in the source Array
+ * @param {Array} dst the destination Array
+ * @param {Number} [dstPosition] starting position in the destination Array
+ * @param {Number} [length] number of Array elements to be copied
+ *
+ * @example
+ * <div class="norender"><code>
+ * function setup() {
+ *
+ * var src = new Array("A", "B", "C");
+ * var dst = new Array( 1 , 2 , 3 );
+ * var srcPosition = 1;
+ * var dstPosition = 0;
+ * var length = 2;
+ *
+ * print(src); // ["A", "B", "C"]
+ * print(dst); // [ 1 , 2 , 3 ]
+ *
+ * arrayCopy(src, srcPosition, dst, dstPosition, length);
+ * print(dst); // ["B", "C", 3]
+ *
+ * }
+ * </div></code>
+ */
+p5.prototype.arrayCopy = function(
+ src,
+ srcPosition,
+ dst,
+ dstPosition,
+ length) {
+
+ // the index to begin splicing from dst array
+ var start,
+ end;
+
+ if (typeof length !== 'undefined') {
+
+ end = Math.min(length, src.length);
+ start = dstPosition;
+ src = src.slice(srcPosition, end + srcPosition);
+
+ } else {
+
+ if (typeof dst !== 'undefined') { // src, dst, length
+ // rename so we don't get confused
+ end = dst;
+ end = Math.min(end, src.length);
+ } else { // src, dst
+ end = src.length;
+ }
+
+ start = 0;
+ // rename so we don't get confused
+ dst = srcPosition;
+ src = src.slice(0, end);
+ }
+
+ // Since we are not returning the array and JavaScript is pass by reference
+ // we must modify the actual values of the array
+ // instead of reassigning arrays
+ Array.prototype.splice.apply(dst, [start, end].concat(src));
+
+};
+
+/**
+ * Concatenates two arrays, maps to Array.concat(). Does not modify the
+ * input arrays.
+ *
+ * @method concat
+ * @param {Array} a first Array to concatenate
+ * @param {Array} b second Array to concatenate
+ * @return {Array} concatenated array
+ *
+ * @example
+ * <div class = "norender"><code>
+ * function setup() {
+ * var arr1 = new Array("A", "B", "C");
+ * var arr2 = new Array( 1 , 2 , 3 );
+ *
+ * print(arr1); // ["A","B","C"]
+ * print(arr2); // [1,2,3]
+ *
+ * var arr3 = concat(arr1, arr2);
+ *
+ * print(arr1); // ["A","B","C"]
+ * print(arr2); // [1,2,3]
+ * print(arr3); // ["A","B","C",1,2,3]
+ *
+ * }
+ * </div></code>
+ */
+p5.prototype.concat = function(list0, list1) {
+ return list0.concat(list1);
+};
+
+/**
+ * Reverses the order of an array, maps to Array.reverse()
+ *
+ * @method reverse
+ * @param {Array} list Array to reverse
+ * @example
+ * <div class="norender"><code>
+ * function setup() {
+ * var myArray = new Array("A", "B", "C");
+ * print(myArray); // ["A","B","C"]
+ *
+ * reverse(myArray);
+ * print(myArray); // ["C","B","A"]
+ * }
+ * </div></code>
+ */
+p5.prototype.reverse = function(list) {
+ return list.reverse();
+};
+
+/**
+ * Decreases an array by one element and returns the shortened array,
+ * maps to Array.pop().
+ *
+ * @method shorten
+ * @param {Array} list Array to shorten
+ * @return {Array} shortened Array
+ * @example
+ * <div class = "norender"><code>
+ * function setup() {
+ * var myArray = new Array("A", "B", "C");
+ * print(myArray); // ["A","B","C"]
+ *
+ * var newArray = shorten(myArray);
+ * print(myArray); // ["A","B","C"]
+ * print(newArray); // ["A","B"]
+ * }
+ * </div></code>
+ */
+p5.prototype.shorten = function(list) {
+ list.pop();
+ return list;
+};
+
+/**
+ * Randomizes the order of the elements of an array. Implements
+ * <a href="http://Bost.Ocks.org/mike/shuffle/" target=_blank>
+ * Fisher-Yates Shuffle Algorithm</a>.
+ *
+ * @method shuffle
+ * @param {Array} array Array to shuffle
+ * @param {Boolean} [bool] modify passed array
+ * @return {Array} shuffled Array
+ * @example
+ * <div><code>
+ * function setup() {
+ * var regularArr = ['ABC', 'def', createVector(), TAU, Math.E];
+ * print(regularArr);
+ * shuffle(regularArr, true); // force modifications to passed array
+ * print(regularArr);
+ *
+ * // By default shuffle() returns a shuffled cloned array:
+ * var newArr = shuffle(regularArr);
+ * print(regularArr);
+ * print(newArr);
+ * }
+ * </code></div>
+ */
+p5.prototype.shuffle = function(arr, bool) {
+ var isView = ArrayBuffer && ArrayBuffer.isView && ArrayBuffer.isView(arr);
+ arr = bool || isView ? arr : arr.slice();
+
+ var rnd, tmp, idx = arr.length;
+ while (idx > 1) {
+ rnd = Math.random()*idx | 0;
+
+ tmp = arr[--idx];
+ arr[idx] = arr[rnd];
+ arr[rnd] = tmp;
+ }
+
+ return arr;
+};
+
+/**
+ * Sorts an array of numbers from smallest to largest, or puts an array of
+ * words in alphabetical order. The original array is not modified; a
+ * re-ordered array is returned. The count parameter states the number of
+ * elements to sort. For example, if there are 12 elements in an array and
+ * count is set to 5, only the first 5 elements in the array will be sorted.
+ *
+ * @method sort
+ * @param {Array} list Array to sort
+ * @param {Number} [count] number of elements to sort, starting from 0
+ *
+ * @example
+ * <div class = "norender"><code>
+ * function setup() {
+ * var words = new Array("banana", "apple", "pear","lime");
+ * print(words); // ["banana", "apple", "pear", "lime"]
+ * var count = 4; // length of array
+ *
+ * sort(words, count);
+ * print(words); // ["apple", "banana", "lime", "pear"]
+ * }
+ * </div></code>
+ * <div class = "norender"><code>
+ * function setup() {
+ * var numbers = new Array(2,6,1,5,14,9,8,12);
+ * print(numbers); // [2,6,1,5,14,9,8,12]
+ * var count = 5; // Less than the length of the array
+ *
+ * sort(numbers, count);
+ * print(numbers); // [1,2,5,6,14,9,8,12]
+ * }
+ * </div></code>
+ */
+p5.prototype.sort = function(list, count) {
+ var arr = count ? list.slice(0, Math.min(count, list.length)) : list;
+ var rest = count ? list.slice(Math.min(count, list.length)) : [];
+ if (typeof arr[0] === 'string') {
+ arr = arr.sort();
+ } else {
+ arr = arr.sort(function(a,b){return a-b;});
+ }
+ return arr.concat(rest);
+};
+
+/**
+ * Inserts a value or an array of values into an existing array. The first
+ * parameter specifies the initial array to be modified, and the second
+ * parameter defines the data to be inserted. The third parameter is an index
+ * value which specifies the array position from which to insert data.
+ * (Remember that array index numbering starts at zero, so the first position
+ * is 0, the second position is 1, and so on.)
+ *
+ * @method splice
+ * @param {Array} list Array to splice into
+ * @param {any} value value to be spliced in
+ * @param {Number} position in the array from which to insert data
+ *
+ * @example
+ * <div class = "norender"><code>
+ * function setup() {
+ * var myArray = new Array(0,1,2,3,4);
+ * var insArray = new Array("A","B","C");
+ * print(myArray); // [0,1,2,3,4]
+ * print(insArray); // ["A","B","C"]
+ *
+ * splice(myArray, insArray, 3);
+ * print(myArray); // [0,1,2,"A","B","C",3,4]
+ * }
+ * </div></code>
+ */
+p5.prototype.splice = function(list, value, index) {
+
+ // note that splice returns spliced elements and not an array
+ Array.prototype.splice.apply(list, [index, 0].concat(value));
+
+ return list;
+};
+
+/**
+ * Extracts an array of elements from an existing array. The list parameter
+ * defines the array from which the elements will be copied, and the start
+ * and count parameters specify which elements to extract. If no count is
+ * given, elements will be extracted from the start to the end of the array.
+ * When specifying the start, remember that the first array element is 0.
+ * This function does not change the source array.
+ *
+ * @method subset
+ * @param {Array} list Array to extract from
+ * @param {Number} start position to begin
+ * @param {Number} [count] number of values to extract
+ * @return {Array} Array of extracted elements
+ *
+ * @example
+ * <div class = "norender"><code>
+ * function setup() {
+ * var myArray = new Array(1,2,3,4,5);
+ * print(myArray); // [1,2,3,4,5]
+ *
+ * var sub1 = subset(myArray, 0, 3);
+ * var sub2 = subset(myArray, 2, 2);
+ * print(sub1); // [1,2,3]
+ * print(sub2); // [3,4]
+ * }
+ * </div></code>
+ */
+p5.prototype.subset = function(list, start, count) {
+ if (typeof count !== 'undefined') {
+ return list.slice(start, start + count);
+ } else {
+ return list.slice(start, list.length);
+ }
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],84:[function(_dereq_,module,exports){
+/**
+ * @module Data
+ * @submodule Conversion
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * Converts a string to its floating point representation. The contents of a
+ * string must resemble a number, or NaN (not a number) will be returned.
+ * For example, float("1234.56") evaluates to 1234.56, but float("giraffe")
+ * will return NaN.
+ *
+ * @method float
+ * @param {String} str float string to parse
+ * @return {Number} floating point representation of string
+ * @example
+ * <div><code>
+ * var str = '20';
+ * var diameter = float(str);
+ * ellipse(width/2, height/2, diameter, diameter);
+ * </code></div>
+ */
+p5.prototype.float = function(str) {
+ return parseFloat(str);
+};
+
+/**
+ * Converts a boolean, string, or float to its integer representation.
+ * When an array of values is passed in, then an int array of the same length
+ * is returned.
+ *
+ * @method int
+ * @param {String|Boolean|Number|Array} n value to parse
+ * @return {Number} integer representation of value
+ * @example
+ * <div class='norender'><code>
+ * print(int("10")); // 10
+ * print(int(10.31)); // 10
+ * print(int(-10)); // -10
+ * print(int(true)); // 1
+ * print(int(false)); // 0
+ * print(int([false, true, "10.3", 9.8])); // [0, 1, 10, 9]
+ * </code></div>
+ */
+p5.prototype.int = function(n, radix) {
+ if (typeof n === 'string') {
+ radix = radix || 10;
+ return parseInt(n, radix);
+ } else if (typeof n === 'number') {
+ return n | 0;
+ } else if (typeof n === 'boolean') {
+ return n ? 1 : 0;
+ } else if (n instanceof Array) {
+ return n.map(function(n) { return p5.prototype.int(n, radix); });
+ }
+};
+
+/**
+ * Converts a boolean, string or number to its string representation.
+ * When an array of values is passed in, then an array of strings of the same
+ * length is returned.
+ *
+ * @method str
+ * @param {String|Boolean|Number|Array} n value to parse
+ * @return {String} string representation of value
+ * @example
+ * <div class='norender'><code>
+ * print(str("10")); // "10"
+ * print(str(10.31)); // "10.31"
+ * print(str(-10)); // "-10"
+ * print(str(true)); // "true"
+ * print(str(false)); // "false"
+ * print(str([true, "10.3", 9.8])); // [ "true", "10.3", "9.8" ]
+ * </code></div>
+ */
+p5.prototype.str = function(n) {
+ if (n instanceof Array) {
+ return n.map(p5.prototype.str);
+ } else {
+ return String(n);
+ }
+};
+
+/**
+ * Converts a number or string to its boolean representation.
+ * For a number, any non-zero value (positive or negative) evaluates to true,
+ * while zero evaluates to false. For a string, the value "true" evaluates to
+ * true, while any other value evaluates to false. When an array of number or
+ * string values is passed in, then a array of booleans of the same length is
+ * returned.
+ *
+ * @method boolean
+ * @param {String|Boolean|Number|Array} n value to parse
+ * @return {Boolean} boolean representation of value
+ * @example
+ * <div class='norender'><code>
+ * print(boolean(0)); // false
+ * print(boolean(1)); // true
+ * print(boolean("true")); // true
+ * print(boolean("abcd")); // false
+ * print(boolean([0, 12, "true"])); // [false, true, false]
+ * </code></div>
+ */
+p5.prototype.boolean = function(n) {
+ if (typeof n === 'number') {
+ return n !== 0;
+ } else if (typeof n === 'string') {
+ return n.toLowerCase() === 'true';
+ } else if (typeof n === 'boolean') {
+ return n;
+ } else if (n instanceof Array) {
+ return n.map(p5.prototype.boolean);
+ }
+};
+
+/**
+ * Converts a number, string or boolean to its byte representation.
+ * A byte can be only a whole number between -128 and 127, so when a value
+ * outside of this range is converted, it wraps around to the corresponding
+ * byte representation. When an array of number, string or boolean values is
+ * passed in, then an array of bytes the same length is returned.
+ *
+ * @method byte
+ * @param {String|Boolean|Number|Array} n value to parse
+ * @return {Number} byte representation of value
+ * @example
+ * <div class='norender'><code>
+ * print(byte(127)); // 127
+ * print(byte(128)); // -128
+ * print(byte(23.4)); // 23
+ * print(byte("23.4")); // 23
+ * print(byte(true)); // 1
+ * print(byte([0, 255, "100"])); // [0, -1, 100]
+ * </code></div>
+ */
+p5.prototype.byte = function(n) {
+ var nn = p5.prototype.int(n, 10);
+ if (typeof nn === 'number') {
+ return ((nn + 128) % 256) - 128;
+ } else if (nn instanceof Array) {
+ return nn.map(p5.prototype.byte);
+ }
+};
+
+/**
+ * Converts a number or string to its corresponding single-character
+ * string representation. If a string parameter is provided, it is first
+ * parsed as an integer and then translated into a single-character string.
+ * When an array of number or string values is passed in, then an array of
+ * single-character strings of the same length is returned.
+ *
+ * @method char
+ * @param {String|Number|Array} n value to parse
+ * @return {String} string representation of value
+ * @example
+ * <div class='norender'><code>
+ * print(char(65)); // "A"
+ * print(char("65")); // "A"
+ * print(char([65, 66, 67])); // [ "A", "B", "C" ]
+ * print(join(char([65, 66, 67]), '')); // "ABC"
+ * </code></div>
+ */
+p5.prototype.char = function(n) {
+ if (typeof n === 'number' && !isNaN(n)) {
+ return String.fromCharCode(n);
+ } else if (n instanceof Array) {
+ return n.map(p5.prototype.char);
+ } else if (typeof n === 'string') {
+ return p5.prototype.char(parseInt(n, 10));
+ }
+};
+
+/**
+ * Converts a single-character string to its corresponding integer
+ * representation. When an array of single-character string values is passed
+ * in, then an array of integers of the same length is returned.
+ *
+ * @method unchar
+ * @param {String|Array} n value to parse
+ * @return {Number} integer representation of value
+ * @example
+ * <div class='norender'><code>
+ * print(unchar("A")); // 65
+ * print(unchar(["A", "B", "C"])); // [ 65, 66, 67 ]
+ * print(unchar(split("ABC", ""))); // [ 65, 66, 67 ]
+ * </code></div>
+ */
+p5.prototype.unchar = function(n) {
+ if (typeof n === 'string' && n.length === 1) {
+ return n.charCodeAt(0);
+ } else if (n instanceof Array) {
+ return n.map(p5.prototype.unchar);
+ }
+};
+
+/**
+ * Converts a number to a string in its equivalent hexadecimal notation. If a
+ * second parameter is passed, it is used to set the number of characters to
+ * generate in the hexadecimal notation. When an array is passed in, an
+ * array of strings in hexadecimal notation of the same length is returned.
+ *
+ * @method hex
+ * @param {Number|Array} n value to parse
+ * @return {String} hexadecimal string representation of value
+ * @example
+ * <div class='norender'><code>
+ * print(hex(255)); // "000000FF"
+ * print(hex(255, 6)); // "0000FF"
+ * print(hex([0, 127, 255], 6)); // [ "000000", "00007F", "0000FF" ]
+ * </code></div>
+ */
+p5.prototype.hex = function(n, digits) {
+ digits = (digits === undefined || digits === null) ? digits = 8 : digits;
+ if (n instanceof Array) {
+ return n.map(function(n) { return p5.prototype.hex(n, digits); });
+ } else if (typeof n === 'number') {
+ if (n < 0) {
+ n = 0xFFFFFFFF + n + 1;
+ }
+ var hex = Number(n).toString(16).toUpperCase();
+ while (hex.length < digits) {
+ hex = '0' + hex;
+ }
+ if (hex.length >= digits) {
+ hex = hex.substring(hex.length - digits, hex.length);
+ }
+ return hex;
+ }
+};
+
+/**
+ * Converts a string representation of a hexadecimal number to its equivalent
+ * integer value. When an array of strings in hexadecimal notation is passed
+ * in, an array of integers of the same length is returned.
+ *
+ * @method unhex
+ * @param {String|Array} n value to parse
+ * @return {Number} integer representation of hexadecimal value
+ * @example
+ * <div class='norender'><code>
+ * print(unhex("A")); // 10
+ * print(unhex("FF")); // 255
+ * print(unhex(["FF", "AA", "00"])); // [ 255, 170, 0 ]
+ * </code></div>
+ */
+p5.prototype.unhex = function(n) {
+ if (n instanceof Array) {
+ return n.map(p5.prototype.unhex);
+ } else {
+ return parseInt('0x' + n, 16);
+ }
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],85:[function(_dereq_,module,exports){
+/**
+ * @module Data
+ * @submodule String Functions
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+//return p5; //LM is this a mistake?
+
+/**
+ * Combines an array of Strings into one String, each separated by the
+ * character(s) used for the separator parameter. To join arrays of ints or
+ * floats, it's necessary to first convert them to Strings using nf() or
+ * nfs().
+ *
+ * @method join
+ * @param {Array} list array of Strings to be joined
+ * @param {String} separator String to be placed between each item
+ * @return {String} joined String
+ * @example
+ * <div>
+ * <code>
+ * var array = ["Hello", "world!"]
+ * var separator = " "
+ * var message = join(array, separator);
+ * text(message, 5, 50);
+ * </code>
+ * </div>
+ */
+p5.prototype.join = function(list, separator) {
+ return list.join(separator);
+};
+
+/**
+ * This function is used to apply a regular expression to a piece of text,
+ * and return matching groups (elements found inside parentheses) as a
+ * String array. If there are no matches, a null value will be returned.
+ * If no groups are specified in the regular expression, but the sequence
+ * matches, an array of length 1 (with the matched text as the first element
+ * of the array) will be returned.
+ * <br><br>
+ * To use the function, first check to see if the result is null. If the
+ * result is null, then the sequence did not match at all. If the sequence
+ * did match, an array is returned.
+ * <br><br>
+ * If there are groups (specified by sets of parentheses) in the regular
+ * expression, then the contents of each will be returned in the array.
+ * Element [0] of a regular expression match returns the entire matching
+ * string, and the match groups start at element [1] (the first group is [1],
+ * the second [2], and so on).
+ *
+ * @method match
+ * @param {String} str the String to be searched
+ * @param {String} regexp the regexp to be used for matching
+ * @return {Array} Array of Strings found
+ * @example
+ * <div>
+ * <code>
+ * var string = "Hello p5js*!"
+ * var regexp = "p5js\\*"
+ * var match = match(string, regexp);
+ * text(match, 5, 50);
+ * </code>
+ * </div>
+ */
+p5.prototype.match = function(str, reg) {
+ return str.match(reg);
+};
+
+/**
+ * This function is used to apply a regular expression to a piece of text,
+ * and return a list of matching groups (elements found inside parentheses)
+ * as a two-dimensional String array. If there are no matches, a null value
+ * will be returned. If no groups are specified in the regular expression,
+ * but the sequence matches, a two dimensional array is still returned, but
+ * the second dimension is only of length one.
+ * <br><br>
+ * To use the function, first check to see if the result is null. If the
+ * result is null, then the sequence did not match at all. If the sequence
+ * did match, a 2D array is returned.
+ * <br><br>
+ * If there are groups (specified by sets of parentheses) in the regular
+ * expression, then the contents of each will be returned in the array.
+ * Assuming a loop with counter variable i, element [i][0] of a regular
+ * expression match returns the entire matching string, and the match groups
+ * start at element [i][1] (the first group is [i][1], the second [i][2],
+ * and so on).
+ *
+ * @method matchAll
+ * @param {String} str the String to be searched
+ * @param {String} regexp the regexp to be used for matching
+ * @return {Array} 2d Array of Strings found
+ * @example
+ * <div class="norender">
+ * <code>
+ * var string = "Hello p5js*! Hello world!"
+ * var regexp = "Hello"
+ * matchAll(string, regexp);
+ * </code>
+ * </div>
+
+ */
+p5.prototype.matchAll = function(str, reg) {
+ var re = new RegExp(reg, 'g');
+ var match = re.exec(str);
+ var matches = [];
+ while (match !== null) {
+ matches.push(match);
+ // matched text: match[0]
+ // match start: match.index
+ // capturing group n: match[n]
+ match = re.exec(str);
+ }
+ return matches;
+};
+
+/**
+ * Utility function for formatting numbers into strings. There are two
+ * versions: one for formatting floats, and one for formatting ints.
+ * The values for the digits, left, and right parameters should always
+ * be positive integers.
+ *
+ * @method nf
+ * @param {Number|Array} num the Number to format
+ * @param {Number} [left] number of digits to the left of the
+ * decimal point
+ * @param {Number} [right] number of digits to the right of the
+ * decimal point
+ * @return {String|Array} formatted String
+ * @example
+ * <div>
+ * <code>
+ * function setup() {
+ * background(200);
+ * var num = 112.53106115;
+ *
+ * noStroke();
+ * fill(0);
+ * textSize(14);
+ * // Draw formatted numbers
+ * text(nf(num, 5, 2), 10, 20);
+ *
+ * text(nf(num, 4, 3), 10, 55);
+ *
+ * text(nf(num, 3, 6), 10, 85);
+ *
+ * // Draw dividing lines
+ * stroke(120);
+ * line(0, 30, width, 30);
+ * line(0, 65, width, 65);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.nf = function () {
+ if (arguments[0] instanceof Array) {
+ var a = arguments[1];
+ var b = arguments[2];
+ return arguments[0].map(function (x) {
+ return doNf(x, a, b);
+ });
+ }
+ else{
+ var typeOfFirst = Object.prototype.toString.call(arguments[0]);
+ if(typeOfFirst === '[object Arguments]'){
+ if(arguments[0].length===3){
+ return this.nf(arguments[0][0],arguments[0][1],arguments[0][2]);
+ }
+ else if(arguments[0].length===2){
+ return this.nf(arguments[0][0],arguments[0][1]);
+ }
+ else{
+ return this.nf(arguments[0][0]);
+ }
+ }
+ else {
+ return doNf.apply(this, arguments);
+ }
+ }
+};
+
+function doNf() {
+ var num = arguments[0];
+ var neg = num < 0;
+ var n = neg ? num.toString().substring(1) : num.toString();
+ var decimalInd = n.indexOf('.');
+ var intPart = decimalInd !== -1 ? n.substring(0, decimalInd) : n;
+ var decPart = decimalInd !== -1 ? n.substring(decimalInd + 1) : '';
+ var str = neg ? '-' : '';
+ if (arguments.length === 3) {
+ var decimal = '';
+ if(decimalInd !== -1 || arguments[2] - decPart.length > 0){
+ decimal = '.';
+ }
+ if (decPart.length > arguments[2]) {
+ decPart = decPart.substring(0, arguments[2]);
+ }
+ for (var i = 0; i < arguments[1] - intPart.length; i++) {
+ str += '0';
+ }
+ str += intPart;
+ str += decimal;
+ str += decPart;
+ for (var j = 0; j < arguments[2] - decPart.length; j++) {
+ str += '0';
+ }
+ return str;
+ }
+ else {
+ for (var k = 0; k < Math.max(arguments[1] - intPart.length, 0); k++) {
+ str += '0';
+ }
+ str += n;
+ return str;
+ }
+}
+
+/**
+ * Utility function for formatting numbers into strings and placing
+ * appropriate commas to mark units of 1000. There are two versions: one
+ * for formatting ints, and one for formatting an array of ints. The value
+ * for the right parameter should always be a positive integer.
+ *
+ * @method nfc
+ * @param {Number|Array} num the Number to format
+ * @param {Number} [right] number of digits to the right of the
+ * decimal point
+ * @return {String|Array} formatted String
+ * @example
+ * <div>
+ * <code>
+ * function setup() {
+ * background(200);
+ * var num = 11253106.115;
+ * var numArr = new Array(1,1,2);
+ *
+ * noStroke();
+ * fill(0);
+ * textSize(12);
+ *
+ * // Draw formatted numbers
+ * text(nfc(num, 4, 2), 10, 30);
+ * text(nfc(numArr, 2, 1), 10, 80);
+ *
+ * // Draw dividing line
+ * stroke(120);
+ * line(0, 50, width, 50);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.nfc = function () {
+ if (arguments[0] instanceof Array) {
+ var a = arguments[1];
+ return arguments[0].map(function (x) {
+ return doNfc(x, a);
+ });
+ } else {
+ return doNfc.apply(this, arguments);
+ }
+};
+function doNfc() {
+ var num = arguments[0].toString();
+ var dec = num.indexOf('.');
+ var rem = dec !== -1 ? num.substring(dec) : '';
+ var n = dec !== -1 ? num.substring(0, dec) : num;
+ n = n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
+ if (arguments[1] === 0) {
+ rem = '';
+ }
+ else if(arguments[1] !== undefined){
+ if(arguments[1] > rem.length){
+ rem+= dec === -1 ? '.' : '';
+ var len = arguments[1] - rem.length + 1;
+ for(var i =0; i< len; i++){
+ rem += '0';
+ }
+ }
+ else{
+ rem = rem.substring(0, arguments[1] + 1);
+ }
+ }
+ return n + rem;
+}
+
+/**
+ * Utility function for formatting numbers into strings. Similar to nf() but
+ * puts a "+" in front of positive numbers and a "-" in front of negative
+ * numbers. There are two versions: one for formatting floats, and one for
+ * formatting ints. The values for left, and right parameters
+ * should always be positive integers.
+ *
+ * @method nfp
+ * @param {Number|Array} num the Number to format
+ * @param {Number} [left] number of digits to the left of the decimal
+ * point
+ * @param {Number} [right] number of digits to the right of the
+ * decimal point
+ * @return {String|Array} formatted String
+ * @example
+ * <div>
+ * <code>
+ * function setup() {
+ * background(200);
+ * var num1 = 11253106.115;
+ * var num2 = -11253106.115;
+ *
+ * noStroke();
+ * fill(0);
+ * textSize(12);
+ *
+ * // Draw formatted numbers
+ * text(nfp(num1, 4, 2), 10, 30);
+ * text(nfp(num2, 4, 2), 10, 80);
+ *
+ * // Draw dividing line
+ * stroke(120);
+ * line(0, 50, width, 50);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.nfp = function() {
+ var nfRes = this.nf.apply(this, arguments);
+ if (nfRes instanceof Array) {
+ return nfRes.map(addNfp);
+ } else {
+ return addNfp(nfRes);
+ }
+};
+
+function addNfp() {
+ return (
+ parseFloat(arguments[0]) > 0) ?
+ '+'+arguments[0].toString() :
+ arguments[0].toString();
+}
+
+/**
+ * Utility function for formatting numbers into strings. Similar to nf() but
+ * puts a " " (space) in front of positive numbers and a "-" in front of
+ * negative numbers. There are two versions: one for formatting floats, and
+ * one for formatting ints. The values for the digits, left, and right
+ * parameters should always be positive integers.
+ *
+ * @method nfs
+ * @param {Number|Array} num the Number to format
+ * @param {Number} [left] number of digits to the left of the decimal
+ * point
+ * @param {Number} [right] number of digits to the right of the
+ * decimal point
+ * @return {String|Array} formatted String
+ * @example
+ * <div>
+ * <code>
+ * function setup() {
+ * background(200);
+ * var num1 = 11253106.115;
+ * var num2 = -11253106.115;
+ *
+ * noStroke();
+ * fill(0);
+ * textSize(12);
+ * // Draw formatted numbers
+ * text(nfs(num1, 4, 2), 10, 30);
+ *
+ * text(nfs(num2, 4, 2), 10, 80);
+ *
+ * // Draw dividing line
+ * stroke(120);
+ * line(0, 50, width, 50);
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.nfs = function() {
+ var nfRes = this.nf.apply(this, arguments);
+ if (nfRes instanceof Array) {
+ return nfRes.map(addNfs);
+ } else {
+ return addNfs(nfRes);
+ }
+};
+
+function addNfs() {
+ return (
+ parseFloat(arguments[0]) > 0) ?
+ ' '+arguments[0].toString() :
+ arguments[0].toString();
+}
+
+/**
+ * The split() function maps to String.split(), it breaks a String into
+ * pieces using a character or string as the delimiter. The delim parameter
+ * specifies the character or characters that mark the boundaries between
+ * each piece. A String[] array is returned that contains each of the pieces.
+ *
+ * The splitTokens() function works in a similar fashion, except that it
+ * splits using a range of characters instead of a specific character or
+ * sequence.
+ *
+ * @method split
+ * @param {String} value the String to be split
+ * @param {String} delim the String used to separate the data
+ * @return {Array} Array of Strings
+ * @example
+ * <div>
+ * <code>
+ * var names = "Pat,Xio,Alex"
+ * var splitString = split(names, ",");
+ * text(splitString[0], 5, 30);
+ * text(splitString[1], 5, 50);
+ * text(splitString[2], 5, 70);
+ * </code>
+ * </div>
+ */
+p5.prototype.split = function(str, delim) {
+ return str.split(delim);
+};
+
+/**
+ * The splitTokens() function splits a String at one or many character
+ * delimiters or "tokens." The delim parameter specifies the character or
+ * characters to be used as a boundary.
+ * <br><br>
+ * If no delim characters are specified, any whitespace character is used to
+ * split. Whitespace characters include tab (\t), line feed (\n), carriage
+ * return (\r), form feed (\f), and space.
+ *
+ * @method splitTokens
+ * @param {String} value the String to be split
+ * @param {String} [delim] list of individual Strings that will be used as
+ * separators
+ * @return {Array} Array of Strings
+ * @example
+ * <div class = "norender">
+ * <code>
+ * function setup() {
+ * var myStr = "Mango, Banana, Lime";
+ * var myStrArr = splitTokens(myStr, ",");
+ *
+ * print(myStrArr); // prints : ["Mango"," Banana"," Lime"]
+ * }
+ * </code>
+ * </div>
+ */
+p5.prototype.splitTokens = function() {
+ var d,sqo,sqc,str;
+ str = arguments[1];
+ if (arguments.length > 1) {
+ sqc = /\]/g.exec(str);
+ sqo = /\[/g.exec(str);
+ if ( sqo && sqc ) {
+ str = str.slice(0, sqc.index) + str.slice(sqc.index+1);
+ sqo = /\[/g.exec(str);
+ str = str.slice(0, sqo.index) + str.slice(sqo.index+1);
+ d = new RegExp('[\\['+str+'\\]]','g');
+ } else if ( sqc ) {
+ str = str.slice(0, sqc.index) + str.slice(sqc.index+1);
+ d = new RegExp('[' + str + '\\]]', 'g');
+ } else if(sqo) {
+ str = str.slice(0, sqo.index) + str.slice(sqo.index+1);
+ d = new RegExp('[' + str + '\\[]', 'g');
+ } else {
+ d = new RegExp('[' + str + ']', 'g');
+ }
+ } else {
+ d = /\s/g;
+ }
+ return arguments[0].split(d).filter(function(n){return n;});
+};
+
+/**
+ * Removes whitespace characters from the beginning and end of a String. In
+ * addition to standard whitespace characters such as space, carriage return,
+ * and tab, this function also removes the Unicode "nbsp" character.
+ *
+ * @method trim
+ * @param {String|Array} [str] a String or Array of Strings to be trimmed
+ * @return {String|Array} a trimmed String or Array of Strings
+ * @example
+ * <div>
+ * <code>
+ * var string = trim(" No new lines\n ");
+ * text(string +" here", 2, 50);
+ * </code>
+ * </div>
+ */
+p5.prototype.trim = function(str) {
+ if (str instanceof Array) {
+ return str.map(this.trim);
+ } else {
+ return str.trim();
+ }
+};
+
+module.exports = p5;
+
+},{"../core/core":48}],86:[function(_dereq_,module,exports){
+/**
+ * @module Input
+ * @submodule Time & Date
+ * @for p5
+ * @requires core
+ */
+
+'use strict';
+
+var p5 = _dereq_('../core/core');
+
+/**
+ * p5.js communicates with the clock on your computer. The day() function
+ * returns the current day as a value from 1 - 31.
+ *
+ * @method day
+ * @return {Number} the current day
+ * @example
+ * <div>
+ * <code>
+ * var day = day();
+ * text("Current day: \n" + day, 5, 50);
+ * </code>
+ * </div>
+ */
+p5.prototype.day = function() {
+ return new Date().getDate();
+};
+
+/**
+ * p5.js communicates with the clock on your computer. The hour() function
+ * returns the current hour as a value from 0 - 23.
+ *
+ * @method hour
+ * @return {Number} the current hour
+ * @example
+ * <div>
+ * <code>
+ * var hour = hour();
+ * text("Current hour:\n" + hour, 5, 50);
+ * </code>
+ * </div>
+ */
+p5.prototype.hour = function() {
+ return new Date().getHours();
+};
+
+/**
+ * p5.js communicates with the clock on your computer. The minute() function
+ * returns the current minute as a value from 0 - 59.
+ *
+ * @method minute
+ * @return {Number} the current minute
+ * @example
+ * <div>
+ * <code>
+ * var minute = minute();
+ * text("Current minute: \n" + minute, 5, 50);
+ * </code>
+ * </div>
+ */
+p5.prototype.minute = function() {
+ return new Date().getMinutes();
+};
+
+/**
+ * Returns the number of milliseconds (thousandths of a second) since
+ * starting the program. This information is often used for timing events and
+ * animation sequences.
+ *
+ * @method millis
+ * @return {Number} the number of milliseconds since starting the program
+ * @example
+ * <div>
+ * <code>
+ * var millisecond = millis();
+ * text("Milliseconds \nrunning: \n" + millisecond, 5, 40);
+ * </code>
+ * </div>
+ */
+p5.prototype.millis = function() {
+ return window.performance.now();
+};
+
+/**
+ * p5.js communicates with the clock on your computer. The month() function
+ * returns the current month as a value from 1 - 12.
+ *
+ * @method month
+ * @return {Number} the current month
+ * @example
+ * <div>
+ * <code>
+ * var month = month();
+ * text("Current month: \n" + month, 5, 50);
+ * </code>
+ * </div>
+ */
+p5.prototype.month = function() {
+ return new Date().getMonth() + 1; //January is 0!
+};
+
+/**
+ * p5.js communicates with the clock on your computer. The second() function
+ * returns the current second as a value from 0 - 59.
+ *
+ * @method second
+ * @return {Number} the current second
+ * @example
+ * <div>
+ * <code>
+ * var second = second();
+ * text("Current second: \n" + second, 5, 50);
+ * </code>
+ * </div>
+ */
+p5.prototype.second = function() {
+ return new Date().getSeconds();
+};
+
+/**
+ * p5.js communicates with the clock on your computer. The year() function
+ * returns the current year as an integer (2014, 2015, 2016, etc).
+ *
+ * @method year
+ * @return {Number} the current year
+ * @example
+ * <div>
+ * <code>
+ * var year = year();
+ * text("Current year: \n" + year, 5, 50);
+ * </code>
+ * </div>
+ */
+p5.prototype.year = function() {
+ return new Date().getFullYear();
+};
+
+module.exports = p5;
+
+},{"../core/core":48}]},{},[39])(39)
+}); \ No newline at end of file
diff --git a/js/processing.js b/js/processing.js
new file mode 100644
index 0000000..4cebd06
--- /dev/null
+++ b/js/processing.js
@@ -0,0 +1,21748 @@
+(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+// build script for generating processing.js
+
+var Browser = {
+ isDomPresent: true,
+ navigator: navigator,
+ window: window,
+ document: document,
+ ajax: function(url) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", url, false);
+ if (xhr.overrideMimeType) {
+ xhr.overrideMimeType("text/plain");
+ }
+ xhr.setRequestHeader("If-Modified-Since", "Fri, 01 Jan 1960 00:00:00 GMT");
+ xhr.send(null);
+ // failed request?
+ if (xhr.status !== 200 && xhr.status !== 0) { throw ("XMLHttpRequest failed, status code " + xhr.status); }
+ return xhr.responseText;
+ }
+};
+
+window.Processing = require('./src/')(Browser);
+
+},{"./src/":28}],2:[function(require,module,exports){
+module.exports={
+ "name": "processing-js",
+ "version": "1.4.16",
+ "author": "Processing.js",
+ "repository": {
+ "type": "git",
+ "url": "git@github.com/processing-js/processing-js.git"
+ },
+ "main": "processing.min.js",
+ "bugs": "https://github.com/processing-js/processing-js/issues",
+ "devDependencies": {
+ "argv": "~0.0.2",
+ "browserify": "^11.0.1",
+ "express": "~3.3.3",
+ "node-minify": "~0.7.3",
+ "nunjucks": "~0.1.9",
+ "open": "0.0.3",
+ "grunt": "~0.4.1",
+ "grunt-cli": "~0.1.8",
+ "grunt-contrib-jshint": "~0.4.3"
+ },
+ "scripts": {
+ "test": "node test",
+ "start": "browserify build.js -o processing.js && node minify"
+ },
+ "license": "MIT"
+}
+
+},{}],3:[function(require,module,exports){
+/**
+* A ObjectIterator is an iterator wrapper for objects. If passed object contains
+* the iterator method, the object instance will be replaced by the result returned by
+* this method call. If passed object is an array, the ObjectIterator instance iterates
+* through its items.
+*
+* @param {Object} obj The object to be iterated.
+*/
+module.exports = function ObjectIterator(obj) {
+ if (obj instanceof Array) {
+ // iterate through array items
+ var index = -1;
+ this.hasNext = function() {
+ return ++index < obj.length;
+ };
+ this.next = function() {
+ return obj[index];
+ };
+ } else if (obj.iterator instanceof Function) {
+ return obj.iterator();
+ } else {
+ throw "Unable to iterate: " + obj;
+ }
+};
+
+},{}],4:[function(require,module,exports){
+/**
+ * Processing.js environment constants
+ */
+module.exports = {
+ X: 0,
+ Y: 1,
+ Z: 2,
+
+ R: 3,
+ G: 4,
+ B: 5,
+ A: 6,
+
+ U: 7,
+ V: 8,
+
+ NX: 9,
+ NY: 10,
+ NZ: 11,
+
+ EDGE: 12,
+
+ // Stroke
+ SR: 13,
+ SG: 14,
+ SB: 15,
+ SA: 16,
+
+ SW: 17,
+
+ // Transformations (2D and 3D)
+ TX: 18,
+ TY: 19,
+ TZ: 20,
+
+ VX: 21,
+ VY: 22,
+ VZ: 23,
+ VW: 24,
+
+ // Material properties
+ AR: 25,
+ AG: 26,
+ AB: 27,
+
+ DR: 3,
+ DG: 4,
+ DB: 5,
+ DA: 6,
+
+ SPR: 28,
+ SPG: 29,
+ SPB: 30,
+
+ SHINE: 31,
+
+ ER: 32,
+ EG: 33,
+ EB: 34,
+
+ BEEN_LIT: 35,
+
+ VERTEX_FIELD_COUNT: 36,
+
+ // Renderers
+ P2D: 1,
+ JAVA2D: 1,
+ WEBGL: 2,
+ P3D: 2,
+ OPENGL: 2,
+ PDF: 0,
+ DXF: 0,
+
+ // Platform IDs
+ OTHER: 0,
+ WINDOWS: 1,
+ MAXOSX: 2,
+ LINUX: 3,
+
+ EPSILON: 0.0001,
+
+ MAX_FLOAT: 3.4028235e+38,
+ MIN_FLOAT: -3.4028235e+38,
+ MAX_INT: 2147483647,
+ MIN_INT: -2147483648,
+
+ PI: Math.PI,
+ TWO_PI: 2 * Math.PI,
+ TAU: 2 * Math.PI,
+ HALF_PI: Math.PI / 2,
+ THIRD_PI: Math.PI / 3,
+ QUARTER_PI: Math.PI / 4,
+
+ DEG_TO_RAD: Math.PI / 180,
+ RAD_TO_DEG: 180 / Math.PI,
+
+ WHITESPACE: " \t\n\r\f\u00A0",
+
+ // Color modes
+ RGB: 1,
+ ARGB: 2,
+ HSB: 3,
+ ALPHA: 4,
+ CMYK: 5,
+
+ // Image file types
+ TIFF: 0,
+ TARGA: 1,
+ JPEG: 2,
+ GIF: 3,
+
+ // Filter/convert types
+ BLUR: 11,
+ GRAY: 12,
+ INVERT: 13,
+ OPAQUE: 14,
+ POSTERIZE: 15,
+ THRESHOLD: 16,
+ ERODE: 17,
+ DILATE: 18,
+
+ // Blend modes
+ REPLACE: 0,
+ BLEND: 1 << 0,
+ ADD: 1 << 1,
+ SUBTRACT: 1 << 2,
+ LIGHTEST: 1 << 3,
+ DARKEST: 1 << 4,
+ DIFFERENCE: 1 << 5,
+ EXCLUSION: 1 << 6,
+ MULTIPLY: 1 << 7,
+ SCREEN: 1 << 8,
+ OVERLAY: 1 << 9,
+ HARD_LIGHT: 1 << 10,
+ SOFT_LIGHT: 1 << 11,
+ DODGE: 1 << 12,
+ BURN: 1 << 13,
+
+ // Color component bit masks
+ ALPHA_MASK: 0xff000000,
+ RED_MASK: 0x00ff0000,
+ GREEN_MASK: 0x0000ff00,
+ BLUE_MASK: 0x000000ff,
+
+ // Projection matrices
+ CUSTOM: 0,
+ ORTHOGRAPHIC: 2,
+ PERSPECTIVE: 3,
+
+ // Shapes
+ POINT: 2,
+ POINTS: 2,
+ LINE: 4,
+ LINES: 4,
+ TRIANGLE: 8,
+ TRIANGLES: 9,
+ TRIANGLE_STRIP: 10,
+ TRIANGLE_FAN: 11,
+ QUAD: 16,
+ QUADS: 16,
+ QUAD_STRIP: 17,
+ POLYGON: 20,
+ PATH: 21,
+ RECT: 30,
+ ELLIPSE: 31,
+ ARC: 32,
+ SPHERE: 40,
+ BOX: 41,
+
+ GROUP: 0,
+ PRIMITIVE: 1,
+ //PATH: 21, // shared with Shape PATH
+ GEOMETRY: 3,
+
+ // Shape Vertex
+ VERTEX: 0,
+ BEZIER_VERTEX: 1,
+ CURVE_VERTEX: 2,
+ BREAK: 3,
+ CLOSESHAPE: 4,
+
+ // Shape closing modes
+ OPEN: 1,
+ CLOSE: 2,
+
+ // Shape drawing modes
+ CORNER: 0, // Draw mode convention to use (x, y) to (width, height)
+ CORNERS: 1, // Draw mode convention to use (x1, y1) to (x2, y2) coordinates
+ RADIUS: 2, // Draw mode from the center, and using the radius
+ CENTER_RADIUS: 2, // Deprecated! Use RADIUS instead
+ CENTER: 3, // Draw from the center, using second pair of values as the diameter
+ DIAMETER: 3, // Synonym for the CENTER constant. Draw from the center
+ CENTER_DIAMETER: 3, // Deprecated! Use DIAMETER instead
+
+ // Text vertical alignment modes
+ BASELINE: 0, // Default vertical alignment for text placement
+ TOP: 101, // Align text to the top
+ BOTTOM: 102, // Align text from the bottom, using the baseline
+
+ // UV Texture coordinate modes
+ NORMAL: 1,
+ NORMALIZED: 1,
+ IMAGE: 2,
+
+ // Text placement modes
+ MODEL: 4,
+ SHAPE: 5,
+
+ // Stroke modes
+ SQUARE: 'butt',
+ ROUND: 'round',
+ PROJECT: 'square',
+ MITER: 'miter',
+ BEVEL: 'bevel',
+
+ // Lighting modes
+ AMBIENT: 0,
+ DIRECTIONAL: 1,
+ //POINT: 2, Shared with Shape constant
+ SPOT: 3,
+
+ // Key constants
+
+ // Both key and keyCode will be equal to these values
+ BACKSPACE: 8,
+ TAB: 9,
+ ENTER: 10,
+ RETURN: 13,
+ ESC: 27,
+ DELETE: 127,
+ CODED: 0xffff,
+
+ // p.key will be CODED and p.keyCode will be this value
+ SHIFT: 16,
+ CONTROL: 17,
+ ALT: 18,
+ CAPSLK: 20,
+ PGUP: 33,
+ PGDN: 34,
+ END: 35,
+ HOME: 36,
+ LEFT: 37,
+ UP: 38,
+ RIGHT: 39,
+ DOWN: 40,
+ F1: 112,
+ F2: 113,
+ F3: 114,
+ F4: 115,
+ F5: 116,
+ F6: 117,
+ F7: 118,
+ F8: 119,
+ F9: 120,
+ F10: 121,
+ F11: 122,
+ F12: 123,
+ NUMLK: 144,
+ META: 157,
+ INSERT: 155,
+
+ // Cursor types
+ ARROW: 'default',
+ CROSS: 'crosshair',
+ HAND: 'pointer',
+ MOVE: 'move',
+ TEXT: 'text',
+ WAIT: 'wait',
+ NOCURSOR: "url('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='), auto",
+
+ // Hints
+ DISABLE_OPENGL_2X_SMOOTH: 1,
+ ENABLE_OPENGL_2X_SMOOTH: -1,
+ ENABLE_OPENGL_4X_SMOOTH: 2,
+ ENABLE_NATIVE_FONTS: 3,
+ DISABLE_DEPTH_TEST: 4,
+ ENABLE_DEPTH_TEST: -4,
+ ENABLE_DEPTH_SORT: 5,
+ DISABLE_DEPTH_SORT: -5,
+ DISABLE_OPENGL_ERROR_REPORT: 6,
+ ENABLE_OPENGL_ERROR_REPORT: -6,
+ ENABLE_ACCURATE_TEXTURES: 7,
+ DISABLE_ACCURATE_TEXTURES: -7,
+ HINT_COUNT: 10,
+
+ // PJS defined constants
+ SINCOS_LENGTH: 720, // every half degree
+ PRECISIONB: 15, // fixed point precision is limited to 15 bits!!
+ PRECISIONF: 1 << 15,
+ PREC_MAXVAL: (1 << 15) - 1,
+ PREC_ALPHA_SHIFT: 24 - 15,
+ PREC_RED_SHIFT: 16 - 15,
+ NORMAL_MODE_AUTO: 0,
+ NORMAL_MODE_SHAPE: 1,
+ NORMAL_MODE_VERTEX: 2,
+ MAX_LIGHTS: 8
+};
+
+},{}],5:[function(require,module,exports){
+// the logger for println()
+module.exports = function PjsConsole(document) {
+ var e = { BufferMax: 200 },
+ style = document.createElement("style"),
+ added = false;
+
+ style.textContent = [
+ ".pjsconsole.hidden {",
+ " display: none!important;",
+ "}"
+ ].join('\n');
+
+ e.wrapper = document.createElement("div");
+ style.textContent += [
+ "",
+ ".pjsconsole {",
+ " opacity: .75;",
+ " display: block;",
+ " position: fixed;",
+ " bottom: 0px;",
+ " left: 0px;",
+ " right: 0px;",
+ " height: 50px;",
+ " background-color: #aaa;",
+ "}"
+ ].join('\n');
+ e.wrapper.classList.add("pjsconsole");
+
+ e.dragger = document.createElement("div");
+ style.textContent += [
+ "",
+ ".pjsconsole .dragger {",
+ " display: block;",
+ " border: 3px black raised;",
+ " cursor: n-resize;",
+ " position: absolute;",
+ " top: 0px;",
+ " left: 0px;",
+ " right: 0px;",
+ " height: 5px;",
+ " background-color: #333;",
+ "}"
+ ].join('\n');
+ e.dragger.classList.add("dragger");
+
+ e.closer = document.createElement("div");
+ style.textContent += [
+ "",
+ ".pjsconsole .closer {",
+ " opacity: .5;",
+ " display: block;",
+ " border: 3px black raised;",
+ " position: absolute;",
+ " top: 10px;",
+ " right: 30px;",
+ " height: 20px;",
+ " width: 20px;",
+ " background-color: #ddd;",
+ " color: #000;",
+ " line-height: 20px;",
+ " text-align: center;",
+ " cursor: pointer",
+ "}"
+ ].join('\n');
+ e.closer.classList.add("closer");
+ e.closer.innerHTML = "&#10006;";
+
+ e.javaconsole = document.createElement("div");
+ style.textContent += [
+ "",
+ ".pjsconsole .console {",
+ " overflow-x: auto;",
+ " display: block;",
+ " position: absolute;",
+ " left: 10px;",
+ " right: 0px;",
+ " bottom: 5px;",
+ " top: 10px;",
+ " overflow-y: scroll;",
+ " height: 40px;",
+ "}"
+ ].join('\n');
+ e.javaconsole.setAttribute("class", "console");
+
+ e.wrapper.appendChild(e.dragger);
+ e.wrapper.appendChild(e.javaconsole);
+ e.wrapper.appendChild(e.closer);
+
+ e.dragger.onmousedown = function (t) {
+ e.divheight = e.wrapper.style.height;
+ if (document.selection) document.selection.empty();
+ else window.getSelection().removeAllRanges();
+ var n = t.screenY;
+ window.onmousemove = function (t) {
+ e.wrapper.style.height = parseFloat(e.divheight) + (n - t.screenY) + "px";
+ e.javaconsole.style.height = parseFloat(e.divheight) + (n - t.screenY) - 10 + "px";
+ };
+ window.onmouseup = function (t) {
+ if (document.selection) document.selection.empty();
+ else window.getSelection().removeAllRanges();
+ e.wrapper.style.height = parseFloat(e.divheight) + (n - t.screenY) + "px";
+ e.javaconsole.style.height = parseFloat(e.divheight) + (n - t.screenY) - 10 + "px";
+ window.onmousemove = null;
+ window.onmouseup = null;
+ };
+ };
+
+ e.BufferArray = [];
+
+ e.print = e.log = function () {
+ var args = Array.prototype.slice.call(arguments);
+ t = args.map(function(t, idx) { return t + (idx+1 === args.length ? "" : " "); }).join('');
+ if (e.BufferArray[e.BufferArray.length - 1]) e.BufferArray[e.BufferArray.length - 1] += (t) + "";
+ else e.BufferArray.push(t);
+ e.javaconsole.innerHTML = e.BufferArray.join('');
+ e.showconsole();
+ };
+
+ e.println = function () {
+ if(!added) {
+ document.body.appendChild(style);
+ document.body.appendChild(e.wrapper);
+ added = true;
+ }
+ var args = Array.prototype.slice.call(arguments);
+ args.push('<br>');
+ e.print.apply(e, args);
+ if (e.BufferArray.length > e.BufferMax) {
+ e.BufferArray.splice(0, 1);
+ } else {
+ e.javaconsole.scrollTop = e.javaconsole.scrollHeight;
+ }
+ };
+
+ e.showconsole = function () { e.wrapper.classList.remove("hidden"); };
+ e.hideconsole = function () { e.wrapper.classList.add("hidden"); };
+
+ e.closer.onclick = function () { e.hideconsole(); };
+
+ e.hideconsole();
+
+ return e;
+};
+
+},{}],6:[function(require,module,exports){
+/**
+ * Processing.js default scope
+ */
+module.exports = function(options) {
+
+ // Building defaultScope. Changing of the prototype protects
+ // internal Processing code from the changes in defaultScope
+ function DefaultScope() {}
+ DefaultScope.prototype = options.PConstants;
+
+ var defaultScope = new DefaultScope();
+
+ // copy over all known Object types and helper objects
+ Object.keys(options).forEach(function(prop) {
+ defaultScope[prop] = options[prop];
+ });
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Class inheritance helper methods
+ ////////////////////////////////////////////////////////////////////////////
+
+ defaultScope.defineProperty = function(obj, name, desc) {
+ if("defineProperty" in Object) {
+ Object.defineProperty(obj, name, desc);
+ } else {
+ if (desc.hasOwnProperty("get")) {
+ obj.__defineGetter__(name, desc.get);
+ }
+ if (desc.hasOwnProperty("set")) {
+ obj.__defineSetter__(name, desc.set);
+ }
+ }
+ };
+
+ /**
+ * class overloading, part 1
+ */
+ function overloadBaseClassFunction(object, name, basefn) {
+ if (!object.hasOwnProperty(name) || typeof object[name] !== 'function') {
+ // object method is not a function or just inherited from Object.prototype
+ object[name] = basefn;
+ return;
+ }
+ var fn = object[name];
+ if ("$overloads" in fn) {
+ // the object method already overloaded (see defaultScope.addMethod)
+ // let's just change a fallback method
+ fn.$defaultOverload = basefn;
+ return;
+ }
+ if (!("$overloads" in basefn) && fn.length === basefn.length) {
+ // special case when we just overriding the method
+ return;
+ }
+ var overloads, defaultOverload;
+ if ("$overloads" in basefn) {
+ // let's inherit base class overloads to speed up things
+ overloads = basefn.$overloads.slice(0);
+ overloads[fn.length] = fn;
+ defaultOverload = basefn.$defaultOverload;
+ } else {
+ overloads = [];
+ overloads[basefn.length] = basefn;
+ overloads[fn.length] = fn;
+ defaultOverload = fn;
+ }
+ var hubfn = function() {
+ var fn = hubfn.$overloads[arguments.length] ||
+ ("$methodArgsIndex" in hubfn && arguments.length > hubfn.$methodArgsIndex ?
+ hubfn.$overloads[hubfn.$methodArgsIndex] : null) ||
+ hubfn.$defaultOverload;
+ return fn.apply(this, arguments);
+ };
+ hubfn.$overloads = overloads;
+ if ("$methodArgsIndex" in basefn) {
+ hubfn.$methodArgsIndex = basefn.$methodArgsIndex;
+ }
+ hubfn.$defaultOverload = defaultOverload;
+ hubfn.name = name;
+ object[name] = hubfn;
+ }
+
+ /**
+ * class overloading, part 2
+ */
+
+ function extendClass(subClass, baseClass) {
+ function extendGetterSetter(propertyName) {
+ defaultScope.defineProperty(subClass, propertyName, {
+ get: function() {
+ return baseClass[propertyName];
+ },
+ set: function(v) {
+ baseClass[propertyName]=v;
+ },
+ enumerable: true
+ });
+ }
+
+ var properties = [];
+ for (var propertyName in baseClass) {
+ if (typeof baseClass[propertyName] === 'function') {
+ overloadBaseClassFunction(subClass, propertyName, baseClass[propertyName]);
+ } else if(propertyName.charAt(0) !== "$" && !(propertyName in subClass)) {
+ // Delaying the properties extension due to the IE9 bug (see #918).
+ properties.push(propertyName);
+ }
+ }
+ while (properties.length > 0) {
+ extendGetterSetter(properties.shift());
+ }
+
+ subClass.$super = baseClass;
+ }
+
+ /**
+ * class overloading, part 3
+ */
+ defaultScope.extendClassChain = function(base) {
+ var path = [base];
+ for (var self = base.$upcast; self; self = self.$upcast) {
+ extendClass(self, base);
+ path.push(self);
+ base = self;
+ }
+ while (path.length > 0) {
+ path.pop().$self=base;
+ }
+ };
+
+ // static
+ defaultScope.extendStaticMembers = function(derived, base) {
+ extendClass(derived, base);
+ };
+
+ // interface
+ defaultScope.extendInterfaceMembers = function(derived, base) {
+ extendClass(derived, base);
+ };
+
+ /**
+ * Java methods and JavaScript functions differ enough that
+ * we need a special function to make sure it all links up
+ * as classical hierarchical class chains.
+ */
+ defaultScope.addMethod = function(object, name, fn, hasMethodArgs) {
+ var existingfn = object[name];
+ if (existingfn || hasMethodArgs) {
+ var args = fn.length;
+ // builds the overload methods table
+ if ("$overloads" in existingfn) {
+ existingfn.$overloads[args] = fn;
+ } else {
+ var hubfn = function() {
+ var fn = hubfn.$overloads[arguments.length] ||
+ ("$methodArgsIndex" in hubfn && arguments.length > hubfn.$methodArgsIndex ?
+ hubfn.$overloads[hubfn.$methodArgsIndex] : null) ||
+ hubfn.$defaultOverload;
+ return fn.apply(this, arguments);
+ };
+ var overloads = [];
+ if (existingfn) {
+ overloads[existingfn.length] = existingfn;
+ }
+ overloads[args] = fn;
+ hubfn.$overloads = overloads;
+ hubfn.$defaultOverload = existingfn || fn;
+ if (hasMethodArgs) {
+ hubfn.$methodArgsIndex = args;
+ }
+ hubfn.name = name;
+ object[name] = hubfn;
+ }
+ } else {
+ object[name] = fn;
+ }
+ };
+
+ // internal helper function
+ function isNumericalJavaType(type) {
+ if (typeof type !== "string") {
+ return false;
+ }
+ return ["byte", "int", "char", "color", "float", "long", "double"].indexOf(type) !== -1;
+ }
+
+ /**
+ * Java's arrays are pre-filled when declared with
+ * an initial size, but no content. JS arrays are not.
+ */
+ defaultScope.createJavaArray = function(type, bounds) {
+ var result = null,
+ defaultValue = null;
+ if (typeof type === "string") {
+ if (type === "boolean") {
+ defaultValue = false;
+ } else if (isNumericalJavaType(type)) {
+ defaultValue = 0;
+ }
+ }
+ if (typeof bounds[0] === 'number') {
+ var itemsCount = 0 | bounds[0];
+ if (bounds.length <= 1) {
+ result = [];
+ result.length = itemsCount;
+ for (var i = 0; i < itemsCount; ++i) {
+ result[i] = defaultValue;
+ }
+ } else {
+ result = [];
+ var newBounds = bounds.slice(1);
+ for (var j = 0; j < itemsCount; ++j) {
+ result.push(defaultScope.createJavaArray(type, newBounds));
+ }
+ }
+ }
+ return result;
+ };
+
+ // screenWidth and screenHeight are shared by all instances.
+ // and return the width/height of the browser's viewport.
+ defaultScope.defineProperty(defaultScope, 'screenWidth',
+ { get: function() { return window.innerWidth; } });
+
+ defaultScope.defineProperty(defaultScope, 'screenHeight',
+ { get: function() { return window.innerHeight; } });
+
+ return defaultScope;
+};
+
+},{}],7:[function(require,module,exports){
+/**
+ * Finalise the Processing.js object.
+ */
+module.exports = function finalizeProcessing(Processing, options) {
+
+ // unpack options
+ var window = options.window,
+ document = options.document,
+ XMLHttpRequest = window.XMLHttpRequest,
+ noop = options.noop,
+ isDOMPresent = options.isDOMPresent,
+ version = options.version,
+ undef;
+
+ // versioning
+ Processing.version = (version ? version : "@DEV-VERSION@");
+
+ // Share lib space
+ Processing.lib = {};
+
+ /**
+ * External libraries can be added to the global Processing
+ * objects with the `registerLibrary` function.
+ */
+ Processing.registerLibrary = function(name, library) {
+ Processing.lib[name] = library;
+ if(library.hasOwnProperty("init")) {
+ library.init(defaultScope);
+ }
+ };
+
+ /**
+ * This is the object that acts as our version of PApplet.
+ * This can be called as Processing.Sketch() or as
+ * Processing.Sketch(function) in which case the function
+ * must be an already-compiled-to-JS sketch function.
+ */
+ Processing.Sketch = function(attachFunction) {
+ this.attachFunction = attachFunction;
+ this.options = {
+ pauseOnBlur: false,
+ globalKeyEvents: false
+ };
+
+ /* Optional Sketch event hooks:
+ * onLoad - parsing/preloading is done, before sketch starts
+ * onSetup - setup() has been called, before first draw()
+ * onPause - noLoop() has been called, pausing draw loop
+ * onLoop - loop() has been called, resuming draw loop
+ * onFrameStart - draw() loop about to begin
+ * onFrameEnd - draw() loop finished
+ * onExit - exit() done being called
+ */
+ this.onLoad = noop;
+ this.onSetup = noop;
+ this.onPause = noop;
+ this.onLoop = noop;
+ this.onFrameStart = noop;
+ this.onFrameEnd = noop;
+ this.onExit = noop;
+
+ this.params = {};
+ this.imageCache = {
+ pending: 0,
+ images: {},
+ // Opera requires special administration for preloading
+ operaCache: {},
+ // Specify an optional img arg if the image is already loaded in the DOM,
+ // otherwise href will get loaded.
+ add: function(href, img) {
+ // Prevent muliple loads for an image, in case it gets
+ // preloaded more than once, or is added via JS and then preloaded.
+ if (this.images[href]) {
+ return;
+ }
+
+ if (!isDOMPresent) {
+ this.images[href] = null;
+ }
+
+ // No image in the DOM, kick-off a background load
+ if (!img) {
+ img = new Image();
+ img.onload = (function(owner) {
+ return function() {
+ owner.pending--;
+ };
+ }(this));
+ this.pending++;
+ img.src = href;
+ }
+
+ this.images[href] = img;
+
+ // Opera will not load images until they are inserted into the DOM.
+ if (window.opera) {
+ var div = document.createElement("div");
+ div.appendChild(img);
+ // we can't use "display: none", since that makes it invisible, and thus not load
+ div.style.position = "absolute";
+ div.style.opacity = 0;
+ div.style.width = "1px";
+ div.style.height= "1px";
+ if (!this.operaCache[href]) {
+ document.body.appendChild(div);
+ this.operaCache[href] = div;
+ }
+ }
+ }
+ };
+
+ this.sourceCode = undefined;
+ this.attach = function(processing) {
+ // either attachFunction or sourceCode must be present on attach
+ if(typeof this.attachFunction === "function") {
+ this.attachFunction(processing);
+ } else if(this.sourceCode) {
+ var func = ((new Function("return (" + this.sourceCode + ");"))());
+ func(processing);
+ this.attachFunction = func;
+ } else {
+ throw "Unable to attach sketch to the processing instance";
+ }
+ };
+
+ this.toString = function() {
+ var i;
+ var code = "((function(Sketch) {\n";
+ code += "var sketch = new Sketch(\n" + this.sourceCode + ");\n";
+ for(i in this.options) {
+ if(this.options.hasOwnProperty(i)) {
+ var value = this.options[i];
+ code += "sketch.options." + i + " = " +
+ (typeof value === 'string' ? '\"' + value + '\"' : "" + value) + ";\n";
+ }
+ }
+ for(i in this.imageCache) {
+ if(this.options.hasOwnProperty(i)) {
+ code += "sketch.imageCache.add(\"" + i + "\");\n";
+ }
+ }
+ // TODO serialize fonts
+ code += "return sketch;\n})(Processing.Sketch))";
+ return code;
+ };
+ };
+
+ /**
+ * aggregate all source code into a single file, then rewrite that
+ * source and bind to canvas via new Processing(canvas, sourcestring).
+ * @param {CANVAS} canvas The html canvas element to bind to
+ * @param {String[]} source The array of files that must be loaded
+ */
+ var loadSketchFromSources = Processing.loadSketchFromSources = function(canvas, sources) {
+ var code = [], errors = [], sourcesCount = sources.length, loaded = 0;
+
+ function ajaxAsync(url, callback) {
+ var xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState === 4) {
+ var error;
+ if (xhr.status !== 200 && xhr.status !== 0) {
+ error = "Invalid XHR status " + xhr.status;
+ } else if (xhr.responseText === "") {
+ // Give a hint when loading fails due to same-origin issues on file:/// urls
+ if ( ("withCredentials" in new XMLHttpRequest()) &&
+ (new XMLHttpRequest()).withCredentials === false &&
+ window.location.protocol === "file:" ) {
+ error = "XMLHttpRequest failure, possibly due to a same-origin policy violation. You can try loading this page in another browser, or load it from http://localhost using a local webserver. See the Processing.js README for a more detailed explanation of this problem and solutions.";
+ } else {
+ error = "File is empty.";
+ }
+ }
+
+ callback(xhr.responseText, error);
+ }
+ };
+ xhr.open("GET", url, true);
+ if (xhr.overrideMimeType) {
+ xhr.overrideMimeType("application/json");
+ }
+ xhr.setRequestHeader("If-Modified-Since", "Fri, 01 Jan 1960 00:00:00 GMT"); // no cache
+ xhr.send(null);
+ }
+
+ function loadBlock(index, filename) {
+ function callback(block, error) {
+ code[index] = block;
+ ++loaded;
+ if (error) {
+ errors.push(filename + " ==> " + error);
+ }
+ if (loaded === sourcesCount) {
+ if (errors.length === 0) {
+ // This used to throw, but it was constantly getting in the way of debugging where things go wrong!
+ return new Processing(canvas, code.join("\n"));
+ } else {
+ throw "Processing.js: Unable to load pjs sketch files: " + errors.join("\n");
+ }
+ }
+ }
+ if (filename.charAt(0) === '#') {
+ // trying to get script from the element
+ var scriptElement = document.getElementById(filename.substring(1));
+ if (scriptElement) {
+ callback(scriptElement.text || scriptElement.textContent);
+ } else {
+ callback("", "Unable to load pjs sketch: element with id \'" + filename.substring(1) + "\' was not found");
+ }
+ return;
+ }
+
+ ajaxAsync(filename, callback);
+ }
+
+ for (var i = 0; i < sourcesCount; ++i) {
+ loadBlock(i, sources[i]);
+ }
+ };
+
+ /**
+ * Automatic initialization function.
+ */
+ var init = function() {
+ document.removeEventListener('DOMContentLoaded', init, false);
+ var i;
+
+ // before running through init, clear the instances list, to prevent
+ // sketch duplication when page content is dynamically swapped without
+ // swapping out processing.js
+ while (Processing.instances.length > 0) {
+ for (i = Processing.instances.length - 1; i >= 0; i--) {
+ if (Processing.instances[i]) {
+ Processing.instances[i].exit();
+ }
+ }
+ }
+
+ var canvas = document.getElementsByTagName('canvas'),
+ filenames;
+
+ for (i = 0, l = canvas.length; i < l; i++) {
+ // datasrc and data-src are deprecated.
+ var processingSources = canvas[i].getAttribute('data-processing-sources');
+ if (processingSources === null) {
+ // Temporary fallback for datasrc and data-src
+ processingSources = canvas[i].getAttribute('data-src');
+ if (processingSources === null) {
+ processingSources = canvas[i].getAttribute('datasrc');
+ }
+ }
+ if (processingSources) {
+ filenames = processingSources.split(/\s+/g);
+ for (var j = 0; j < filenames.length;) {
+ if (filenames[j]) {
+ j++;
+ } else {
+ filenames.splice(j, 1);
+ }
+ }
+ loadSketchFromSources(canvas[i], filenames);
+ }
+ }
+
+ // also process all <script>-indicated sketches, if there are any
+ var s, last, source, instance,
+ nodelist = document.getElementsByTagName('script'),
+ scripts=[];
+
+ // snapshot the DOM, as the nodelist is only a DOM view, and is
+ // updated instantly when a script element is added or removed.
+ for (s = nodelist.length - 1; s >= 0; s--) {
+ scripts.push(nodelist[s]);
+ }
+
+ // iterate over all script elements to see if they contain Processing code
+ for (s = 0, last = scripts.length; s < last; s++) {
+ var script = scripts[s];
+ if (!script.getAttribute) {
+ continue;
+ }
+
+ var type = script.getAttribute("type");
+ if (type && (type.toLowerCase() === "text/processing" || type.toLowerCase() === "application/processing")) {
+ var target = script.getAttribute("data-processing-target");
+ canvas = undef;
+ if (target) {
+ canvas = document.getElementById(target);
+ } else {
+ var nextSibling = script.nextSibling;
+ while (nextSibling && nextSibling.nodeType !== 1) {
+ nextSibling = nextSibling.nextSibling;
+ }
+ if (nextSibling && nextSibling.nodeName.toLowerCase() === "canvas") {
+ canvas = nextSibling;
+ }
+ }
+
+ if (canvas) {
+ if (script.getAttribute("src")) {
+ filenames = script.getAttribute("src").split(/\s+/);
+ loadSketchFromSources(canvas, filenames);
+ continue;
+ }
+ source = script.textContent || script.text;
+ instance = new Processing(canvas, source);
+ }
+ }
+ }
+ };
+
+ /**
+ * automatic loading of all sketches on the page
+ */
+ document.addEventListener('DOMContentLoaded', init, false);
+
+ /**
+ * Make Processing run through init after already having
+ * been set up for a page. This function exists mostly for pages
+ * that swap content in/out without reloading a page.
+ */
+ Processing.reload = init;
+
+ /**
+ * Disable the automatic loading of all sketches on the page.
+ * This will work as long as it's issued before DOMContentLoaded.
+ */
+ Processing.disableInit = function() {
+ document.removeEventListener('DOMContentLoaded', init, false);
+ };
+
+ // done.
+ return Processing;
+};
+
+},{}],8:[function(require,module,exports){
+/**
+ * Returns Java equals() result for two objects. If the first object
+ * has the "equals" function, it preforms the call of this function.
+ * Otherwise the method uses the JavaScript === operator.
+ *
+ * @param {Object} obj The first object.
+ * @param {Object} other The second object.
+ *
+ * @returns {boolean} true if the objects are equal.
+ */
+module.exports = function virtEquals(obj, other) {
+ if (obj === null || other === null) {
+ return (obj === null) && (other === null);
+ }
+ if (typeof (obj) === "string") {
+ return obj === other;
+ }
+ if (typeof(obj) !== "object") {
+ return obj === other;
+ }
+ if (obj.equals instanceof Function) {
+ return obj.equals(other);
+ }
+ return obj === other;
+};
+
+},{}],9:[function(require,module,exports){
+/**
+ * Returns Java hashCode() result for the object. If the object has the "hashCode" function,
+ * it preforms the call of this function. Otherwise it uses/creates the "$id" property,
+ * which is used as the hashCode.
+ *
+ * @param {Object} obj The object.
+ * @returns {int} The object's hash code.
+ */
+module.exports = function virtHashCode(obj, undef) {
+ if (typeof(obj) === "string") {
+ var hash = 0;
+ for (var i = 0; i < obj.length; ++i) {
+ hash = (hash * 31 + obj.charCodeAt(i)) & 0xFFFFFFFF;
+ }
+ return hash;
+ }
+ if (typeof(obj) !== "object") {
+ return obj & 0xFFFFFFFF;
+ }
+ if (obj.hashCode instanceof Function) {
+ return obj.hashCode();
+ }
+ if (obj.$id === undef) {
+ obj.$id = ((Math.floor(Math.random() * 0x10000) - 0x8000) << 16) | Math.floor(Math.random() * 0x10000);
+ }
+ return obj.$id;
+};
+
+},{}],10:[function(require,module,exports){
+/**
+ * An ArrayList stores a variable number of objects.
+ *
+ * @param {int} initialCapacity optional defines the initial capacity of the list, it's empty by default
+ *
+ * @returns {ArrayList} new ArrayList object
+ */
+module.exports = function(options) {
+ var virtHashCode = options.virtHashCode,
+ virtEquals = options.virtEquals;
+
+ function Iterator(array) {
+ var index = -1;
+ this.hasNext = function() {
+ return (index + 1) < array.length;
+ };
+
+ this.next = function() {
+ return array[++index];
+ };
+
+ this.remove = function() {
+ array.splice(index--, 1);
+ };
+ }
+
+ function ArrayList(a) {
+ var array = [];
+
+ if (a && a.toArray) {
+ array = a.toArray();
+ }
+
+ /**
+ * @member ArrayList
+ * ArrayList.get() Returns the element at the specified position in this list.
+ *
+ * @param {int} i index of element to return
+ *
+ * @returns {Object} the element at the specified position in this list.
+ */
+ this.get = function(i) {
+ return array[i];
+ };
+ /**
+ * @member ArrayList
+ * ArrayList.contains() Returns true if this list contains the specified element.
+ *
+ * @param {Object} item element whose presence in this List is to be tested.
+ *
+ * @returns {boolean} true if the specified element is present; false otherwise.
+ */
+ this.contains = function(item) {
+ return this.indexOf(item)>-1;
+ };
+ /**
+ * @member ArrayList
+ * ArrayList.indexOf() Returns the position this element takes in the list, or -1 if the element is not found.
+ *
+ * @param {Object} item element whose position in this List is to be tested.
+ *
+ * @returns {int} the list position that the first match for this element holds in the list, or -1 if it is not in the list.
+ */
+ this.indexOf = function(item) {
+ for (var i = 0, len = array.length; i < len; ++i) {
+ if (virtEquals(item, array[i])) {
+ return i;
+ }
+ }
+ return -1;
+ };
+ /**
+ * @member ArrayList
+ * ArrayList.lastIndexOf() Returns the index of the last occurrence of the specified element in this list,
+ * or -1 if this list does not contain the element. More formally, returns the highest index i such that
+ * (o==null ? get(i)==null : o.equals(get(i))), or -1 if there is no such index.
+ *
+ * @param {Object} item element to search for.
+ *
+ * @returns {int} the index of the last occurrence of the specified element in this list, or -1 if this list does not contain the element.
+ */
+ this.lastIndexOf = function(item) {
+ for (var i = array.length-1; i >= 0; --i) {
+ if (virtEquals(item, array[i])) {
+ return i;
+ }
+ }
+ return -1;
+ };
+ /**
+ * @member ArrayList
+ * ArrayList.add() Adds the specified element to this list.
+ *
+ * @param {int} index optional index at which the specified element is to be inserted
+ * @param {Object} object element to be added to the list
+ */
+ this.add = function() {
+ if (arguments.length === 1) {
+ array.push(arguments[0]); // for add(Object)
+ } else if (arguments.length === 2) {
+ var arg0 = arguments[0];
+ if (typeof arg0 === 'number') {
+ if (arg0 >= 0 && arg0 <= array.length) {
+ array.splice(arg0, 0, arguments[1]); // for add(i, Object)
+ } else {
+ throw(arg0 + " is not a valid index");
+ }
+ } else {
+ throw(typeof arg0 + " is not a number");
+ }
+ } else {
+ throw("Please use the proper number of parameters.");
+ }
+ };
+ /**
+ * @member ArrayList
+ * ArrayList.addAll(collection) appends all of the elements in the specified
+ * Collection to the end of this list, in the order that they are returned by
+ * the specified Collection's Iterator.
+ *
+ * When called as addAll(index, collection) the elements are inserted into
+ * this list at the position indicated by index.
+ *
+ * @param {index} Optional; specifies the position the colletion should be inserted at
+ * @param {collection} Any iterable object (ArrayList, HashMap.keySet(), etc.)
+ * @throws out of bounds error for negative index, or index greater than list size.
+ */
+ this.addAll = function(arg1, arg2) {
+ // addAll(int, Collection)
+ var it;
+ if (typeof arg1 === "number") {
+ if (arg1 < 0 || arg1 > array.length) {
+ throw("Index out of bounds for addAll: " + arg1 + " greater or equal than " + array.length);
+ }
+ it = new ObjectIterator(arg2);
+ while (it.hasNext()) {
+ array.splice(arg1++, 0, it.next());
+ }
+ }
+ // addAll(Collection)
+ else {
+ it = new ObjectIterator(arg1);
+ while (it.hasNext()) {
+ array.push(it.next());
+ }
+ }
+ };
+ /**
+ * @member ArrayList
+ * ArrayList.set() Replaces the element at the specified position in this list with the specified element.
+ *
+ * @param {int} index index of element to replace
+ * @param {Object} object element to be stored at the specified position
+ */
+ this.set = function() {
+ if (arguments.length === 2) {
+ var arg0 = arguments[0];
+ if (typeof arg0 === 'number') {
+ if (arg0 >= 0 && arg0 < array.length) {
+ array.splice(arg0, 1, arguments[1]);
+ } else {
+ throw(arg0 + " is not a valid index.");
+ }
+ } else {
+ throw(typeof arg0 + " is not a number");
+ }
+ } else {
+ throw("Please use the proper number of parameters.");
+ }
+ };
+
+ /**
+ * @member ArrayList
+ * ArrayList.size() Returns the number of elements in this list.
+ *
+ * @returns {int} the number of elements in this list
+ */
+ this.size = function() {
+ return array.length;
+ };
+
+ /**
+ * @member ArrayList
+ * ArrayList.clear() Removes all of the elements from this list. The list will be empty after this call returns.
+ */
+ this.clear = function() {
+ array.length = 0;
+ };
+
+ /**
+ * @member ArrayList
+ * ArrayList.remove() Removes an element either based on index, if the argument is a number, or
+ * by equality check, if the argument is an object.
+ *
+ * @param {int|Object} item either the index of the element to be removed, or the element itself.
+ *
+ * @returns {Object|boolean} If removal is by index, the element that was removed, or null if nothing was removed. If removal is by object, true if removal occurred, otherwise false.
+ */
+ this.remove = function(item) {
+ if (typeof item === 'number') {
+ return array.splice(item, 1)[0];
+ }
+ item = this.indexOf(item);
+ if (item > -1) {
+ array.splice(item, 1);
+ return true;
+ }
+ return false;
+ };
+
+ /**
+ * @member ArrayList
+ * ArrayList.removeAll Removes from this List all of the elements from
+ * the current ArrayList which are present in the passed in paramater ArrayList 'c'.
+ * Shifts any succeeding elements to the left (reduces their index).
+ *
+ * @param {ArrayList} the ArrayList to compare to the current ArrayList
+ *
+ * @returns {boolean} true if the ArrayList had an element removed; false otherwise
+ */
+ this.removeAll = function(c) {
+ var i, x, item,
+ newList = new ArrayList();
+ newList.addAll(this);
+ this.clear();
+ // For every item that exists in the original ArrayList and not in the c ArrayList
+ // copy it into the empty 'this' ArrayList to create the new 'this' Array.
+ for (i = 0, x = 0; i < newList.size(); i++) {
+ item = newList.get(i);
+ if (!c.contains(item)) {
+ this.add(x++, item);
+ }
+ }
+ if (this.size() < newList.size()) {
+ return true;
+ }
+ return false;
+ };
+
+ /**
+ * @member ArrayList
+ * ArrayList.isEmpty() Tests if this list has no elements.
+ *
+ * @returns {boolean} true if this list has no elements; false otherwise
+ */
+ this.isEmpty = function() {
+ return !array.length;
+ };
+
+ /**
+ * @member ArrayList
+ * ArrayList.clone() Returns a shallow copy of this ArrayList instance. (The elements themselves are not copied.)
+ *
+ * @returns {ArrayList} a clone of this ArrayList instance
+ */
+ this.clone = function() {
+ return new ArrayList(this);
+ };
+
+ /**
+ * @member ArrayList
+ * ArrayList.toArray() Returns an array containing all of the elements in this list in the correct order.
+ *
+ * @returns {Object[]} Returns an array containing all of the elements in this list in the correct order
+ */
+ this.toArray = function() {
+ return array.slice(0);
+ };
+
+ this.iterator = function() {
+ return new Iterator(array);
+ };
+ }
+
+ return ArrayList;
+};
+
+},{}],11:[function(require,module,exports){
+module.exports = (function(charMap, undef) {
+
+ var Char = function(chr) {
+ if (typeof chr === 'string' && chr.length === 1) {
+ this.code = chr.charCodeAt(0);
+ } else if (typeof chr === 'number') {
+ this.code = chr;
+ } else if (chr instanceof Char) {
+ this.code = chr;
+ } else {
+ this.code = NaN;
+ }
+ return (charMap[this.code] === undef) ? charMap[this.code] = this : charMap[this.code];
+ };
+
+ Char.prototype.toString = function() {
+ return String.fromCharCode(this.code);
+ };
+
+ Char.prototype.valueOf = function() {
+ return this.code;
+ };
+
+ return Char;
+}({}));
+
+},{}],12:[function(require,module,exports){
+/**
+* A HashMap stores a collection of objects, each referenced by a key. This is similar to an Array, only
+* instead of accessing elements with a numeric index, a String is used. (If you are familiar with
+* associative arrays from other languages, this is the same idea.)
+*
+* @param {int} initialCapacity defines the initial capacity of the map, it's 16 by default
+* @param {float} loadFactor the load factor for the map, the default is 0.75
+* @param {Map} m gives the new HashMap the same mappings as this Map
+*/
+module.exports = function(options) {
+ var virtHashCode = options.virtHashCode,
+ virtEquals = options.virtEquals;
+
+ /**
+ * @member HashMap
+ * A HashMap stores a collection of objects, each referenced by a key. This is similar to an Array, only
+ * instead of accessing elements with a numeric index, a String is used. (If you are familiar with
+ * associative arrays from other languages, this is the same idea.)
+ *
+ * @param {int} initialCapacity defines the initial capacity of the map, it's 16 by default
+ * @param {float} loadFactor the load factor for the map, the default is 0.75
+ * @param {Map} m gives the new HashMap the same mappings as this Map
+ */
+ function HashMap() {
+ if (arguments.length === 1 && arguments[0] instanceof HashMap) {
+ return arguments[0].clone();
+ }
+
+ var initialCapacity = arguments.length > 0 ? arguments[0] : 16;
+ var loadFactor = arguments.length > 1 ? arguments[1] : 0.75;
+ var buckets = [];
+ buckets.length = initialCapacity;
+ var count = 0;
+ var hashMap = this;
+
+ function getBucketIndex(key) {
+ var index = virtHashCode(key) % buckets.length;
+ return index < 0 ? buckets.length + index : index;
+ }
+ function ensureLoad() {
+ if (count <= loadFactor * buckets.length) {
+ return;
+ }
+ var allEntries = [];
+ for (var i = 0; i < buckets.length; ++i) {
+ if (buckets[i] !== undefined) {
+ allEntries = allEntries.concat(buckets[i]);
+ }
+ }
+ var newBucketsLength = buckets.length * 2;
+ buckets = [];
+ buckets.length = newBucketsLength;
+ for (var j = 0; j < allEntries.length; ++j) {
+ var index = getBucketIndex(allEntries[j].key);
+ var bucket = buckets[index];
+ if (bucket === undefined) {
+ buckets[index] = bucket = [];
+ }
+ bucket.push(allEntries[j]);
+ }
+ }
+
+ function Iterator(conversion, removeItem) {
+ var bucketIndex = 0;
+ var itemIndex = -1;
+ var endOfBuckets = false;
+ var currentItem;
+
+ function findNext() {
+ while (!endOfBuckets) {
+ ++itemIndex;
+ if (bucketIndex >= buckets.length) {
+ endOfBuckets = true;
+ } else if (buckets[bucketIndex] === undefined || itemIndex >= buckets[bucketIndex].length) {
+ itemIndex = -1;
+ ++bucketIndex;
+ } else {
+ return;
+ }
+ }
+ }
+
+ /*
+ * @member Iterator
+ * Checks if the Iterator has more items
+ */
+ this.hasNext = function() {
+ return !endOfBuckets;
+ };
+
+ /*
+ * @member Iterator
+ * Return the next Item
+ */
+ this.next = function() {
+ currentItem = conversion(buckets[bucketIndex][itemIndex]);
+ findNext();
+ return currentItem;
+ };
+
+ /*
+ * @member Iterator
+ * Remove the current item
+ */
+ this.remove = function() {
+ if (currentItem !== undefined) {
+ removeItem(currentItem);
+ --itemIndex;
+ findNext();
+ }
+ };
+
+ findNext();
+ }
+
+ function Set(conversion, isIn, removeItem) {
+ this.clear = function() {
+ hashMap.clear();
+ };
+
+ this.contains = function(o) {
+ return isIn(o);
+ };
+
+ this.containsAll = function(o) {
+ var it = o.iterator();
+ while (it.hasNext()) {
+ if (!this.contains(it.next())) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ this.isEmpty = function() {
+ return hashMap.isEmpty();
+ };
+
+ this.iterator = function() {
+ return new Iterator(conversion, removeItem);
+ };
+
+ this.remove = function(o) {
+ if (this.contains(o)) {
+ removeItem(o);
+ return true;
+ }
+ return false;
+ };
+
+ this.removeAll = function(c) {
+ var it = c.iterator();
+ var changed = false;
+ while (it.hasNext()) {
+ var item = it.next();
+ if (this.contains(item)) {
+ removeItem(item);
+ changed = true;
+ }
+ }
+ return true;
+ };
+
+ this.retainAll = function(c) {
+ var it = this.iterator();
+ var toRemove = [];
+ while (it.hasNext()) {
+ var entry = it.next();
+ if (!c.contains(entry)) {
+ toRemove.push(entry);
+ }
+ }
+ for (var i = 0; i < toRemove.length; ++i) {
+ removeItem(toRemove[i]);
+ }
+ return toRemove.length > 0;
+ };
+
+ this.size = function() {
+ return hashMap.size();
+ };
+
+ this.toArray = function() {
+ var result = [];
+ var it = this.iterator();
+ while (it.hasNext()) {
+ result.push(it.next());
+ }
+ return result;
+ };
+ }
+
+ function Entry(pair) {
+ this._isIn = function(map) {
+ return map === hashMap && (pair.removed === undefined);
+ };
+
+ this.equals = function(o) {
+ return virtEquals(pair.key, o.getKey());
+ };
+
+ this.getKey = function() {
+ return pair.key;
+ };
+
+ this.getValue = function() {
+ return pair.value;
+ };
+
+ this.hashCode = function(o) {
+ return virtHashCode(pair.key);
+ };
+
+ this.setValue = function(value) {
+ var old = pair.value;
+ pair.value = value;
+ return old;
+ };
+ }
+
+ this.clear = function() {
+ count = 0;
+ buckets = [];
+ buckets.length = initialCapacity;
+ };
+
+ this.clone = function() {
+ var map = new HashMap();
+ map.putAll(this);
+ return map;
+ };
+
+ this.containsKey = function(key) {
+ var index = getBucketIndex(key);
+ var bucket = buckets[index];
+ if (bucket === undefined) {
+ return false;
+ }
+ for (var i = 0; i < bucket.length; ++i) {
+ if (virtEquals(bucket[i].key, key)) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ this.containsValue = function(value) {
+ for (var i = 0; i < buckets.length; ++i) {
+ var bucket = buckets[i];
+ if (bucket === undefined) {
+ continue;
+ }
+ for (var j = 0; j < bucket.length; ++j) {
+ if (virtEquals(bucket[j].value, value)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+
+ this.entrySet = function() {
+ return new Set(
+
+ function(pair) {
+ return new Entry(pair);
+ },
+
+ function(pair) {
+ return (pair instanceof Entry) && pair._isIn(hashMap);
+ },
+
+ function(pair) {
+ return hashMap.remove(pair.getKey());
+ });
+ };
+
+ this.get = function(key) {
+ var index = getBucketIndex(key);
+ var bucket = buckets[index];
+ if (bucket === undefined) {
+ return null;
+ }
+ for (var i = 0; i < bucket.length; ++i) {
+ if (virtEquals(bucket[i].key, key)) {
+ return bucket[i].value;
+ }
+ }
+ return null;
+ };
+
+ this.isEmpty = function() {
+ return count === 0;
+ };
+
+ this.keySet = function() {
+ return new Set(
+ // get key from pair
+ function(pair) {
+ return pair.key;
+ },
+ // is-in test
+ function(key) {
+ return hashMap.containsKey(key);
+ },
+ // remove from hashmap by key
+ function(key) {
+ return hashMap.remove(key);
+ }
+ );
+ };
+
+ this.values = function() {
+ return new Set(
+ // get value from pair
+ function(pair) {
+ return pair.value;
+ },
+ // is-in test
+ function(value) {
+ return hashMap.containsValue(value);
+ },
+ // remove from hashmap by value
+ function(value) {
+ return hashMap.removeByValue(value);
+ }
+ );
+ };
+
+ this.put = function(key, value) {
+ var index = getBucketIndex(key);
+ var bucket = buckets[index];
+ if (bucket === undefined) {
+ ++count;
+ buckets[index] = [{
+ key: key,
+ value: value
+ }];
+ ensureLoad();
+ return null;
+ }
+ for (var i = 0; i < bucket.length; ++i) {
+ if (virtEquals(bucket[i].key, key)) {
+ var previous = bucket[i].value;
+ bucket[i].value = value;
+ return previous;
+ }
+ }
+ ++count;
+ bucket.push({
+ key: key,
+ value: value
+ });
+ ensureLoad();
+ return null;
+ };
+
+ this.putAll = function(m) {
+ var it = m.entrySet().iterator();
+ while (it.hasNext()) {
+ var entry = it.next();
+ this.put(entry.getKey(), entry.getValue());
+ }
+ };
+
+ this.remove = function(key) {
+ var index = getBucketIndex(key);
+ var bucket = buckets[index];
+ if (bucket === undefined) {
+ return null;
+ }
+ for (var i = 0; i < bucket.length; ++i) {
+ if (virtEquals(bucket[i].key, key)) {
+ --count;
+ var previous = bucket[i].value;
+ bucket[i].removed = true;
+ if (bucket.length > 1) {
+ bucket.splice(i, 1);
+ } else {
+ buckets[index] = undefined;
+ }
+ return previous;
+ }
+ }
+ return null;
+ };
+
+ this.removeByValue = function(value) {
+ var bucket, i, ilen, pair;
+ for (bucket in buckets) {
+ if (buckets.hasOwnProperty(bucket)) {
+ for (i = 0, ilen = buckets[bucket].length; i < ilen; i++) {
+ pair = buckets[bucket][i];
+ // removal on values is based on identity, not equality
+ if (pair.value === value) {
+ buckets[bucket].splice(i, 1);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ };
+
+ this.size = function() {
+ return count;
+ };
+ }
+
+ return HashMap;
+};
+
+},{}],13:[function(require,module,exports){
+// module export
+module.exports = function(options,undef) {
+ var window = options.Browser.window,
+ document = options.Browser.document,
+ noop = options.noop;
+
+ /**
+ * [internal function] computeFontMetrics() calculates various metrics for text
+ * placement. Currently this function computes the ascent, descent and leading
+ * (from "lead", used for vertical space) values for the currently active font.
+ */
+ function computeFontMetrics(pfont) {
+ var emQuad = 250,
+ correctionFactor = pfont.size / emQuad,
+ canvas = document.createElement("canvas");
+ canvas.width = 2*emQuad;
+ canvas.height = 2*emQuad;
+ canvas.style.opacity = 0;
+ var cfmFont = pfont.getCSSDefinition(emQuad+"px", "normal"),
+ ctx = canvas.getContext("2d");
+ ctx.font = cfmFont;
+
+ // Size the canvas using a string with common max-ascent and max-descent letters.
+ // Changing the canvas dimensions resets the context, so we must reset the font.
+ var protrusions = "dbflkhyjqpg";
+ canvas.width = ctx.measureText(protrusions).width;
+ ctx.font = cfmFont;
+
+ // for text lead values, we meaure a multiline text container.
+ var leadDiv = document.createElement("div");
+ leadDiv.style.position = "absolute";
+ leadDiv.style.opacity = 0;
+ leadDiv.style.fontFamily = '"' + pfont.name + '"';
+ leadDiv.style.fontSize = emQuad + "px";
+ leadDiv.innerHTML = protrusions + "<br/>" + protrusions;
+ document.body.appendChild(leadDiv);
+
+ var w = canvas.width,
+ h = canvas.height,
+ baseline = h/2;
+
+ // Set all canvas pixeldata values to 255, with all the content
+ // data being 0. This lets us scan for data[i] != 255.
+ ctx.fillStyle = "white";
+ ctx.fillRect(0, 0, w, h);
+ ctx.fillStyle = "black";
+ ctx.fillText(protrusions, 0, baseline);
+ var pixelData = ctx.getImageData(0, 0, w, h).data;
+
+ // canvas pixel data is w*4 by h*4, because R, G, B and A are separate,
+ // consecutive values in the array, rather than stored as 32 bit ints.
+ var i = 0,
+ w4 = w * 4,
+ len = pixelData.length;
+
+ // Finding the ascent uses a normal, forward scanline
+ while (++i < len && pixelData[i] === 255) {
+ noop();
+ }
+ var ascent = Math.round(i / w4);
+
+ // Finding the descent uses a reverse scanline
+ i = len - 1;
+ while (--i > 0 && pixelData[i] === 255) {
+ noop();
+ }
+ var descent = Math.round(i / w4);
+
+ // set font metrics
+ pfont.ascent = correctionFactor * (baseline - ascent);
+ pfont.descent = correctionFactor * (descent - baseline);
+
+ // Then we try to get the real value from the browser
+ if (document.defaultView.getComputedStyle) {
+ var leadDivHeight = document.defaultView.getComputedStyle(leadDiv,null).getPropertyValue("height");
+ leadDivHeight = correctionFactor * leadDivHeight.replace("px","");
+ if (leadDivHeight >= pfont.size * 2) {
+ pfont.leading = Math.round(leadDivHeight/2);
+ }
+ }
+ document.body.removeChild(leadDiv);
+
+ // if we're caching, cache the context used for this pfont
+ if (pfont.caching) {
+ return ctx;
+ }
+ }
+
+ /**
+ * Constructor for a system or from-file (non-SVG) font.
+ */
+ function PFont(name, size) {
+ // according to the P5 API, new PFont() is legal (albeit completely useless)
+ if (name === undef) {
+ name = "";
+ }
+ this.name = name;
+ if (size === undef) {
+ size = 0;
+ }
+ this.size = size;
+ this.glyph = false;
+ this.ascent = 0;
+ this.descent = 0;
+ // For leading, the "safe" value uses the standard TEX ratio
+ this.leading = 1.2 * size;
+
+ // Note that an italic, bold font must used "... Bold Italic"
+ // in P5. "... Italic Bold" is treated as normal/normal.
+ var illegalIndicator = name.indexOf(" Italic Bold");
+ if (illegalIndicator !== -1) {
+ name = name.substring(0, illegalIndicator);
+ }
+
+ // determine font style
+ this.style = "normal";
+ var italicsIndicator = name.indexOf(" Italic");
+ if (italicsIndicator !== -1) {
+ name = name.substring(0, italicsIndicator);
+ this.style = "italic";
+ }
+
+ // determine font weight
+ this.weight = "normal";
+ var boldIndicator = name.indexOf(" Bold");
+ if (boldIndicator !== -1) {
+ name = name.substring(0, boldIndicator);
+ this.weight = "bold";
+ }
+
+ // determine font-family name
+ this.family = "sans-serif";
+ if (name !== undef) {
+ switch(name) {
+ case "sans-serif":
+ case "serif":
+ case "monospace":
+ case "fantasy":
+ case "cursive":
+ this.family = name;
+ break;
+ default:
+ this.family = '"' + name + '", sans-serif';
+ break;
+ }
+ }
+ // Calculate the ascent/descent/leading value based on
+ // how the browser renders this font.
+ this.context2d = computeFontMetrics(this);
+ this.css = this.getCSSDefinition();
+ if (this.context2d) {
+ this.context2d.font = this.css;
+ }
+ }
+
+ /**
+ * regulates whether or not we're caching the canvas
+ * 2d context for quick text width computation.
+ */
+ PFont.prototype.caching = true;
+
+ /**
+ * This function generates the CSS "font" string for this PFont
+ */
+ PFont.prototype.getCSSDefinition = function(fontSize, lineHeight) {
+ if(fontSize===undef) {
+ fontSize = this.size + "px";
+ }
+ if(lineHeight===undef) {
+ lineHeight = this.leading + "px";
+ }
+ // CSS "font" definition: font-style font-variant font-weight font-size/line-height font-family
+ var components = [this.style, "normal", this.weight, fontSize + "/" + lineHeight, this.family];
+ return components.join(" ");
+ };
+
+ /**
+ * Rely on the cached context2d measureText function.
+ */
+ PFont.prototype.measureTextWidth = function(string) {
+ return this.context2d.measureText(string).width;
+ };
+
+ /**
+ * FALLBACK FUNCTION -- replaces Pfont.prototype.measureTextWidth
+ * when the font cache becomes too large. This contructs a new
+ * canvas 2d context object for calling measureText on.
+ */
+ PFont.prototype.measureTextWidthFallback = function(string) {
+ var canvas = document.createElement("canvas"),
+ ctx = canvas.getContext("2d");
+ ctx.font = this.css;
+ return ctx.measureText(string).width;
+ };
+
+ /**
+ * Global "loaded fonts" list, internal to PFont
+ */
+ PFont.PFontCache = { length: 0 };
+
+ /**
+ * This function acts as single access point for getting and caching
+ * fonts across all sketches handled by an instance of Processing.js
+ */
+ PFont.get = function(fontName, fontSize) {
+ // round fontSize to one decimal point
+ fontSize = ((fontSize*10)+0.5|0)/10;
+ var cache = PFont.PFontCache,
+ idx = fontName+"/"+fontSize;
+ if (!cache[idx]) {
+ cache[idx] = new PFont(fontName, fontSize);
+ cache.length++;
+
+ // FALLBACK FUNCTIONALITY 1:
+ // If the cache has become large, switch over from full caching
+ // to caching only the static metrics for each new font request.
+ if (cache.length === 50) {
+ PFont.prototype.measureTextWidth = PFont.prototype.measureTextWidthFallback;
+ PFont.prototype.caching = false;
+ // clear contexts stored for each cached font
+ var entry;
+ for (entry in cache) {
+ if (entry !== "length") {
+ cache[entry].context2d = null;
+ }
+ }
+ return new PFont(fontName, fontSize);
+ }
+
+ // FALLBACK FUNCTIONALITY 2:
+ // If the cache has become too large, switch off font caching entirely.
+ if (cache.length === 400) {
+ PFont.PFontCache = {};
+ PFont.get = PFont.getFallback;
+ return new PFont(fontName, fontSize);
+ }
+ }
+ return cache[idx];
+ };
+
+ /**
+ * FALLBACK FUNCTION -- replaces PFont.get when the font cache
+ * becomes too large. This function bypasses font caching entirely.
+ */
+ PFont.getFallback = function(fontName, fontSize) {
+ return new PFont(fontName, fontSize);
+ };
+
+ /**
+ * Lists all standard fonts. Due to browser limitations, this list is
+ * not the system font list, like in P5, but the CSS "genre" list.
+ */
+ PFont.list = function() {
+ return ["sans-serif", "serif", "monospace", "fantasy", "cursive"];
+ };
+
+ /**
+ * Loading external fonts through @font-face rules is handled by PFont,
+ * to ensure fonts loaded in this way are globally available.
+ */
+ PFont.preloading = {
+ // template element used to compare font sizes
+ template: {},
+ // indicates whether or not the reference tiny font has been loaded
+ initialized: false,
+ // load the reference tiny font via a css @font-face rule
+ initialize: function() {
+ var generateTinyFont = function() {
+ var encoded = "#E3KAI2wAgT1MvMg7Eo3VmNtYX7ABi3CxnbHlm" +
+ "7Abw3kaGVhZ7ACs3OGhoZWE7A53CRobXR47AY3" +
+ "AGbG9jYQ7G03Bm1heH7ABC3CBuYW1l7Ae3AgcG" +
+ "9zd7AI3AE#B3AQ2kgTY18PPPUACwAg3ALSRoo3" +
+ "#yld0xg32QAB77#E777773B#E3C#I#Q77773E#" +
+ "Q7777777772CMAIw7AB77732B#M#Q3wAB#g3B#" +
+ "E#E2BB//82BB////w#B7#gAEg3E77x2B32B#E#" +
+ "Q#MTcBAQ32gAe#M#QQJ#E32M#QQJ#I#g32Q77#";
+ var expand = function(input) {
+ return "AAAAAAAA".substr(~~input ? 7-input : 6);
+ };
+ return encoded.replace(/[#237]/g, expand);
+ };
+ var fontface = document.createElement("style");
+ fontface.setAttribute("type","text/css");
+ fontface.innerHTML = "@font-face {\n" +
+ ' font-family: "PjsEmptyFont";' + "\n" +
+ " src: url('data:application/x-font-ttf;base64,"+generateTinyFont()+"')\n" +
+ " format('truetype');\n" +
+ "}";
+ document.head.appendChild(fontface);
+
+ // set up the template element
+ var element = document.createElement("span");
+ element.style.cssText = 'position: absolute; top: -1000; left: 0; opacity: 0; font-family: "PjsEmptyFont", fantasy;';
+ element.innerHTML = "AAAAAAAA";
+ document.body.appendChild(element);
+ this.template = element;
+
+ this.initialized = true;
+ },
+ // Shorthand function to get the computed width for an element.
+ getElementWidth: function(element) {
+ return document.defaultView.getComputedStyle(element,"").getPropertyValue("width");
+ },
+ // time taken so far in attempting to load a font
+ timeAttempted: 0,
+ // returns false if no fonts are pending load, or true otherwise.
+ pending: function(intervallength) {
+ if (!this.initialized) {
+ this.initialize();
+ }
+ var element,
+ computedWidthFont,
+ computedWidthRef = this.getElementWidth(this.template);
+ for (var i = 0; i < this.fontList.length; i++) {
+ // compares size of text in pixels. if equal, custom font is not yet loaded
+ element = this.fontList[i];
+ computedWidthFont = this.getElementWidth(element);
+ if (this.timeAttempted < 4000 && computedWidthFont === computedWidthRef) {
+ this.timeAttempted += intervallength;
+ return true;
+ } else {
+ document.body.removeChild(element);
+ this.fontList.splice(i--, 1);
+ this.timeAttempted = 0;
+ }
+ }
+ // if there are no more fonts to load, pending is false
+ if (this.fontList.length === 0) {
+ return false;
+ }
+ // We should have already returned before getting here.
+ // But, if we do get here, length!=0 so fonts are pending.
+ return true;
+ },
+ // fontList contains elements to compare font sizes against a template
+ fontList: [],
+ // addedList contains the fontnames of all the fonts loaded via @font-face
+ addedList: {},
+ // adds a font to the font cache
+ // creates an element using the font, to start loading the font,
+ // and compare against a default font to see if the custom font is loaded
+ add: function(fontSrc) {
+ if (!this.initialized) {
+ this.initialize();
+ }
+ // fontSrc can be a string or a javascript object
+ // acceptable fonts are .ttf, .otf, and data uri
+ var fontName = (typeof fontSrc === 'object' ? fontSrc.fontFace : fontSrc),
+ fontUrl = (typeof fontSrc === 'object' ? fontSrc.url : fontSrc);
+
+ // check whether we already created the @font-face rule for this font
+ if (this.addedList[fontName]) {
+ return;
+ }
+
+ // if we didn't, create the @font-face rule
+ var style = document.createElement("style");
+ style.setAttribute("type","text/css");
+ style.innerHTML = "@font-face{\n font-family: '" + fontName + "';\n src: url('" + fontUrl + "');\n}\n";
+ document.head.appendChild(style);
+ this.addedList[fontName] = true;
+
+ // also create the element to load and compare the new font
+ var element = document.createElement("span");
+ element.style.cssText = "position: absolute; top: 0; left: 0; opacity: 0;";
+ element.style.fontFamily = '"' + fontName + '", "PjsEmptyFont", fantasy';
+ element.innerHTML = "AAAAAAAA";
+ document.body.appendChild(element);
+ this.fontList.push(element);
+ }
+ };
+
+ return PFont;
+};
+},{}],14:[function(require,module,exports){
+module.exports = function(options, undef) {
+
+ // FIXME: hack
+ var p = options.p;
+
+ /**
+ * PMatrix2D is a 3x2 affine matrix implementation. The constructor accepts another PMatrix2D or a list of six float elements.
+ * If no parameters are provided the matrix is set to the identity matrix.
+ *
+ * @param {PMatrix2D} matrix the initial matrix to set to
+ * @param {float} m00 the first element of the matrix
+ * @param {float} m01 the second element of the matrix
+ * @param {float} m02 the third element of the matrix
+ * @param {float} m10 the fourth element of the matrix
+ * @param {float} m11 the fifth element of the matrix
+ * @param {float} m12 the sixth element of the matrix
+ */
+ var PMatrix2D = function() {
+ if (arguments.length === 0) {
+ this.reset();
+ } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
+ this.set(arguments[0].array());
+ } else if (arguments.length === 6) {
+ this.set(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
+ }
+ };
+
+ /**
+ * PMatrix2D methods
+ */
+ PMatrix2D.prototype = {
+ /**
+ * @member PMatrix2D
+ * The set() function sets the matrix elements. The function accepts either another PMatrix2D, an array of elements, or a list of six floats.
+ *
+ * @param {PMatrix2D} matrix the matrix to set this matrix to
+ * @param {float[]} elements an array of elements to set this matrix to
+ * @param {float} m00 the first element of the matrix
+ * @param {float} m01 the third element of the matrix
+ * @param {float} m10 the fourth element of the matrix
+ * @param {float} m11 the fith element of the matrix
+ * @param {float} m12 the sixth element of the matrix
+ */
+ set: function() {
+ if (arguments.length === 6) {
+ var a = arguments;
+ this.set([a[0], a[1], a[2],
+ a[3], a[4], a[5]]);
+ } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
+ this.elements = arguments[0].array();
+ } else if (arguments.length === 1 && arguments[0] instanceof Array) {
+ this.elements = arguments[0].slice();
+ }
+ },
+ /**
+ * @member PMatrix2D
+ * The get() function returns a copy of this PMatrix2D.
+ *
+ * @return {PMatrix2D} a copy of this PMatrix2D
+ */
+ get: function() {
+ var outgoing = new PMatrix2D();
+ outgoing.set(this.elements);
+ return outgoing;
+ },
+ /**
+ * @member PMatrix2D
+ * The reset() function sets this PMatrix2D to the identity matrix.
+ */
+ reset: function() {
+ this.set([1, 0, 0, 0, 1, 0]);
+ },
+ /**
+ * @member PMatrix2D
+ * The array() function returns a copy of the element values.
+ * @addon
+ *
+ * @return {float[]} returns a copy of the element values
+ */
+ array: function array() {
+ return this.elements.slice();
+ },
+ /**
+ * @member PMatrix2D
+ * The translate() function translates this matrix by moving the current coordinates to the location specified by tx and ty.
+ *
+ * @param {float} tx the x-axis coordinate to move to
+ * @param {float} ty the y-axis coordinate to move to
+ */
+ translate: function(tx, ty) {
+ this.elements[2] = tx * this.elements[0] + ty * this.elements[1] + this.elements[2];
+ this.elements[5] = tx * this.elements[3] + ty * this.elements[4] + this.elements[5];
+ },
+ /**
+ * @member PMatrix2D
+ * The invTranslate() function translates this matrix by moving the current coordinates to the negative location specified by tx and ty.
+ *
+ * @param {float} tx the x-axis coordinate to move to
+ * @param {float} ty the y-axis coordinate to move to
+ */
+ invTranslate: function(tx, ty) {
+ this.translate(-tx, -ty);
+ },
+ /**
+ * @member PMatrix2D
+ * The transpose() function is not used in processingjs.
+ */
+ transpose: function() {
+ // Does nothing in Processing.
+ },
+ /**
+ * @member PMatrix2D
+ * The mult() function multiplied this matrix.
+ * If two array elements are passed in the function will multiply a two element vector against this matrix.
+ * If target is null or not length four, a new float array will be returned.
+ * The values for vec and target can be the same (though that's less efficient).
+ * If two PVectors are passed in the function multiply the x and y coordinates of a PVector against this matrix.
+ *
+ * @param {PVector} source, target the PVectors used to multiply this matrix
+ * @param {float[]} source, target the arrays used to multiply this matrix
+ *
+ * @return {PVector|float[]} returns a PVector or an array representing the new matrix
+ */
+ mult: function(source, target) {
+ var x, y;
+ if (source instanceof PVector) {
+ x = source.x;
+ y = source.y;
+ if (!target) {
+ target = new PVector();
+ }
+ } else if (source instanceof Array) {
+ x = source[0];
+ y = source[1];
+ if (!target) {
+ target = [];
+ }
+ }
+ if (target instanceof Array) {
+ target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2];
+ target[1] = this.elements[3] * x + this.elements[4] * y + this.elements[5];
+ } else if (target instanceof PVector) {
+ target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2];
+ target.y = this.elements[3] * x + this.elements[4] * y + this.elements[5];
+ target.z = 0;
+ }
+ return target;
+ },
+ /**
+ * @member PMatrix2D
+ * The multX() function calculates the x component of a vector from a transformation.
+ *
+ * @param {float} x the x component of the vector being transformed
+ * @param {float} y the y component of the vector being transformed
+ *
+ * @return {float} returnes the result of the calculation
+ */
+ multX: function(x, y) {
+ return (x * this.elements[0] + y * this.elements[1] + this.elements[2]);
+ },
+ /**
+ * @member PMatrix2D
+ * The multY() function calculates the y component of a vector from a transformation.
+ *
+ * @param {float} x the x component of the vector being transformed
+ * @param {float} y the y component of the vector being transformed
+ *
+ * @return {float} returnes the result of the calculation
+ */
+ multY: function(x, y) {
+ return (x * this.elements[3] + y * this.elements[4] + this.elements[5]);
+ },
+ /**
+ * @member PMatrix2D
+ * The skewX() function skews the matrix along the x-axis the amount specified by the angle parameter.
+ * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
+ *
+ * @param {float} angle angle of skew specified in radians
+ */
+ skewX: function(angle) {
+ this.apply(1, 0, 1, angle, 0, 0);
+ },
+ /**
+ * @member PMatrix2D
+ * The skewY() function skews the matrix along the y-axis the amount specified by the angle parameter.
+ * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
+ *
+ * @param {float} angle angle of skew specified in radians
+ */
+ skewY: function(angle) {
+ this.apply(1, 0, 1, 0, angle, 0);
+ },
+ /**
+ * @member PMatrix2D
+ * The shearX() function shears the matrix along the x-axis the amount specified by the angle parameter.
+ * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
+ *
+ * @param {float} angle angle of skew specified in radians
+ */
+ shearX: function(angle) {
+ this.apply(1, 0, 1, Math.tan(angle) , 0, 0);
+ },
+ /**
+ * @member PMatrix2D
+ * The shearY() function shears the matrix along the y-axis the amount specified by the angle parameter.
+ * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
+ *
+ * @param {float} angle angle of skew specified in radians
+ */
+ shearY: function(angle) {
+ this.apply(1, 0, 1, 0, Math.tan(angle), 0);
+ },
+ /**
+ * @member PMatrix2D
+ * The determinant() function calvculates the determinant of this matrix.
+ *
+ * @return {float} the determinant of the matrix
+ */
+ determinant: function() {
+ return (this.elements[0] * this.elements[4] - this.elements[1] * this.elements[3]);
+ },
+ /**
+ * @member PMatrix2D
+ * The invert() function inverts this matrix
+ *
+ * @return {boolean} true if successful
+ */
+ invert: function() {
+ var d = this.determinant();
+ if (Math.abs( d ) > PConstants.MIN_INT) {
+ var old00 = this.elements[0];
+ var old01 = this.elements[1];
+ var old02 = this.elements[2];
+ var old10 = this.elements[3];
+ var old11 = this.elements[4];
+ var old12 = this.elements[5];
+ this.elements[0] = old11 / d;
+ this.elements[3] = -old10 / d;
+ this.elements[1] = -old01 / d;
+ this.elements[4] = old00 / d;
+ this.elements[2] = (old01 * old12 - old11 * old02) / d;
+ this.elements[5] = (old10 * old02 - old00 * old12) / d;
+ return true;
+ }
+ return false;
+ },
+ /**
+ * @member PMatrix2D
+ * The scale() function increases or decreases the size of a shape by expanding and contracting vertices. When only one parameter is specified scale will occur in all dimensions.
+ * This is equivalent to a two parameter call.
+ *
+ * @param {float} sx the amount to scale on the x-axis
+ * @param {float} sy the amount to scale on the y-axis
+ */
+ scale: function(sx, sy) {
+ if (sx && !sy) {
+ sy = sx;
+ }
+ if (sx && sy) {
+ this.elements[0] *= sx;
+ this.elements[1] *= sy;
+ this.elements[3] *= sx;
+ this.elements[4] *= sy;
+ }
+ },
+ /**
+ * @member PMatrix2D
+ * The invScale() function decreases or increases the size of a shape by contracting and expanding vertices. When only one parameter is specified scale will occur in all dimensions.
+ * This is equivalent to a two parameter call.
+ *
+ * @param {float} sx the amount to scale on the x-axis
+ * @param {float} sy the amount to scale on the y-axis
+ */
+ invScale: function(sx, sy) {
+ if (sx && !sy) {
+ sy = sx;
+ }
+ this.scale(1 / sx, 1 / sy);
+ },
+ /**
+ * @member PMatrix2D
+ * The apply() function multiplies the current matrix by the one specified through the parameters. Note that either a PMatrix2D or a list of floats can be passed in.
+ *
+ * @param {PMatrix2D} matrix the matrix to apply this matrix to
+ * @param {float} m00 the first element of the matrix
+ * @param {float} m01 the third element of the matrix
+ * @param {float} m10 the fourth element of the matrix
+ * @param {float} m11 the fith element of the matrix
+ * @param {float} m12 the sixth element of the matrix
+ */
+ apply: function() {
+ var source;
+ if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
+ source = arguments[0].array();
+ } else if (arguments.length === 6) {
+ source = Array.prototype.slice.call(arguments);
+ } else if (arguments.length === 1 && arguments[0] instanceof Array) {
+ source = arguments[0];
+ }
+
+ var result = [0, 0, this.elements[2],
+ 0, 0, this.elements[5]];
+ var e = 0;
+ for (var row = 0; row < 2; row++) {
+ for (var col = 0; col < 3; col++, e++) {
+ result[e] += this.elements[row * 3 + 0] * source[col + 0] +
+ this.elements[row * 3 + 1] * source[col + 3];
+ }
+ }
+ this.elements = result.slice();
+ },
+ /**
+ * @member PMatrix2D
+ * The preApply() function applies another matrix to the left of this one. Note that either a PMatrix2D or elements of a matrix can be passed in.
+ *
+ * @param {PMatrix2D} matrix the matrix to apply this matrix to
+ * @param {float} m00 the first element of the matrix
+ * @param {float} m01 the third element of the matrix
+ * @param {float} m10 the fourth element of the matrix
+ * @param {float} m11 the fith element of the matrix
+ * @param {float} m12 the sixth element of the matrix
+ */
+ preApply: function() {
+ var source;
+ if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
+ source = arguments[0].array();
+ } else if (arguments.length === 6) {
+ source = Array.prototype.slice.call(arguments);
+ } else if (arguments.length === 1 && arguments[0] instanceof Array) {
+ source = arguments[0];
+ }
+ var result = [0, 0, source[2],
+ 0, 0, source[5]];
+ result[2] = source[2] + this.elements[2] * source[0] + this.elements[5] * source[1];
+ result[5] = source[5] + this.elements[2] * source[3] + this.elements[5] * source[4];
+ result[0] = this.elements[0] * source[0] + this.elements[3] * source[1];
+ result[3] = this.elements[0] * source[3] + this.elements[3] * source[4];
+ result[1] = this.elements[1] * source[0] + this.elements[4] * source[1];
+ result[4] = this.elements[1] * source[3] + this.elements[4] * source[4];
+ this.elements = result.slice();
+ },
+ /**
+ * @member PMatrix2D
+ * The rotate() function rotates the matrix.
+ *
+ * @param {float} angle the angle of rotation in radiants
+ */
+ rotate: function(angle) {
+ var c = Math.cos(angle);
+ var s = Math.sin(angle);
+ var temp1 = this.elements[0];
+ var temp2 = this.elements[1];
+ this.elements[0] = c * temp1 + s * temp2;
+ this.elements[1] = -s * temp1 + c * temp2;
+ temp1 = this.elements[3];
+ temp2 = this.elements[4];
+ this.elements[3] = c * temp1 + s * temp2;
+ this.elements[4] = -s * temp1 + c * temp2;
+ },
+ /**
+ * @member PMatrix2D
+ * The rotateZ() function rotates the matrix.
+ *
+ * @param {float} angle the angle of rotation in radiants
+ */
+ rotateZ: function(angle) {
+ this.rotate(angle);
+ },
+ /**
+ * @member PMatrix2D
+ * The invRotateZ() function rotates the matrix in opposite direction.
+ *
+ * @param {float} angle the angle of rotation in radiants
+ */
+ invRotateZ: function(angle) {
+ this.rotateZ(angle - Math.PI);
+ },
+ /**
+ * @member PMatrix2D
+ * The print() function prints out the elements of this matrix
+ */
+ print: function() {
+ var digits = printMatrixHelper(this.elements);
+ var output = "" + p.nfs(this.elements[0], digits, 4) + " " +
+ p.nfs(this.elements[1], digits, 4) + " " +
+ p.nfs(this.elements[2], digits, 4) + "\n" +
+ p.nfs(this.elements[3], digits, 4) + " " +
+ p.nfs(this.elements[4], digits, 4) + " " +
+ p.nfs(this.elements[5], digits, 4) + "\n\n";
+ p.println(output);
+ }
+ };
+
+ return PMatrix2D;
+};
+
+},{}],15:[function(require,module,exports){
+module.exports = function(options, undef) {
+
+ // FIXME: hack
+ var p = options.p;
+
+ /**
+ * PMatrix3D is a 4x4 matrix implementation. The constructor accepts another PMatrix3D or a list of six or sixteen float elements.
+ * If no parameters are provided the matrix is set to the identity matrix.
+ */
+ var PMatrix3D = function() {
+ // When a matrix is created, it is set to an identity matrix
+ this.reset();
+ };
+
+ /**
+ * PMatrix3D methods
+ */
+ PMatrix3D.prototype = {
+ /**
+ * @member PMatrix2D
+ * The set() function sets the matrix elements. The function accepts either another PMatrix3D, an array of elements, or a list of six or sixteen floats.
+ *
+ * @param {PMatrix3D} matrix the initial matrix to set to
+ * @param {float[]} elements an array of elements to set this matrix to
+ * @param {float} m00 the first element of the matrix
+ * @param {float} m01 the second element of the matrix
+ * @param {float} m02 the third element of the matrix
+ * @param {float} m03 the fourth element of the matrix
+ * @param {float} m10 the fifth element of the matrix
+ * @param {float} m11 the sixth element of the matrix
+ * @param {float} m12 the seventh element of the matrix
+ * @param {float} m13 the eight element of the matrix
+ * @param {float} m20 the nineth element of the matrix
+ * @param {float} m21 the tenth element of the matrix
+ * @param {float} m22 the eleventh element of the matrix
+ * @param {float} m23 the twelveth element of the matrix
+ * @param {float} m30 the thirteenth element of the matrix
+ * @param {float} m31 the fourtheenth element of the matrix
+ * @param {float} m32 the fivetheenth element of the matrix
+ * @param {float} m33 the sixteenth element of the matrix
+ */
+ set: function() {
+ if (arguments.length === 16) {
+ this.elements = Array.prototype.slice.call(arguments);
+ } else if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
+ this.elements = arguments[0].array();
+ } else if (arguments.length === 1 && arguments[0] instanceof Array) {
+ this.elements = arguments[0].slice();
+ }
+ },
+ /**
+ * @member PMatrix3D
+ * The get() function returns a copy of this PMatrix3D.
+ *
+ * @return {PMatrix3D} a copy of this PMatrix3D
+ */
+ get: function() {
+ var outgoing = new PMatrix3D();
+ outgoing.set(this.elements);
+ return outgoing;
+ },
+ /**
+ * @member PMatrix3D
+ * The reset() function sets this PMatrix3D to the identity matrix.
+ */
+ reset: function() {
+ this.elements = [1,0,0,0,
+ 0,1,0,0,
+ 0,0,1,0,
+ 0,0,0,1];
+ },
+ /**
+ * @member PMatrix3D
+ * The array() function returns a copy of the element values.
+ * @addon
+ *
+ * @return {float[]} returns a copy of the element values
+ */
+ array: function array() {
+ return this.elements.slice();
+ },
+ /**
+ * @member PMatrix3D
+ * The translate() function translates this matrix by moving the current coordinates to the location specified by tx, ty, and tz.
+ *
+ * @param {float} tx the x-axis coordinate to move to
+ * @param {float} ty the y-axis coordinate to move to
+ * @param {float} tz the z-axis coordinate to move to
+ */
+ translate: function(tx, ty, tz) {
+ if (tz === undef) {
+ tz = 0;
+ }
+
+ this.elements[3] += tx * this.elements[0] + ty * this.elements[1] + tz * this.elements[2];
+ this.elements[7] += tx * this.elements[4] + ty * this.elements[5] + tz * this.elements[6];
+ this.elements[11] += tx * this.elements[8] + ty * this.elements[9] + tz * this.elements[10];
+ this.elements[15] += tx * this.elements[12] + ty * this.elements[13] + tz * this.elements[14];
+ },
+ /**
+ * @member PMatrix3D
+ * The transpose() function transpose this matrix.
+ */
+ transpose: function() {
+ var temp = this.elements[4];
+ this.elements[4] = this.elements[1];
+ this.elements[1] = temp;
+
+ temp = this.elements[8];
+ this.elements[8] = this.elements[2];
+ this.elements[2] = temp;
+
+ temp = this.elements[6];
+ this.elements[6] = this.elements[9];
+ this.elements[9] = temp;
+
+ temp = this.elements[3];
+ this.elements[3] = this.elements[12];
+ this.elements[12] = temp;
+
+ temp = this.elements[7];
+ this.elements[7] = this.elements[13];
+ this.elements[13] = temp;
+
+ temp = this.elements[11];
+ this.elements[11] = this.elements[14];
+ this.elements[14] = temp;
+ },
+ /**
+ * @member PMatrix3D
+ * The mult() function multiplied this matrix.
+ * If two array elements are passed in the function will multiply a two element vector against this matrix.
+ * If target is null or not length four, a new float array will be returned.
+ * The values for vec and target can be the same (though that's less efficient).
+ * If two PVectors are passed in the function multiply the x and y coordinates of a PVector against this matrix.
+ *
+ * @param {PVector} source, target the PVectors used to multiply this matrix
+ * @param {float[]} source, target the arrays used to multiply this matrix
+ *
+ * @return {PVector|float[]} returns a PVector or an array representing the new matrix
+ */
+ mult: function(source, target) {
+ var x, y, z, w;
+ if (source instanceof PVector) {
+ x = source.x;
+ y = source.y;
+ z = source.z;
+ w = 1;
+ if (!target) {
+ target = new PVector();
+ }
+ } else if (source instanceof Array) {
+ x = source[0];
+ y = source[1];
+ z = source[2];
+ w = source[3] || 1;
+
+ if ( !target || (target.length !== 3 && target.length !== 4) ) {
+ target = [0, 0, 0];
+ }
+ }
+
+ if (target instanceof Array) {
+ if (target.length === 3) {
+ target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
+ target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
+ target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
+ } else if (target.length === 4) {
+ target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w;
+ target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w;
+ target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w;
+ target[3] = this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w;
+ }
+ }
+ if (target instanceof PVector) {
+ target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
+ target.y = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
+ target.z = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
+ }
+ return target;
+ },
+ /**
+ * @member PMatrix3D
+ * The preApply() function applies another matrix to the left of this one. Note that either a PMatrix3D or elements of a matrix can be passed in.
+ *
+ * @param {PMatrix3D} matrix the matrix to apply this matrix to
+ * @param {float} m00 the first element of the matrix
+ * @param {float} m01 the second element of the matrix
+ * @param {float} m02 the third element of the matrix
+ * @param {float} m03 the fourth element of the matrix
+ * @param {float} m10 the fifth element of the matrix
+ * @param {float} m11 the sixth element of the matrix
+ * @param {float} m12 the seventh element of the matrix
+ * @param {float} m13 the eight element of the matrix
+ * @param {float} m20 the nineth element of the matrix
+ * @param {float} m21 the tenth element of the matrix
+ * @param {float} m22 the eleventh element of the matrix
+ * @param {float} m23 the twelveth element of the matrix
+ * @param {float} m30 the thirteenth element of the matrix
+ * @param {float} m31 the fourtheenth element of the matrix
+ * @param {float} m32 the fivetheenth element of the matrix
+ * @param {float} m33 the sixteenth element of the matrix
+ */
+ preApply: function() {
+ var source;
+ if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
+ source = arguments[0].array();
+ } else if (arguments.length === 16) {
+ source = Array.prototype.slice.call(arguments);
+ } else if (arguments.length === 1 && arguments[0] instanceof Array) {
+ source = arguments[0];
+ }
+
+ var result = [0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0];
+ var e = 0;
+ for (var row = 0; row < 4; row++) {
+ for (var col = 0; col < 4; col++, e++) {
+ result[e] += this.elements[col + 0] * source[row * 4 + 0] + this.elements[col + 4] *
+ source[row * 4 + 1] + this.elements[col + 8] * source[row * 4 + 2] +
+ this.elements[col + 12] * source[row * 4 + 3];
+ }
+ }
+ this.elements = result.slice();
+ },
+ /**
+ * @member PMatrix3D
+ * The apply() function multiplies the current matrix by the one specified through the parameters. Note that either a PMatrix3D or a list of floats can be passed in.
+ *
+ * @param {PMatrix3D} matrix the matrix to apply this matrix to
+ * @param {float} m00 the first element of the matrix
+ * @param {float} m01 the second element of the matrix
+ * @param {float} m02 the third element of the matrix
+ * @param {float} m03 the fourth element of the matrix
+ * @param {float} m10 the fifth element of the matrix
+ * @param {float} m11 the sixth element of the matrix
+ * @param {float} m12 the seventh element of the matrix
+ * @param {float} m13 the eight element of the matrix
+ * @param {float} m20 the nineth element of the matrix
+ * @param {float} m21 the tenth element of the matrix
+ * @param {float} m22 the eleventh element of the matrix
+ * @param {float} m23 the twelveth element of the matrix
+ * @param {float} m30 the thirteenth element of the matrix
+ * @param {float} m31 the fourtheenth element of the matrix
+ * @param {float} m32 the fivetheenth element of the matrix
+ * @param {float} m33 the sixteenth element of the matrix
+ */
+ apply: function() {
+ var source;
+ if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
+ source = arguments[0].array();
+ } else if (arguments.length === 16) {
+ source = Array.prototype.slice.call(arguments);
+ } else if (arguments.length === 1 && arguments[0] instanceof Array) {
+ source = arguments[0];
+ }
+
+ var result = [0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0];
+ var e = 0;
+ for (var row = 0; row < 4; row++) {
+ for (var col = 0; col < 4; col++, e++) {
+ result[e] += this.elements[row * 4 + 0] * source[col + 0] + this.elements[row * 4 + 1] *
+ source[col + 4] + this.elements[row * 4 + 2] * source[col + 8] +
+ this.elements[row * 4 + 3] * source[col + 12];
+ }
+ }
+ this.elements = result.slice();
+ },
+ /**
+ * @member PMatrix3D
+ * The rotate() function rotates the matrix.
+ *
+ * @param {float} angle the angle of rotation in radiants
+ */
+ rotate: function(angle, v0, v1, v2) {
+ if (!v1) {
+ this.rotateZ(angle);
+ } else {
+ // TODO should make sure this vector is normalized
+ var c = Math.cos(angle);
+ var s = Math.sin(angle);
+ var t = 1.0 - c;
+
+ this.apply((t * v0 * v0) + c,
+ (t * v0 * v1) - (s * v2),
+ (t * v0 * v2) + (s * v1),
+ 0,
+ (t * v0 * v1) + (s * v2),
+ (t * v1 * v1) + c,
+ (t * v1 * v2) - (s * v0),
+ 0,
+ (t * v0 * v2) - (s * v1),
+ (t * v1 * v2) + (s * v0),
+ (t * v2 * v2) + c,
+ 0,
+ 0, 0, 0, 1);
+ }
+ },
+ /**
+ * @member PMatrix3D
+ * The invApply() function applies the inverted matrix to this matrix.
+ *
+ * @param {float} m00 the first element of the matrix
+ * @param {float} m01 the second element of the matrix
+ * @param {float} m02 the third element of the matrix
+ * @param {float} m03 the fourth element of the matrix
+ * @param {float} m10 the fifth element of the matrix
+ * @param {float} m11 the sixth element of the matrix
+ * @param {float} m12 the seventh element of the matrix
+ * @param {float} m13 the eight element of the matrix
+ * @param {float} m20 the nineth element of the matrix
+ * @param {float} m21 the tenth element of the matrix
+ * @param {float} m22 the eleventh element of the matrix
+ * @param {float} m23 the twelveth element of the matrix
+ * @param {float} m30 the thirteenth element of the matrix
+ * @param {float} m31 the fourtheenth element of the matrix
+ * @param {float} m32 the fivetheenth element of the matrix
+ * @param {float} m33 the sixteenth element of the matrix
+ *
+ * @return {boolean} returns true if the operation was successful.
+ */
+ invApply: function() {
+ if (inverseCopy === undef) {
+ inverseCopy = new PMatrix3D();
+ }
+ var a = arguments;
+ inverseCopy.set(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8],
+ a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
+
+ if (!inverseCopy.invert()) {
+ return false;
+ }
+ this.preApply(inverseCopy);
+ return true;
+ },
+ /**
+ * @member PMatrix3D
+ * The rotateZ() function rotates the matrix.
+ *
+ * @param {float} angle the angle of rotation in radiants
+ */
+ rotateX: function(angle) {
+ var c = Math.cos(angle);
+ var s = Math.sin(angle);
+ this.apply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]);
+ },
+ /**
+ * @member PMatrix3D
+ * The rotateY() function rotates the matrix.
+ *
+ * @param {float} angle the angle of rotation in radiants
+ */
+ rotateY: function(angle) {
+ var c = Math.cos(angle);
+ var s = Math.sin(angle);
+ this.apply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]);
+ },
+ /**
+ * @member PMatrix3D
+ * The rotateZ() function rotates the matrix.
+ *
+ * @param {float} angle the angle of rotation in radiants
+ */
+ rotateZ: function(angle) {
+ var c = Math.cos(angle);
+ var s = Math.sin(angle);
+ this.apply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
+ },
+ /**
+ * @member PMatrix3D
+ * The scale() function increases or decreases the size of a matrix by expanding and contracting vertices. When only one parameter is specified scale will occur in all dimensions.
+ * This is equivalent to a three parameter call.
+ *
+ * @param {float} sx the amount to scale on the x-axis
+ * @param {float} sy the amount to scale on the y-axis
+ * @param {float} sz the amount to scale on the z-axis
+ */
+ scale: function(sx, sy, sz) {
+ if (sx && !sy && !sz) {
+ sy = sz = sx;
+ } else if (sx && sy && !sz) {
+ sz = 1;
+ }
+
+ if (sx && sy && sz) {
+ this.elements[0] *= sx;
+ this.elements[1] *= sy;
+ this.elements[2] *= sz;
+ this.elements[4] *= sx;
+ this.elements[5] *= sy;
+ this.elements[6] *= sz;
+ this.elements[8] *= sx;
+ this.elements[9] *= sy;
+ this.elements[10] *= sz;
+ this.elements[12] *= sx;
+ this.elements[13] *= sy;
+ this.elements[14] *= sz;
+ }
+ },
+ /**
+ * @member PMatrix3D
+ * The skewX() function skews the matrix along the x-axis the amount specified by the angle parameter.
+ * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
+ *
+ * @param {float} angle angle of skew specified in radians
+ */
+ skewX: function(angle) {
+ var t = Math.tan(angle);
+ this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+ },
+ /**
+ * @member PMatrix3D
+ * The skewY() function skews the matrix along the y-axis the amount specified by the angle parameter.
+ * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
+ *
+ * @param {float} angle angle of skew specified in radians
+ */
+ skewY: function(angle) {
+ var t = Math.tan(angle);
+ this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+ },
+ /**
+ * @member PMatrix3D
+ * The shearX() function shears the matrix along the x-axis the amount specified by the angle parameter.
+ * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
+ *
+ * @param {float} angle angle of shear specified in radians
+ */
+ shearX: function(angle) {
+ var t = Math.tan(angle);
+ this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+ },
+ /**
+ * @member PMatrix3D
+ * The shearY() function shears the matrix along the y-axis the amount specified by the angle parameter.
+ * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
+ *
+ * @param {float} angle angle of shear specified in radians
+ */
+ shearY: function(angle) {
+ var t = Math.tan(angle);
+ this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+ },
+ multX: function(x, y, z, w) {
+ if (!z) {
+ return this.elements[0] * x + this.elements[1] * y + this.elements[3];
+ }
+ if (!w) {
+ return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
+ }
+ return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w;
+ },
+ multY: function(x, y, z, w) {
+ if (!z) {
+ return this.elements[4] * x + this.elements[5] * y + this.elements[7];
+ }
+ if (!w) {
+ return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
+ }
+ return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w;
+ },
+ multZ: function(x, y, z, w) {
+ if (!w) {
+ return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
+ }
+ return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w;
+ },
+ multW: function(x, y, z, w) {
+ if (!w) {
+ return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15];
+ }
+ return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w;
+ },
+ /**
+ * @member PMatrix3D
+ * The invert() function inverts this matrix
+ *
+ * @return {boolean} true if successful
+ */
+ invert: function() {
+ var fA0 = this.elements[0] * this.elements[5] - this.elements[1] * this.elements[4];
+ var fA1 = this.elements[0] * this.elements[6] - this.elements[2] * this.elements[4];
+ var fA2 = this.elements[0] * this.elements[7] - this.elements[3] * this.elements[4];
+ var fA3 = this.elements[1] * this.elements[6] - this.elements[2] * this.elements[5];
+ var fA4 = this.elements[1] * this.elements[7] - this.elements[3] * this.elements[5];
+ var fA5 = this.elements[2] * this.elements[7] - this.elements[3] * this.elements[6];
+ var fB0 = this.elements[8] * this.elements[13] - this.elements[9] * this.elements[12];
+ var fB1 = this.elements[8] * this.elements[14] - this.elements[10] * this.elements[12];
+ var fB2 = this.elements[8] * this.elements[15] - this.elements[11] * this.elements[12];
+ var fB3 = this.elements[9] * this.elements[14] - this.elements[10] * this.elements[13];
+ var fB4 = this.elements[9] * this.elements[15] - this.elements[11] * this.elements[13];
+ var fB5 = this.elements[10] * this.elements[15] - this.elements[11] * this.elements[14];
+
+ // Determinant
+ var fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0;
+
+ // Account for a very small value
+ // return false if not successful.
+ if (Math.abs(fDet) <= 1e-9) {
+ return false;
+ }
+
+ var kInv = [];
+ kInv[0] = +this.elements[5] * fB5 - this.elements[6] * fB4 + this.elements[7] * fB3;
+ kInv[4] = -this.elements[4] * fB5 + this.elements[6] * fB2 - this.elements[7] * fB1;
+ kInv[8] = +this.elements[4] * fB4 - this.elements[5] * fB2 + this.elements[7] * fB0;
+ kInv[12] = -this.elements[4] * fB3 + this.elements[5] * fB1 - this.elements[6] * fB0;
+ kInv[1] = -this.elements[1] * fB5 + this.elements[2] * fB4 - this.elements[3] * fB3;
+ kInv[5] = +this.elements[0] * fB5 - this.elements[2] * fB2 + this.elements[3] * fB1;
+ kInv[9] = -this.elements[0] * fB4 + this.elements[1] * fB2 - this.elements[3] * fB0;
+ kInv[13] = +this.elements[0] * fB3 - this.elements[1] * fB1 + this.elements[2] * fB0;
+ kInv[2] = +this.elements[13] * fA5 - this.elements[14] * fA4 + this.elements[15] * fA3;
+ kInv[6] = -this.elements[12] * fA5 + this.elements[14] * fA2 - this.elements[15] * fA1;
+ kInv[10] = +this.elements[12] * fA4 - this.elements[13] * fA2 + this.elements[15] * fA0;
+ kInv[14] = -this.elements[12] * fA3 + this.elements[13] * fA1 - this.elements[14] * fA0;
+ kInv[3] = -this.elements[9] * fA5 + this.elements[10] * fA4 - this.elements[11] * fA3;
+ kInv[7] = +this.elements[8] * fA5 - this.elements[10] * fA2 + this.elements[11] * fA1;
+ kInv[11] = -this.elements[8] * fA4 + this.elements[9] * fA2 - this.elements[11] * fA0;
+ kInv[15] = +this.elements[8] * fA3 - this.elements[9] * fA1 + this.elements[10] * fA0;
+
+ // Inverse using Determinant
+ var fInvDet = 1.0 / fDet;
+ kInv[0] *= fInvDet;
+ kInv[1] *= fInvDet;
+ kInv[2] *= fInvDet;
+ kInv[3] *= fInvDet;
+ kInv[4] *= fInvDet;
+ kInv[5] *= fInvDet;
+ kInv[6] *= fInvDet;
+ kInv[7] *= fInvDet;
+ kInv[8] *= fInvDet;
+ kInv[9] *= fInvDet;
+ kInv[10] *= fInvDet;
+ kInv[11] *= fInvDet;
+ kInv[12] *= fInvDet;
+ kInv[13] *= fInvDet;
+ kInv[14] *= fInvDet;
+ kInv[15] *= fInvDet;
+
+ this.elements = kInv.slice();
+ return true;
+ },
+ toString: function() {
+ var str = "";
+ for (var i = 0; i < 15; i++) {
+ str += this.elements[i] + ", ";
+ }
+ str += this.elements[15];
+ return str;
+ },
+ /**
+ * @member PMatrix3D
+ * The print() function prints out the elements of this matrix
+ */
+ print: function() {
+ var digits = printMatrixHelper(this.elements);
+
+ var output = "" + p.nfs(this.elements[0], digits, 4) + " " + p.nfs(this.elements[1], digits, 4) +
+ " " + p.nfs(this.elements[2], digits, 4) + " " + p.nfs(this.elements[3], digits, 4) +
+ "\n" + p.nfs(this.elements[4], digits, 4) + " " + p.nfs(this.elements[5], digits, 4) +
+ " " + p.nfs(this.elements[6], digits, 4) + " " + p.nfs(this.elements[7], digits, 4) +
+ "\n" + p.nfs(this.elements[8], digits, 4) + " " + p.nfs(this.elements[9], digits, 4) +
+ " " + p.nfs(this.elements[10], digits, 4) + " " + p.nfs(this.elements[11], digits, 4) +
+ "\n" + p.nfs(this.elements[12], digits, 4) + " " + p.nfs(this.elements[13], digits, 4) +
+ " " + p.nfs(this.elements[14], digits, 4) + " " + p.nfs(this.elements[15], digits, 4) + "\n\n";
+ p.println(output);
+ },
+ invTranslate: function(tx, ty, tz) {
+ this.preApply(1, 0, 0, -tx, 0, 1, 0, -ty, 0, 0, 1, -tz, 0, 0, 0, 1);
+ },
+ invRotateX: function(angle) {
+ var c = Math.cos(-angle);
+ var s = Math.sin(-angle);
+ this.preApply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]);
+ },
+ invRotateY: function(angle) {
+ var c = Math.cos(-angle);
+ var s = Math.sin(-angle);
+ this.preApply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]);
+ },
+ invRotateZ: function(angle) {
+ var c = Math.cos(-angle);
+ var s = Math.sin(-angle);
+ this.preApply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
+ },
+ invScale: function(x, y, z) {
+ this.preApply([1 / x, 0, 0, 0, 0, 1 / y, 0, 0, 0, 0, 1 / z, 0, 0, 0, 0, 1]);
+ }
+ };
+
+ return PMatrix3D;
+};
+},{}],16:[function(require,module,exports){
+module.exports = function(options) {
+ var PConstants = options.PConstants,
+ PMatrix2D = options.PMatrix2D,
+ PMatrix3D = options.PMatrix3D;
+
+ /**
+ * Datatype for storing shapes. Processing can currently load and display SVG (Scalable Vector Graphics) shapes.
+ * Before a shape is used, it must be loaded with the <b>loadShape()</b> function. The <b>shape()</b> function is used to draw the shape to the display window.
+ * The <b>PShape</b> object contain a group of methods, linked below, that can operate on the shape data.
+ * <br><br>The <b>loadShape()</b> method supports SVG files created with Inkscape and Adobe Illustrator.
+ * It is not a full SVG implementation, but offers some straightforward support for handling vector data.
+ *
+ * @param {int} family the shape type, one of GROUP, PRIMITIVE, PATH, or GEOMETRY
+ *
+ * @see #shape()
+ * @see #loadShape()
+ * @see #shapeMode()
+ */
+ var PShape = function(family) {
+ this.family = family || PConstants.GROUP;
+ this.visible = true;
+ this.style = true;
+ this.children = [];
+ this.nameTable = [];
+ this.params = [];
+ this.name = "";
+ this.image = null; //type PImage
+ this.matrix = null;
+ this.kind = null;
+ this.close = null;
+ this.width = null;
+ this.height = null;
+ this.parent = null;
+ };
+ /**
+ * PShape methods
+ * missing: findChild(), apply(), contains(), findChild(), getPrimitive(), getParams(), getVertex() , getVertexCount(),
+ * getVertexCode() , getVertexCodes() , getVertexCodeCount(), getVertexX(), getVertexY(), getVertexZ()
+ */
+ PShape.prototype = {
+ /**
+ * @member PShape
+ * The isVisible() function returns a boolean value "true" if the image is set to be visible, "false" if not. This is modified with the <b>setVisible()</b> parameter.
+ * <br><br>The visibility of a shape is usually controlled by whatever program created the SVG file.
+ * For instance, this parameter is controlled by showing or hiding the shape in the layers palette in Adobe Illustrator.
+ *
+ * @return {boolean} returns "true" if the image is set to be visible, "false" if not
+ */
+ isVisible: function(){
+ return this.visible;
+ },
+ /**
+ * @member PShape
+ * The setVisible() function sets the shape to be visible or invisible. This is determined by the value of the <b>visible</b> parameter.
+ * <br><br>The visibility of a shape is usually controlled by whatever program created the SVG file.
+ * For instance, this parameter is controlled by showing or hiding the shape in the layers palette in Adobe Illustrator.
+ *
+ * @param {boolean} visible "false" makes the shape invisible and "true" makes it visible
+ */
+ setVisible: function (visible){
+ this.visible = visible;
+ },
+ /**
+ * @member PShape
+ * The disableStyle() function disables the shape's style data and uses Processing's current styles. Styles include attributes such as colors, stroke weight, and stroke joints.
+ * Overrides this shape's style information and uses PGraphics styles and colors. Identical to ignoreStyles(true). Also disables styles for all child shapes.
+ */
+ disableStyle: function(){
+ this.style = false;
+ for(var i = 0, j=this.children.length; i<j; i++) {
+ this.children[i].disableStyle();
+ }
+ },
+ /**
+ * @member PShape
+ * The enableStyle() function enables the shape's style data and ignores Processing's current styles. Styles include attributes such as colors, stroke weight, and stroke joints.
+ */
+ enableStyle: function(){
+ this.style = true;
+ for(var i = 0, j=this.children.length; i<j; i++) {
+ this.children[i].enableStyle();
+ }
+ },
+ /**
+ * @member PShape
+ * The getFamily function returns the shape type
+ *
+ * @return {int} the shape type, one of GROUP, PRIMITIVE, PATH, or GEOMETRY
+ */
+ getFamily: function(){
+ return this.family;
+ },
+ /**
+ * @member PShape
+ * The getWidth() function gets the width of the drawing area (not necessarily the shape boundary).
+ */
+ getWidth: function(){
+ return this.width;
+ },
+ /**
+ * @member PShape
+ * The getHeight() function gets the height of the drawing area (not necessarily the shape boundary).
+ */
+ getHeight: function(){
+ return this.height;
+ },
+ /**
+ * @member PShape
+ * The setName() function sets the name of the shape
+ *
+ * @param {String} name the name of the shape
+ */
+ setName: function(name){
+ this.name = name;
+ },
+ /**
+ * @member PShape
+ * The getName() function returns the name of the shape
+ *
+ * @return {String} the name of the shape
+ */
+ getName: function(){
+ return this.name;
+ },
+ /**
+ * @member PShape
+ * Called by the following (the shape() command adds the g)
+ * PShape s = loadShapes("blah.svg");
+ * shape(s);
+ */
+ draw: function(renderContext) {
+ if(!renderContext) {
+ throw "render context missing for draw() in PShape";
+ }
+ if (this.visible) {
+ this.pre(renderContext);
+ this.drawImpl(renderContext);
+ this.post(renderContext);
+ }
+ },
+ /**
+ * @member PShape
+ * the drawImpl() function draws the SVG document.
+ */
+ drawImpl: function(renderContext) {
+ if (this.family === PConstants.GROUP) {
+ this.drawGroup(renderContext);
+ } else if (this.family === PConstants.PRIMITIVE) {
+ this.drawPrimitive(renderContext);
+ } else if (this.family === PConstants.GEOMETRY) {
+ this.drawGeometry(renderContext);
+ } else if (this.family === PConstants.PATH) {
+ this.drawPath(renderContext);
+ }
+ },
+ /**
+ * @member PShape
+ * The drawPath() function draws the <path> part of the SVG document.
+ */
+ drawPath: function(renderContext) {
+ var i, j;
+ if (this.vertices.length === 0) { return; }
+ renderContext.beginShape();
+ if (this.vertexCodes.length === 0) { // each point is a simple vertex
+ if (this.vertices[0].length === 2) { // drawing 2D vertices
+ for (i = 0, j = this.vertices.length; i < j; i++) {
+ renderContext.vertex(this.vertices[i][0], this.vertices[i][1]);
+ }
+ } else { // drawing 3D vertices
+ for (i = 0, j = this.vertices.length; i < j; i++) {
+ renderContext.vertex(this.vertices[i][0],
+ this.vertices[i][1],
+ this.vertices[i][2]);
+ }
+ }
+ } else { // coded set of vertices
+ var index = 0;
+ if (this.vertices[0].length === 2) { // drawing a 2D path
+ for (i = 0, j = this.vertexCodes.length; i < j; i++) {
+ if (this.vertexCodes[i] === PConstants.VERTEX) {
+ renderContext.vertex(this.vertices[index][0], this.vertices[index][1], this.vertices[index].moveTo);
+ renderContext.breakShape = false;
+ index++;
+ } else if (this.vertexCodes[i] === PConstants.BEZIER_VERTEX) {
+ renderContext.bezierVertex(this.vertices[index+0][0],
+ this.vertices[index+0][1],
+ this.vertices[index+1][0],
+ this.vertices[index+1][1],
+ this.vertices[index+2][0],
+ this.vertices[index+2][1]);
+ index += 3;
+ } else if (this.vertexCodes[i] === PConstants.CURVE_VERTEX) {
+ renderContext.curveVertex(this.vertices[index][0],
+ this.vertices[index][1]);
+ index++;
+ } else if (this.vertexCodes[i] === PConstants.BREAK) {
+ renderContext.breakShape = true;
+ }
+ }
+ } else { // drawing a 3D path
+ for (i = 0, j = this.vertexCodes.length; i < j; i++) {
+ if (this.vertexCodes[i] === PConstants.VERTEX) {
+ renderContext.vertex(this.vertices[index][0],
+ this.vertices[index][1],
+ this.vertices[index][2]);
+ if (this.vertices[index].moveTo === true) {
+ vertArray[vertArray.length-1].moveTo = true;
+ } else if (this.vertices[index].moveTo === false) {
+ vertArray[vertArray.length-1].moveTo = false;
+ }
+ renderContext.breakShape = false;
+ } else if (this.vertexCodes[i] === PConstants.BEZIER_VERTEX) {
+ renderContext.bezierVertex(this.vertices[index+0][0],
+ this.vertices[index+0][1],
+ this.vertices[index+0][2],
+ this.vertices[index+1][0],
+ this.vertices[index+1][1],
+ this.vertices[index+1][2],
+ this.vertices[index+2][0],
+ this.vertices[index+2][1],
+ this.vertices[index+2][2]);
+ index += 3;
+ } else if (this.vertexCodes[i] === PConstants.CURVE_VERTEX) {
+ renderContext.curveVertex(this.vertices[index][0],
+ this.vertices[index][1],
+ this.vertices[index][2]);
+ index++;
+ } else if (this.vertexCodes[i] === PConstants.BREAK) {
+ renderContext.breakShape = true;
+ }
+ }
+ }
+ }
+ renderContext.endShape(this.close ? PConstants.CLOSE : PConstants.OPEN);
+ },
+ /**
+ * @member PShape
+ * The drawGeometry() function draws the geometry part of the SVG document.
+ */
+ drawGeometry: function(renderContext) {
+ var i, j;
+ renderContext.beginShape(this.kind);
+ if (this.style) {
+ for (i = 0, j = this.vertices.length; i < j; i++) {
+ renderContext.vertex(this.vertices[i]);
+ }
+ } else {
+ for (i = 0, j = this.vertices.length; i < j; i++) {
+ var vert = this.vertices[i];
+ if (vert[2] === 0) {
+ renderContext.vertex(vert[0], vert[1]);
+ } else {
+ renderContext.vertex(vert[0], vert[1], vert[2]);
+ }
+ }
+ }
+ renderContext.endShape();
+ },
+ /**
+ * @member PShape
+ * The drawGroup() function draws the <g> part of the SVG document.
+ */
+ drawGroup: function(renderContext) {
+ for (var i = 0, j = this.children.length; i < j; i++) {
+ this.children[i].draw(renderContext);
+ }
+ },
+ /**
+ * @member PShape
+ * The drawPrimitive() function draws SVG document shape elements. These can be point, line, triangle, quad, rect, ellipse, arc, box, or sphere.
+ */
+ drawPrimitive: function(renderContext) {
+ if (this.kind === PConstants.POINT) {
+ renderContext.point(this.params[0], this.params[1]);
+ } else if (this.kind === PConstants.LINE) {
+ if (this.params.length === 4) { // 2D
+ renderContext.line(this.params[0], this.params[1],
+ this.params[2], this.params[3]);
+ } else { // 3D
+ renderContext.line(this.params[0], this.params[1], this.params[2],
+ this.params[3], this.params[4], this.params[5]);
+ }
+ } else if (this.kind === PConstants.TRIANGLE) {
+ renderContext.triangle(this.params[0], this.params[1],
+ this.params[2], this.params[3],
+ this.params[4], this.params[5]);
+ } else if (this.kind === PConstants.QUAD) {
+ renderContext.quad(this.params[0], this.params[1],
+ this.params[2], this.params[3],
+ this.params[4], this.params[5],
+ this.params[6], this.params[7]);
+ } else if (this.kind === PConstants.RECT) {
+ if (this.image !== null) {
+ var imMode = imageModeConvert;
+ renderContext.imageMode(PConstants.CORNER);
+ renderContext.image(this.image,
+ this.params[0],
+ this.params[1],
+ this.params[2],
+ this.params[3]);
+ imageModeConvert = imMode;
+ } else {
+ var rcMode = renderContext.curRectMode;
+ renderContext.rectMode(PConstants.CORNER);
+ renderContext.rect(this.params[0],
+ this.params[1],
+ this.params[2],
+ this.params[3]);
+ renderContext.curRectMode = rcMode;
+ }
+ } else if (this.kind === PConstants.ELLIPSE) {
+ var elMode = renderContext.curEllipseMode;
+ renderContext.ellipseMode(PConstants.CORNER);
+ renderContext.ellipse(this.params[0],
+ this.params[1],
+ this.params[2],
+ this.params[3]);
+ renderContext.curEllipseMode = elMode;
+ } else if (this.kind === PConstants.ARC) {
+ var eMode = curEllipseMode;
+ renderContext.ellipseMode(PConstants.CORNER);
+ renderContext.arc(this.params[0],
+ this.params[1],
+ this.params[2],
+ this.params[3],
+ this.params[4],
+ this.params[5]);
+ curEllipseMode = eMode;
+ } else if (this.kind === PConstants.BOX) {
+ if (this.params.length === 1) {
+ renderContext.box(this.params[0]);
+ } else {
+ renderContext.box(this.params[0], this.params[1], this.params[2]);
+ }
+ } else if (this.kind === PConstants.SPHERE) {
+ renderContext.sphere(this.params[0]);
+ }
+ },
+ /**
+ * @member PShape
+ * The pre() function performs the preparations before the SVG is drawn. This includes doing transformations and storing previous styles.
+ */
+ pre: function(renderContext) {
+ if (this.matrix) {
+ renderContext.pushMatrix();
+ renderContext.transform(this.matrix);
+ }
+ if (this.style) {
+ renderContext.pushStyle();
+ this.styles(renderContext);
+ }
+ },
+ /**
+ * @member PShape
+ * The post() function performs the necessary actions after the SVG is drawn. This includes removing transformations and removing added styles.
+ */
+ post: function(renderContext) {
+ if (this.matrix) {
+ renderContext.popMatrix();
+ }
+ if (this.style) {
+ renderContext.popStyle();
+ }
+ },
+ /**
+ * @member PShape
+ * The styles() function changes the Processing's current styles
+ */
+ styles: function(renderContext) {
+ if (this.stroke) {
+ renderContext.stroke(this.strokeColor);
+ renderContext.strokeWeight(this.strokeWeight);
+ renderContext.strokeCap(this.strokeCap);
+ renderContext.strokeJoin(this.strokeJoin);
+ } else {
+ renderContext.noStroke();
+ }
+
+ if (this.fill) {
+ renderContext.fill(this.fillColor);
+
+ } else {
+ renderContext.noFill();
+ }
+ },
+ /**
+ * @member PShape
+ * The getChild() function extracts a child shape from a parent shape. Specify the name of the shape with the <b>target</b> parameter or the
+ * layer position of the shape to get with the <b>index</b> parameter.
+ * The shape is returned as a <b>PShape</b> object, or <b>null</b> is returned if there is an error.
+ *
+ * @param {String} target the name of the shape to get
+ * @param {int} index the layer position of the shape to get
+ *
+ * @return {PShape} returns a child element of a shape as a PShape object or null if there is an error
+ */
+ getChild: function(child) {
+ var i, j;
+ if (typeof child === 'number') {
+ return this.children[child];
+ }
+ var found;
+ if(child === "" || this.name === child){
+ return this;
+ }
+ if(this.nameTable.length > 0) {
+ for(i = 0, j = this.nameTable.length; i < j || found; i++) {
+ if(this.nameTable[i].getName === child) {
+ found = this.nameTable[i];
+ break;
+ }
+ }
+ if (found) { return found; }
+ }
+ for(i = 0, j = this.children.length; i < j; i++) {
+ found = this.children[i].getChild(child);
+ if(found) { return found; }
+ }
+ return null;
+ },
+ /**
+ * @member PShape
+ * The getChildCount() returns the number of children
+ *
+ * @return {int} returns a count of children
+ */
+ getChildCount: function () {
+ return this.children.length;
+ },
+ /**
+ * @member PShape
+ * The addChild() adds a child to the PShape.
+ *
+ * @param {PShape} child the child to add
+ */
+ addChild: function( child ) {
+ this.children.push(child);
+ child.parent = this;
+ if (child.getName() !== null) {
+ this.addName(child.getName(), child);
+ }
+ },
+ /**
+ * @member PShape
+ * The addName() functions adds a shape to the name lookup table.
+ *
+ * @param {String} name the name to be added
+ * @param {PShape} shape the shape
+ */
+ addName: function(name, shape) {
+ if (this.parent !== null) {
+ this.parent.addName( name, shape );
+ } else {
+ this.nameTable.push( [name, shape] );
+ }
+ },
+ /**
+ * @member PShape
+ * The translate() function specifies an amount to displace the shape. The <b>x</b> parameter specifies left/right translation, the <b>y</b> parameter specifies up/down translation, and the <b>z</b> parameter specifies translations toward/away from the screen.
+ * Subsequent calls to the method accumulates the effect. For example, calling <b>translate(50, 0)</b> and then <b>translate(20, 0)</b> is the same as <b>translate(70, 0)</b>.
+ * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
+ * <br><br>Using this method with the <b>z</b> parameter requires using the P3D or OPENGL parameter in combination with size.
+ *
+ * @param {int|float} x left/right translation
+ * @param {int|float} y up/down translation
+ * @param {int|float} z forward/back translation
+ *
+ * @see PMatrix2D#translate
+ * @see PMatrix3D#translate
+ */
+ translate: function() {
+ if(arguments.length === 2)
+ {
+ this.checkMatrix(2);
+ this.matrix.translate(arguments[0], arguments[1]);
+ } else {
+ this.checkMatrix(3);
+ this.matrix.translate(arguments[0], arguments[1], 0);
+ }
+ },
+ /**
+ * @member PShape
+ * The checkMatrix() function makes sure that the shape's matrix is 1) not null, and 2) has a matrix
+ * that can handle <em>at least</em> the specified number of dimensions.
+ *
+ * @param {int} dimensions the specified number of dimensions
+ */
+ checkMatrix: function(dimensions) {
+ if(this.matrix === null) {
+ if(dimensions === 2) {
+ this.matrix = new PMatrix2D();
+ } else {
+ this.matrix = new PMatrix3D();
+ }
+ }else if(dimensions === 3 && this.matrix instanceof PMatrix2D) {
+ this.matrix = new PMatrix3D();
+ }
+ },
+ /**
+ * @member PShape
+ * The rotateX() function rotates a shape around the x-axis the amount specified by the <b>angle</b> parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the <b>radians()</b> method.
+ * <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
+ * Subsequent calls to the method accumulates the effect. For example, calling <b>rotateX(HALF_PI)</b> and then <b>rotateX(HALF_PI)</b> is the same as <b>rotateX(PI)</b>.
+ * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
+ * <br><br>This method requires a 3D renderer. You need to pass P3D or OPENGL as a third parameter into the <b>size()</b> method as shown in the example above.
+ *
+ * @param {float}angle angle of rotation specified in radians
+ *
+ * @see PMatrix3D#rotateX
+ */
+ rotateX: function(angle) {
+ this.rotate(angle, 1, 0, 0);
+ },
+ /**
+ * @member PShape
+ * The rotateY() function rotates a shape around the y-axis the amount specified by the <b>angle</b> parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the <b>radians()</b> method.
+ * <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
+ * Subsequent calls to the method accumulates the effect. For example, calling <b>rotateY(HALF_PI)</b> and then <b>rotateY(HALF_PI)</b> is the same as <b>rotateY(PI)</b>.
+ * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
+ * <br><br>This method requires a 3D renderer. You need to pass P3D or OPENGL as a third parameter into the <b>size()</b> method as shown in the example above.
+ *
+ * @param {float}angle angle of rotation specified in radians
+ *
+ * @see PMatrix3D#rotateY
+ */
+ rotateY: function(angle) {
+ this.rotate(angle, 0, 1, 0);
+ },
+ /**
+ * @member PShape
+ * The rotateZ() function rotates a shape around the z-axis the amount specified by the <b>angle</b> parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the <b>radians()</b> method.
+ * <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
+ * Subsequent calls to the method accumulates the effect. For example, calling <b>rotateZ(HALF_PI)</b> and then <b>rotateZ(HALF_PI)</b> is the same as <b>rotateZ(PI)</b>.
+ * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
+ * <br><br>This method requires a 3D renderer. You need to pass P3D or OPENGL as a third parameter into the <b>size()</b> method as shown in the example above.
+ *
+ * @param {float}angle angle of rotation specified in radians
+ *
+ * @see PMatrix3D#rotateZ
+ */
+ rotateZ: function(angle) {
+ this.rotate(angle, 0, 0, 1);
+ },
+ /**
+ * @member PShape
+ * The rotate() function rotates a shape the amount specified by the <b>angle</b> parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the <b>radians()</b> method.
+ * <br><br>Shapes are always rotated around the upper-left corner of their bounding box. Positive numbers rotate objects in a clockwise direction.
+ * Transformations apply to everything that happens after and subsequent calls to the method accumulates the effect.
+ * For example, calling <b>rotate(HALF_PI)</b> and then <b>rotate(HALF_PI)</b> is the same as <b>rotate(PI)</b>.
+ * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
+ * If optional parameters x,y,z are supplied, the rotate is about the point (x, y, z).
+ *
+ * @param {float}angle angle of rotation specified in radians
+ * @param {float}x x-coordinate of the point
+ * @param {float}y y-coordinate of the point
+ * @param {float}z z-coordinate of the point
+ * @see PMatrix2D#rotate
+ * @see PMatrix3D#rotate
+ */
+ rotate: function() {
+ if(arguments.length === 1){
+ this.checkMatrix(2);
+ this.matrix.rotate(arguments[0]);
+ } else {
+ this.checkMatrix(3);
+ this.matrix.rotate(arguments[0],
+ arguments[1],
+ arguments[2],
+ arguments[3]);
+ }
+ },
+ /**
+ * @member PShape
+ * The scale() function increases or decreases the size of a shape by expanding and contracting vertices. Shapes always scale from the relative origin of their bounding box.
+ * Scale values are specified as decimal percentages. For example, the method call <b>scale(2.0)</b> increases the dimension of a shape by 200%.
+ * Subsequent calls to the method multiply the effect. For example, calling <b>scale(2.0)</b> and then <b>scale(1.5)</b> is the same as <b>scale(3.0)</b>.
+ * This transformation is applied directly to the shape, it's not refreshed each time <b>draw()</b> is run.
+ * <br><br>Using this fuction with the <b>z</b> parameter requires passing P3D or OPENGL into the size() parameter.
+ *
+ * @param {float}s percentage to scale the object
+ * @param {float}x percentage to scale the object in the x-axis
+ * @param {float}y percentage to scale the object in the y-axis
+ * @param {float}z percentage to scale the object in the z-axis
+ *
+ * @see PMatrix2D#scale
+ * @see PMatrix3D#scale
+ */
+ scale: function() {
+ if(arguments.length === 2) {
+ this.checkMatrix(2);
+ this.matrix.scale(arguments[0], arguments[1]);
+ } else if (arguments.length === 3) {
+ this.checkMatrix(2);
+ this.matrix.scale(arguments[0], arguments[1], arguments[2]);
+ } else {
+ this.checkMatrix(2);
+ this.matrix.scale(arguments[0]);
+ }
+ },
+ /**
+ * @member PShape
+ * The resetMatrix() function resets the matrix
+ *
+ * @see PMatrix2D#reset
+ * @see PMatrix3D#reset
+ */
+ resetMatrix: function() {
+ this.checkMatrix(2);
+ this.matrix.reset();
+ },
+ /**
+ * @member PShape
+ * The applyMatrix() function multiplies this matrix by another matrix of type PMatrix3D or PMatrix2D.
+ * Individual elements can also be provided
+ *
+ * @param {PMatrix3D|PMatrix2D} matrix the matrix to multiply by
+ *
+ * @see PMatrix2D#apply
+ * @see PMatrix3D#apply
+ */
+ applyMatrix: function(matrix) {
+ if (arguments.length === 1) {
+ this.applyMatrix(matrix.elements[0],
+ matrix.elements[1], 0,
+ matrix.elements[2],
+ matrix.elements[3],
+ matrix.elements[4], 0,
+ matrix.elements[5],
+ 0, 0, 1, 0,
+ 0, 0, 0, 1);
+ } else if (arguments.length === 6) {
+ this.checkMatrix(2);
+ this.matrix.apply(arguments[0], arguments[1], arguments[2], 0,
+ arguments[3], arguments[4], arguments[5], 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1);
+
+ } else if (arguments.length === 16) {
+ this.checkMatrix(3);
+ this.matrix.apply(arguments[0],
+ arguments[1],
+ arguments[2],
+ arguments[3],
+ arguments[4],
+ arguments[5],
+ arguments[6],
+ arguments[7],
+ arguments[8],
+ arguments[9],
+ arguments[10],
+ arguments[11],
+ arguments[12],
+ arguments[13],
+ arguments[14],
+ arguments[15]);
+ }
+ }
+ };
+
+ return PShape;
+};
+},{}],17:[function(require,module,exports){
+/**
+ * SVG stands for Scalable Vector Graphics, a portable graphics format. It is
+ * a vector format so it allows for infinite resolution and relatively small
+ * file sizes. Most modern media software can view SVG files, including Adobe
+ * products, Firefox, etc. Illustrator and Inkscape can edit SVG files.
+ *
+ * @param {PApplet} parent typically use "this"
+ * @param {String} filename name of the SVG file to load
+ * @param {XMLElement} xml an XMLElement element
+ * @param {PShapeSVG} parent the parent PShapeSVG
+ *
+ * @see PShape
+ */
+module.exports = function(options) {
+ var CommonFunctions = options.CommonFunctions,
+ PConstants = options.PConstants,
+ PShape = options.PShape,
+ XMLElement = options.XMLElement,
+ colors = options.colors;
+
+ var PShapeSVG = function() {
+ PShape.call(this); // PShape is the base class.
+ if (arguments.length === 1) { // xml element coming in
+ this.element = arguments[0];
+
+ // set values to their defaults according to the SVG spec
+ this.vertexCodes = [];
+ this.vertices = [];
+ this.opacity = 1;
+
+ this.stroke = false;
+ this.strokeColor = PConstants.ALPHA_MASK;
+ this.strokeWeight = 1;
+ this.strokeCap = PConstants.SQUARE; // BUTT in svg spec
+ this.strokeJoin = PConstants.MITER;
+ this.strokeGradient = null;
+ this.strokeGradientPaint = null;
+ this.strokeName = null;
+ this.strokeOpacity = 1;
+
+ this.fill = true;
+ this.fillColor = PConstants.ALPHA_MASK;
+ this.fillGradient = null;
+ this.fillGradientPaint = null;
+ this.fillName = null;
+ this.fillOpacity = 1;
+
+ if (this.element.getName() !== "svg") {
+ throw("root is not <svg>, it's <" + this.element.getName() + ">");
+ }
+ }
+ else if (arguments.length === 2) {
+ if (typeof arguments[1] === 'string') {
+ if (arguments[1].indexOf(".svg") > -1) { //its a filename
+ this.element = new XMLElement(true, arguments[1]);
+ // set values to their defaults according to the SVG spec
+ this.vertexCodes = [];
+ this.vertices = [];
+ this.opacity = 1;
+
+ this.stroke = false;
+ this.strokeColor = PConstants.ALPHA_MASK;
+ this.strokeWeight = 1;
+ this.strokeCap = PConstants.SQUARE; // BUTT in svg spec
+ this.strokeJoin = PConstants.MITER;
+ this.strokeGradient = "";
+ this.strokeGradientPaint = "";
+ this.strokeName = "";
+ this.strokeOpacity = 1;
+
+ this.fill = true;
+ this.fillColor = PConstants.ALPHA_MASK;
+ this.fillGradient = null;
+ this.fillGradientPaint = null;
+ this.fillOpacity = 1;
+
+ }
+ } else { // XMLElement
+ if (arguments[0]) { // PShapeSVG
+ this.element = arguments[1];
+ this.vertexCodes = arguments[0].vertexCodes.slice();
+ this.vertices = arguments[0].vertices.slice();
+
+ this.stroke = arguments[0].stroke;
+ this.strokeColor = arguments[0].strokeColor;
+ this.strokeWeight = arguments[0].strokeWeight;
+ this.strokeCap = arguments[0].strokeCap;
+ this.strokeJoin = arguments[0].strokeJoin;
+ this.strokeGradient = arguments[0].strokeGradient;
+ this.strokeGradientPaint = arguments[0].strokeGradientPaint;
+ this.strokeName = arguments[0].strokeName;
+
+ this.fill = arguments[0].fill;
+ this.fillColor = arguments[0].fillColor;
+ this.fillGradient = arguments[0].fillGradient;
+ this.fillGradientPaint = arguments[0].fillGradientPaint;
+ this.fillName = arguments[0].fillName;
+ this.strokeOpacity = arguments[0].strokeOpacity;
+ this.fillOpacity = arguments[0].fillOpacity;
+ this.opacity = arguments[0].opacity;
+ }
+ }
+ }
+
+ this.name = this.element.getStringAttribute("id");
+ var displayStr = this.element.getStringAttribute("display", "inline");
+ this.visible = displayStr !== "none";
+ var str = this.element.getAttribute("transform");
+ if (str) {
+ this.matrix = this.parseMatrix(str);
+ }
+ // not proper parsing of the viewBox, but will cover us for cases where
+ // the width and height of the object is not specified
+ var viewBoxStr = this.element.getStringAttribute("viewBox");
+ if ( viewBoxStr !== null ) {
+ var viewBox = viewBoxStr.split(" ");
+ this.width = viewBox[2];
+ this.height = viewBox[3];
+ }
+
+ // TODO if viewbox is not same as width/height, then use it to scale
+ // the original objects. for now, viewbox only used when width/height
+ // are empty values (which by the spec means w/h of "100%"
+ var unitWidth = this.element.getStringAttribute("width");
+ var unitHeight = this.element.getStringAttribute("height");
+ if (unitWidth !== null) {
+ this.width = this.parseUnitSize(unitWidth);
+ this.height = this.parseUnitSize(unitHeight);
+ } else {
+ if ((this.width === 0) || (this.height === 0)) {
+ // For the spec, the default is 100% and 100%. For purposes
+ // here, insert a dummy value because this is prolly just a
+ // font or something for which the w/h doesn't matter.
+ this.width = 1;
+ this.height = 1;
+
+ //show warning
+ throw("The width and/or height is not " +
+ "readable in the <svg> tag of this file.");
+ }
+ }
+ this.parseColors(this.element);
+ this.parseChildren(this.element);
+
+ };
+ /**
+ * PShapeSVG methods are inherited from the PShape prototype
+ */
+ PShapeSVG.prototype = new PShape();
+ /**
+ * @member PShapeSVG
+ * The parseMatrix() function parses the specified SVG matrix into a PMatrix2D. Note that PMatrix2D
+ * is rotated relative to the SVG definition, so parameters are rearranged
+ * here. More about the transformation matrices in
+ * <a href="http://www.w3.org/TR/SVG/coords.html#TransformAttribute">this section</a>
+ * of the SVG documentation.
+ *
+ * @param {String} str text of the matrix param.
+ *
+ * @return {PMatrix2D} a PMatrix2D
+ */
+ PShapeSVG.prototype.parseMatrix = (function() {
+ function getCoords(s) {
+ var m = [];
+ s.replace(/\((.*?)\)/, (function() {
+ return function(all, params) {
+ // get the coordinates that can be separated by spaces or a comma
+ m = params.replace(/,+/g, " ").split(/\s+/);
+ };
+ }()));
+ return m;
+ }
+
+ return function(str) {
+ this.checkMatrix(2);
+ var pieces = [];
+ str.replace(/\s*(\w+)\((.*?)\)/g, function(all) {
+ // get a list of transform definitions
+ pieces.push(CommonFunctions.trim(all));
+ });
+ if (pieces.length === 0) {
+ return null;
+ }
+
+ for (var i = 0, j = pieces.length; i < j; i++) {
+ var m = getCoords(pieces[i]);
+
+ if (pieces[i].indexOf("matrix") !== -1) {
+ this.matrix.set(m[0], m[2], m[4], m[1], m[3], m[5]);
+ } else if (pieces[i].indexOf("translate") !== -1) {
+ var tx = m[0];
+ var ty = (m.length === 2) ? m[1] : 0;
+ this.matrix.translate(tx,ty);
+ } else if (pieces[i].indexOf("scale") !== -1) {
+ var sx = m[0];
+ var sy = (m.length === 2) ? m[1] : m[0];
+ this.matrix.scale(sx,sy);
+ } else if (pieces[i].indexOf("rotate") !== -1) {
+ var angle = m[0];
+ if (m.length === 1) {
+ this.matrix.rotate(CommonFunctions.radians(angle));
+ } else if (m.length === 3) {
+ this.matrix.translate(m[1], m[2]);
+ this.matrix.rotate(CommonFunctions.radians(m[0]));
+ this.matrix.translate(-m[1], -m[2]);
+ }
+ } else if (pieces[i].indexOf("skewX") !== -1) {
+ this.matrix.skewX(parseFloat(m[0]));
+ } else if (pieces[i].indexOf("skewY") !== -1) {
+ this.matrix.skewY(m[0]);
+ } else if (pieces[i].indexOf("shearX") !== -1) {
+ this.matrix.shearX(m[0]);
+ } else if (pieces[i].indexOf("shearY") !== -1) {
+ this.matrix.shearY(m[0]);
+ }
+ }
+ return this.matrix;
+ };
+ }());
+
+ /**
+ * @member PShapeSVG
+ * The parseChildren() function parses the specified XMLElement
+ *
+ * @param {XMLElement}element the XMLElement to parse
+ */
+ PShapeSVG.prototype.parseChildren = function(element) {
+ var newelement = element.getChildren();
+ var base = new PShape();
+ var i, j;
+ for (i = 0, j = newelement.length; i < j; i++) {
+ var kid = this.parseChild(newelement[i]);
+ if (kid) {
+ base.addChild(kid);
+ }
+ }
+ for (i = 0, j = base.children.length; i < j; i++) {
+ this.children.push(base.children[i]);
+ }
+ };
+ /**
+ * @member PShapeSVG
+ * The getName() function returns the name
+ *
+ * @return {String} the name
+ */
+ PShapeSVG.prototype.getName = function() {
+ return this.name;
+ };
+ /**
+ * @member PShapeSVG
+ * The parseChild() function parses a child XML element.
+ *
+ * @param {XMLElement} elem the element to parse
+ *
+ * @return {PShape} the newly created PShape
+ */
+ PShapeSVG.prototype.parseChild = function( elem ) {
+ var name = elem.getName();
+ var shape;
+ if (name === "g") {
+ shape = new PShapeSVG(this, elem);
+ } else if (name === "defs") {
+ // generally this will contain gradient info, so may
+ // as well just throw it into a group element for parsing
+ shape = new PShapeSVG(this, elem);
+ } else if (name === "line") {
+ shape = new PShapeSVG(this, elem);
+ shape.parseLine();
+ } else if (name === "circle") {
+ shape = new PShapeSVG(this, elem);
+ shape.parseEllipse(true);
+ } else if (name === "ellipse") {
+ shape = new PShapeSVG(this, elem);
+ shape.parseEllipse(false);
+ } else if (name === "rect") {
+ shape = new PShapeSVG(this, elem);
+ shape.parseRect();
+ } else if (name === "polygon") {
+ shape = new PShapeSVG(this, elem);
+ shape.parsePoly(true);
+ } else if (name === "polyline") {
+ shape = new PShapeSVG(this, elem);
+ shape.parsePoly(false);
+ } else if (name === "path") {
+ shape = new PShapeSVG(this, elem);
+ shape.parsePath();
+ } else if (name === "radialGradient") {
+ //return new RadialGradient(this, elem);
+ unimplemented('PShapeSVG.prototype.parseChild, name = radialGradient');
+ } else if (name === "linearGradient") {
+ //return new LinearGradient(this, elem);
+ unimplemented('PShapeSVG.prototype.parseChild, name = linearGradient');
+ } else if (name === "text") {
+ unimplemented('PShapeSVG.prototype.parseChild, name = text');
+ } else if (name === "filter") {
+ unimplemented('PShapeSVG.prototype.parseChild, name = filter');
+ } else if (name === "mask") {
+ unimplemented('PShapeSVG.prototype.parseChild, name = mask');
+ } else {
+ // ignoring
+ }
+ return shape;
+ };
+ /**
+ * @member PShapeSVG
+ * The parsePath() function parses the <path> element of the svg file
+ * A path is defined by including a path element which contains a d="(path data)" attribute, where the d attribute contains
+ * the moveto, line, curve (both cubic and quadratic Beziers), arc and closepath instructions.
+ **/
+ PShapeSVG.prototype.parsePath = function() {
+ this.family = PConstants.PATH;
+ this.kind = 0;
+ var pathDataChars = [];
+ var c;
+ //change multiple spaces and commas to single space
+ var pathData = CommonFunctions.trim(this.element.getStringAttribute("d").replace(/[\s,]+/g,' '));
+ if (pathData === null) {
+ return;
+ }
+ pathData = pathData.split('');
+ var cx = 0,
+ cy = 0,
+ ctrlX = 0,
+ ctrlY = 0,
+ ctrlX1 = 0,
+ ctrlX2 = 0,
+ ctrlY1 = 0,
+ ctrlY2 = 0,
+ endX = 0,
+ endY = 0,
+ ppx = 0,
+ ppy = 0,
+ px = 0,
+ py = 0,
+ i = 0,
+ valOf = 0;
+ var str = "";
+ var tmpArray = [];
+ var flag = false;
+ var lastInstruction;
+ var command;
+ var j, k;
+ while (i< pathData.length) {
+ valOf = pathData[i].charCodeAt(0);
+ if ((valOf >= 65 && valOf <= 90) || (valOf >= 97 && valOf <= 122)) {
+ // if it's a letter
+ // populate the tmpArray with coordinates
+ j = i;
+ i++;
+ if (i < pathData.length) { // don't go over boundary of array
+ tmpArray = [];
+ valOf = pathData[i].charCodeAt(0);
+ while (!((valOf >= 65 && valOf <= 90) ||
+ (valOf >= 97 && valOf <= 100) ||
+ (valOf >= 102 && valOf <= 122)) && flag === false) { // if its NOT a letter
+ if (valOf === 32) { //if its a space and the str isn't empty
+ // sometimes you get a space after the letter
+ if (str !== "") {
+ tmpArray.push(parseFloat(str));
+ str = "";
+ }
+ i++;
+ } else if (valOf === 45) { //if it's a -
+ // allow for 'e' notation in numbers, e.g. 2.10e-9
+ if (pathData[i-1].charCodeAt(0) === 101) {
+ str += pathData[i].toString();
+ i++;
+ } else {
+ // sometimes no space separator after (ex: 104.535-16.322)
+ if (str !== "") {
+ tmpArray.push(parseFloat(str));
+ }
+ str = pathData[i].toString();
+ i++;
+ }
+ } else {
+ str += pathData[i].toString();
+ i++;
+ }
+ if (i === pathData.length) { // don't go over boundary of array
+ flag = true;
+ } else {
+ valOf = pathData[i].charCodeAt(0);
+ }
+ }
+ }
+ if (str !== "") {
+ tmpArray.push(parseFloat(str));
+ str = "";
+ }
+ command = pathData[j];
+ valOf = command.charCodeAt(0);
+ if (valOf === 77) { // M - move to (absolute)
+ if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
+ // need one+ pairs of co-ordinates
+ cx = tmpArray[0];
+ cy = tmpArray[1];
+ this.parsePathMoveto(cx, cy);
+ if (tmpArray.length > 2) {
+ for (j = 2, k = tmpArray.length; j < k; j+=2) {
+ // absolute line to
+ cx = tmpArray[j];
+ cy = tmpArray[j+1];
+ this.parsePathLineto(cx,cy);
+ }
+ }
+ }
+ } else if (valOf === 109) { // m - move to (relative)
+ if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
+ // need one+ pairs of co-ordinates
+ cx += tmpArray[0];
+ cy += tmpArray[1];
+ this.parsePathMoveto(cx,cy);
+ if (tmpArray.length > 2) {
+ for (j = 2, k = tmpArray.length; j < k; j+=2) {
+ // relative line to
+ cx += tmpArray[j];
+ cy += tmpArray[j + 1];
+ this.parsePathLineto(cx,cy);
+ }
+ }
+ }
+ } else if (valOf === 76) { // L - lineto (absolute)
+ if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
+ // need one+ pairs of co-ordinates
+ for (j = 0, k = tmpArray.length; j < k; j+=2) {
+ cx = tmpArray[j];
+ cy = tmpArray[j + 1];
+ this.parsePathLineto(cx,cy);
+ }
+ }
+ } else if (valOf === 108) { // l - lineto (relative)
+ if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
+ // need one+ pairs of co-ordinates
+ for (j = 0, k = tmpArray.length; j < k; j+=2) {
+ cx += tmpArray[j];
+ cy += tmpArray[j+1];
+ this.parsePathLineto(cx,cy);
+ }
+ }
+ } else if (valOf === 72) { // H - horizontal lineto (absolute)
+ for (j = 0, k = tmpArray.length; j < k; j++) {
+ // multiple x co-ordinates can be provided
+ cx = tmpArray[j];
+ this.parsePathLineto(cx, cy);
+ }
+ } else if (valOf === 104) { // h - horizontal lineto (relative)
+ for (j = 0, k = tmpArray.length; j < k; j++) {
+ // multiple x co-ordinates can be provided
+ cx += tmpArray[j];
+ this.parsePathLineto(cx, cy);
+ }
+ } else if (valOf === 86) { // V - vertical lineto (absolute)
+ for (j = 0, k = tmpArray.length; j < k; j++) {
+ // multiple y co-ordinates can be provided
+ cy = tmpArray[j];
+ this.parsePathLineto(cx, cy);
+ }
+ } else if (valOf === 118) { // v - vertical lineto (relative)
+ for (j = 0, k = tmpArray.length; j < k; j++) {
+ // multiple y co-ordinates can be provided
+ cy += tmpArray[j];
+ this.parsePathLineto(cx, cy);
+ }
+ } else if (valOf === 67) { // C - curve to (absolute)
+ if (tmpArray.length >= 6 && tmpArray.length % 6 === 0) {
+ // need one+ multiples of 6 co-ordinates
+ for (j = 0, k = tmpArray.length; j < k; j+=6) {
+ ctrlX1 = tmpArray[j];
+ ctrlY1 = tmpArray[j + 1];
+ ctrlX2 = tmpArray[j + 2];
+ ctrlY2 = tmpArray[j + 3];
+ endX = tmpArray[j + 4];
+ endY = tmpArray[j + 5];
+ this.parsePathCurveto(ctrlX1,
+ ctrlY1,
+ ctrlX2,
+ ctrlY2,
+ endX,
+ endY);
+ cx = endX;
+ cy = endY;
+ }
+ }
+ } else if (valOf === 99) { // c - curve to (relative)
+ if (tmpArray.length >= 6 && tmpArray.length % 6 === 0) {
+ // need one+ multiples of 6 co-ordinates
+ for (j = 0, k = tmpArray.length; j < k; j+=6) {
+ ctrlX1 = cx + tmpArray[j];
+ ctrlY1 = cy + tmpArray[j + 1];
+ ctrlX2 = cx + tmpArray[j + 2];
+ ctrlY2 = cy + tmpArray[j + 3];
+ endX = cx + tmpArray[j + 4];
+ endY = cy + tmpArray[j + 5];
+ this.parsePathCurveto(ctrlX1,
+ ctrlY1,
+ ctrlX2,
+ ctrlY2,
+ endX,
+ endY);
+ cx = endX;
+ cy = endY;
+ }
+ }
+ } else if (valOf === 83) { // S - curve to shorthand (absolute)
+ if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
+ // need one+ multiples of 4 co-ordinates
+ for (j = 0, k = tmpArray.length; j < k; j+=4) {
+ if (lastInstruction.toLowerCase() === "c" ||
+ lastInstruction.toLowerCase() === "s") {
+ ppx = this.vertices[ this.vertices.length-2 ][0];
+ ppy = this.vertices[ this.vertices.length-2 ][1];
+ px = this.vertices[ this.vertices.length-1 ][0];
+ py = this.vertices[ this.vertices.length-1 ][1];
+ ctrlX1 = px + (px - ppx);
+ ctrlY1 = py + (py - ppy);
+ } else {
+ //If there is no previous curve,
+ //the current point will be used as the first control point.
+ ctrlX1 = this.vertices[this.vertices.length-1][0];
+ ctrlY1 = this.vertices[this.vertices.length-1][1];
+ }
+ ctrlX2 = tmpArray[j];
+ ctrlY2 = tmpArray[j + 1];
+ endX = tmpArray[j + 2];
+ endY = tmpArray[j + 3];
+ this.parsePathCurveto(ctrlX1,
+ ctrlY1,
+ ctrlX2,
+ ctrlY2,
+ endX,
+ endY);
+ cx = endX;
+ cy = endY;
+ }
+ }
+ } else if (valOf === 115) { // s - curve to shorthand (relative)
+ if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
+ // need one+ multiples of 4 co-ordinates
+ for (j = 0, k = tmpArray.length; j < k; j+=4) {
+ if (lastInstruction.toLowerCase() === "c" ||
+ lastInstruction.toLowerCase() === "s") {
+ ppx = this.vertices[this.vertices.length-2][0];
+ ppy = this.vertices[this.vertices.length-2][1];
+ px = this.vertices[this.vertices.length-1][0];
+ py = this.vertices[this.vertices.length-1][1];
+ ctrlX1 = px + (px - ppx);
+ ctrlY1 = py + (py - ppy);
+ } else {
+ //If there is no previous curve,
+ //the current point will be used as the first control point.
+ ctrlX1 = this.vertices[this.vertices.length-1][0];
+ ctrlY1 = this.vertices[this.vertices.length-1][1];
+ }
+ ctrlX2 = cx + tmpArray[j];
+ ctrlY2 = cy + tmpArray[j + 1];
+ endX = cx + tmpArray[j + 2];
+ endY = cy + tmpArray[j + 3];
+ this.parsePathCurveto(ctrlX1,
+ ctrlY1,
+ ctrlX2,
+ ctrlY2,
+ endX,
+ endY);
+ cx = endX;
+ cy = endY;
+ }
+ }
+ } else if (valOf === 81) { // Q - quadratic curve to (absolute)
+ if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
+ // need one+ multiples of 4 co-ordinates
+ for (j = 0, k = tmpArray.length; j < k; j+=4) {
+ ctrlX = tmpArray[j];
+ ctrlY = tmpArray[j + 1];
+ endX = tmpArray[j + 2];
+ endY = tmpArray[j + 3];
+ this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
+ cx = endX;
+ cy = endY;
+ }
+ }
+ } else if (valOf === 113) { // q - quadratic curve to (relative)
+ if (tmpArray.length >= 4 && tmpArray.length % 4 === 0) {
+ // need one+ multiples of 4 co-ordinates
+ for (j = 0, k = tmpArray.length; j < k; j+=4) {
+ ctrlX = cx + tmpArray[j];
+ ctrlY = cy + tmpArray[j + 1];
+ endX = cx + tmpArray[j + 2];
+ endY = cy + tmpArray[j + 3];
+ this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
+ cx = endX;
+ cy = endY;
+ }
+ }
+ } else if (valOf === 84) {
+ // T - quadratic curve to shorthand (absolute)
+ if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
+ // need one+ pairs of co-ordinates
+ for (j = 0, k = tmpArray.length; j < k; j+=2) {
+ if (lastInstruction.toLowerCase() === "q" ||
+ lastInstruction.toLowerCase() === "t") {
+ ppx = this.vertices[this.vertices.length-2][0];
+ ppy = this.vertices[this.vertices.length-2][1];
+ px = this.vertices[this.vertices.length-1][0];
+ py = this.vertices[this.vertices.length-1][1];
+ ctrlX = px + (px - ppx);
+ ctrlY = py + (py - ppy);
+ } else {
+ // If there is no previous command or if the previous command
+ // was not a Q, q, T or t, assume the control point is
+ // coincident with the current point.
+ ctrlX = cx;
+ ctrlY = cy;
+ }
+ endX = tmpArray[j];
+ endY = tmpArray[j + 1];
+ this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
+ cx = endX;
+ cy = endY;
+ }
+ }
+ } else if (valOf === 116) {
+ // t - quadratic curve to shorthand (relative)
+ if (tmpArray.length >= 2 && tmpArray.length % 2 === 0) {
+ // need one+ pairs of co-ordinates
+ for (j = 0, k = tmpArray.length; j < k; j+=2) {
+ if (lastInstruction.toLowerCase() === "q" ||
+ lastInstruction.toLowerCase() === "t") {
+ ppx = this.vertices[this.vertices.length-2][0];
+ ppy = this.vertices[this.vertices.length-2][1];
+ px = this.vertices[this.vertices.length-1][0];
+ py = this.vertices[this.vertices.length-1][1];
+ ctrlX = px + (px - ppx);
+ ctrlY = py + (py - ppy);
+ } else {
+ // If there is no previous command or if the previous command
+ // was not a Q, q, T or t, assume the control point is
+ // coincident with the current point.
+ ctrlX = cx;
+ ctrlY = cy;
+ }
+ endX = cx + tmpArray[j];
+ endY = cy + tmpArray[j + 1];
+ this.parsePathQuadto(cx, cy, ctrlX, ctrlY, endX, endY);
+ cx = endX;
+ cy = endY;
+ }
+ }
+ } else if (valOf === 90 || valOf === 122) { // Z or z (these do the same thing)
+ this.close = true;
+ }
+ lastInstruction = command.toString();
+ } else { i++;}
+ }
+ };
+ /**
+ * @member PShapeSVG
+ * PShapeSVG.parsePath() helper function
+ *
+ * @see PShapeSVG#parsePath
+ */
+ PShapeSVG.prototype.parsePathQuadto = function(x1, y1, cx, cy, x2, y2) {
+ if (this.vertices.length > 0) {
+ this.parsePathCode(PConstants.BEZIER_VERTEX);
+ // x1/y1 already covered by last moveto, lineto, or curveto
+ this.parsePathVertex(x1 + ((cx-x1)*2/3), y1 + ((cy-y1)*2/3));
+ this.parsePathVertex(x2 + ((cx-x2)*2/3), y2 + ((cy-y2)*2/3));
+ this.parsePathVertex(x2, y2);
+ } else {
+ throw ("Path must start with M/m");
+ }
+ };
+ /**
+ * @member PShapeSVG
+ * PShapeSVG.parsePath() helper function
+ *
+ * @see PShapeSVG#parsePath
+ */
+ PShapeSVG.prototype.parsePathCurveto = function(x1, y1, x2, y2, x3, y3) {
+ if (this.vertices.length > 0) {
+ this.parsePathCode(PConstants.BEZIER_VERTEX );
+ this.parsePathVertex(x1, y1);
+ this.parsePathVertex(x2, y2);
+ this.parsePathVertex(x3, y3);
+ } else {
+ throw ("Path must start with M/m");
+ }
+ };
+ /**
+ * @member PShapeSVG
+ * PShapeSVG.parsePath() helper function
+ *
+ * @see PShapeSVG#parsePath
+ */
+ PShapeSVG.prototype.parsePathLineto = function(px, py) {
+ if (this.vertices.length > 0) {
+ this.parsePathCode(PConstants.VERTEX);
+ this.parsePathVertex(px, py);
+ // add property to distinguish between curContext.moveTo
+ // or curContext.lineTo
+ this.vertices[this.vertices.length-1].moveTo = false;
+ } else {
+ throw ("Path must start with M/m");
+ }
+ };
+
+ PShapeSVG.prototype.parsePathMoveto = function(px, py) {
+ if (this.vertices.length > 0) {
+ this.parsePathCode(PConstants.BREAK);
+ }
+ this.parsePathCode(PConstants.VERTEX);
+ this.parsePathVertex(px, py);
+ // add property to distinguish between curContext.moveTo
+ // or curContext.lineTo
+ this.vertices[this.vertices.length-1].moveTo = true;
+ };
+ /**
+ * @member PShapeSVG
+ * PShapeSVG.parsePath() helper function
+ *
+ * @see PShapeSVG#parsePath
+ */
+ PShapeSVG.prototype.parsePathVertex = function(x, y) {
+ var verts = [];
+ verts[0] = x;
+ verts[1] = y;
+ this.vertices.push(verts);
+ };
+ /**
+ * @member PShapeSVG
+ * PShapeSVG.parsePath() helper function
+ *
+ * @see PShapeSVG#parsePath
+ */
+ PShapeSVG.prototype.parsePathCode = function(what) {
+ this.vertexCodes.push(what);
+ };
+ /**
+ * @member PShapeSVG
+ * The parsePoly() function parses a polyline or polygon from an SVG file.
+ *
+ * @param {boolean}val true if shape is closed (polygon), false if not (polyline)
+ */
+ PShapeSVG.prototype.parsePoly = function(val) {
+ this.family = PConstants.PATH;
+ this.close = val;
+ var pointsAttr = CommonFunctions.trim(this.element.getStringAttribute("points").replace(/[,\s]+/g,' '));
+ if (pointsAttr !== null) {
+ //split into array
+ var pointsBuffer = pointsAttr.split(" ");
+ if (pointsBuffer.length % 2 === 0) {
+ for (var i = 0, j = pointsBuffer.length; i < j; i++) {
+ var verts = [];
+ verts[0] = pointsBuffer[i];
+ verts[1] = pointsBuffer[++i];
+ this.vertices.push(verts);
+ }
+ } else {
+ throw("Error parsing polygon points: odd number of coordinates provided");
+ }
+ }
+ };
+ /**
+ * @member PShapeSVG
+ * The parseRect() function parses a rect from an SVG file.
+ */
+ PShapeSVG.prototype.parseRect = function() {
+ this.kind = PConstants.RECT;
+ this.family = PConstants.PRIMITIVE;
+ this.params = [];
+ this.params[0] = this.element.getFloatAttribute("x");
+ this.params[1] = this.element.getFloatAttribute("y");
+ this.params[2] = this.element.getFloatAttribute("width");
+ this.params[3] = this.element.getFloatAttribute("height");
+ if (this.params[2] < 0 || this.params[3] < 0) {
+ throw("svg error: negative width or height found while parsing <rect>");
+ }
+ };
+ /**
+ * @member PShapeSVG
+ * The parseEllipse() function handles parsing ellipse and circle tags.
+ *
+ * @param {boolean}val true if this is a circle and not an ellipse
+ */
+ PShapeSVG.prototype.parseEllipse = function(val) {
+ this.kind = PConstants.ELLIPSE;
+ this.family = PConstants.PRIMITIVE;
+ this.params = [];
+
+ this.params[0] = this.element.getFloatAttribute("cx") | 0 ;
+ this.params[1] = this.element.getFloatAttribute("cy") | 0;
+
+ var rx, ry;
+ if (val) {
+ rx = ry = this.element.getFloatAttribute("r");
+ if (rx < 0) {
+ throw("svg error: negative radius found while parsing <circle>");
+ }
+ } else {
+ rx = this.element.getFloatAttribute("rx");
+ ry = this.element.getFloatAttribute("ry");
+ if (rx < 0 || ry < 0) {
+ throw("svg error: negative x-axis radius or y-axis radius found while parsing <ellipse>");
+ }
+ }
+ this.params[0] -= rx;
+ this.params[1] -= ry;
+
+ this.params[2] = rx*2;
+ this.params[3] = ry*2;
+ };
+ /**
+ * @member PShapeSVG
+ * The parseLine() function handles parsing line tags.
+ *
+ * @param {boolean}val true if this is a circle and not an ellipse
+ */
+ PShapeSVG.prototype.parseLine = function() {
+ this.kind = PConstants.LINE;
+ this.family = PConstants.PRIMITIVE;
+ this.params = [];
+ this.params[0] = this.element.getFloatAttribute("x1");
+ this.params[1] = this.element.getFloatAttribute("y1");
+ this.params[2] = this.element.getFloatAttribute("x2");
+ this.params[3] = this.element.getFloatAttribute("y2");
+ };
+ /**
+ * @member PShapeSVG
+ * The parseColors() function handles parsing the opacity, strijem stroke-width, stroke-linejoin,stroke-linecap, fill, and style attributes
+ *
+ * @param {XMLElement}element the element of which attributes to parse
+ */
+ PShapeSVG.prototype.parseColors = function(element) {
+ if (element.hasAttribute("opacity")) {
+ this.setOpacity(element.getAttribute("opacity"));
+ }
+ if (element.hasAttribute("stroke")) {
+ this.setStroke(element.getAttribute("stroke"));
+ }
+ if (element.hasAttribute("stroke-width")) {
+ // if NaN (i.e. if it's 'inherit') then default
+ // back to the inherit setting
+ this.setStrokeWeight(element.getAttribute("stroke-width"));
+ }
+ if (element.hasAttribute("stroke-linejoin") ) {
+ this.setStrokeJoin(element.getAttribute("stroke-linejoin"));
+ }
+ if (element.hasAttribute("stroke-linecap")) {
+ this.setStrokeCap(element.getStringAttribute("stroke-linecap"));
+ }
+ // fill defaults to black (though stroke defaults to "none")
+ // http://www.w3.org/TR/SVG/painting.html#FillProperties
+ if (element.hasAttribute("fill")) {
+ this.setFill(element.getStringAttribute("fill"));
+ }
+ if (element.hasAttribute("style")) {
+ var styleText = element.getStringAttribute("style");
+ var styleTokens = styleText.toString().split( ";" );
+
+ for (var i = 0, j = styleTokens.length; i < j; i++) {
+ var tokens = CommonFunctions.trim(styleTokens[i].split( ":" ));
+ if (tokens[0] === "fill") {
+ this.setFill(tokens[1]);
+ } else if (tokens[0] === "fill-opacity") {
+ this.setFillOpacity(tokens[1]);
+ } else if (tokens[0] === "stroke") {
+ this.setStroke(tokens[1]);
+ } else if (tokens[0] === "stroke-width") {
+ this.setStrokeWeight(tokens[1]);
+ } else if (tokens[0] === "stroke-linecap") {
+ this.setStrokeCap(tokens[1]);
+ } else if (tokens[0] === "stroke-linejoin") {
+ this.setStrokeJoin(tokens[1]);
+ } else if (tokens[0] === "stroke-opacity") {
+ this.setStrokeOpacity(tokens[1]);
+ } else if (tokens[0] === "opacity") {
+ this.setOpacity(tokens[1]);
+ } // Other attributes are not yet implemented
+ }
+ }
+ };
+ /**
+ * @member PShapeSVG
+ * PShapeSVG.parseColors() helper function
+ *
+ * @param {String} opacityText the value of fillOpacity
+ *
+ * @see PShapeSVG#parseColors
+ */
+ PShapeSVG.prototype.setFillOpacity = function(opacityText) {
+ this.fillOpacity = parseFloat(opacityText);
+ this.fillColor = this.fillOpacity * 255 << 24 |
+ this.fillColor & 0xFFFFFF;
+ };
+ /**
+ * @member PShapeSVG
+ * PShapeSVG.parseColors() helper function
+ *
+ * @param {String} fillText the value of fill
+ *
+ * @see PShapeSVG#parseColors
+ */
+ PShapeSVG.prototype.setFill = function (fillText) {
+ var opacityMask = this.fillColor & 0xFF000000;
+ if (fillText === "none") {
+ this.fill = false;
+ } else if (fillText.indexOf("#") === 0) {
+ this.fill = true;
+ if (fillText.length === 4) {
+ // convert #00F to #0000FF
+ fillText = fillText.replace(/#(.)(.)(.)/,"#$1$1$2$2$3$3");
+ }
+ this.fillColor = opacityMask |
+ (parseInt(fillText.substring(1), 16 )) &
+ 0xFFFFFF;
+ } else if (fillText.indexOf("rgb") === 0) {
+ this.fill = true;
+ this.fillColor = opacityMask | this.parseRGB(fillText);
+ } else if (fillText.indexOf("url(#") === 0) {
+ this.fillName = fillText.substring(5, fillText.length - 1 );
+ } else if (colors[fillText]) {
+ this.fill = true;
+ this.fillColor = opacityMask |
+ (parseInt(colors[fillText].substring(1), 16)) &
+ 0xFFFFFF;
+ }
+ };
+ /**
+ * @member PShapeSVG
+ * PShapeSVG.parseColors() helper function
+ *
+ * @param {String} opacity the value of opacity
+ *
+ * @see PShapeSVG#parseColors
+ */
+ PShapeSVG.prototype.setOpacity = function(opacity) {
+ this.strokeColor = parseFloat(opacity) * 255 << 24 |
+ this.strokeColor & 0xFFFFFF;
+ this.fillColor = parseFloat(opacity) * 255 << 24 |
+ this.fillColor & 0xFFFFFF;
+ };
+ /**
+ * @member PShapeSVG
+ * PShapeSVG.parseColors() helper function
+ *
+ * @param {String} strokeText the value to set stroke to
+ *
+ * @see PShapeSVG#parseColors
+ */
+ PShapeSVG.prototype.setStroke = function(strokeText) {
+ var opacityMask = this.strokeColor & 0xFF000000;
+ if (strokeText === "none") {
+ this.stroke = false;
+ } else if (strokeText.charAt( 0 ) === "#") {
+ this.stroke = true;
+ if (strokeText.length === 4) {
+ // convert #00F to #0000FF
+ strokeText = strokeText.replace(/#(.)(.)(.)/,"#$1$1$2$2$3$3");
+ }
+ this.strokeColor = opacityMask |
+ (parseInt( strokeText.substring( 1 ), 16 )) &
+ 0xFFFFFF;
+ } else if (strokeText.indexOf( "rgb" ) === 0 ) {
+ this.stroke = true;
+ this.strokeColor = opacityMask | this.parseRGB(strokeText);
+ } else if (strokeText.indexOf( "url(#" ) === 0) {
+ this.strokeName = strokeText.substring(5, strokeText.length - 1);
+ } else if (colors[strokeText]) {
+ this.stroke = true;
+ this.strokeColor = opacityMask |
+ (parseInt(colors[strokeText].substring(1), 16)) &
+ 0xFFFFFF;
+ }
+ };
+ /**
+ * @member PShapeSVG
+ * PShapeSVG.parseColors() helper function
+ *
+ * @param {String} weight the value to set strokeWeight to
+ *
+ * @see PShapeSVG#parseColors
+ */
+ PShapeSVG.prototype.setStrokeWeight = function(weight) {
+ this.strokeWeight = this.parseUnitSize(weight);
+ };
+ /**
+ * @member PShapeSVG
+ * PShapeSVG.parseColors() helper function
+ *
+ * @param {String} linejoin the value to set strokeJoin to
+ *
+ * @see PShapeSVG#parseColors
+ */
+ PShapeSVG.prototype.setStrokeJoin = function(linejoin) {
+ if (linejoin === "miter") {
+ this.strokeJoin = PConstants.MITER;
+
+ } else if (linejoin === "round") {
+ this.strokeJoin = PConstants.ROUND;
+
+ } else if (linejoin === "bevel") {
+ this.strokeJoin = PConstants.BEVEL;
+ }
+ };
+ /**
+ * @member PShapeSVG
+ * PShapeSVG.parseColors() helper function
+ *
+ * @param {String} linecap the value to set strokeCap to
+ *
+ * @see PShapeSVG#parseColors
+ */
+ PShapeSVG.prototype.setStrokeCap = function (linecap) {
+ if (linecap === "butt") {
+ this.strokeCap = PConstants.SQUARE;
+
+ } else if (linecap === "round") {
+ this.strokeCap = PConstants.ROUND;
+
+ } else if (linecap === "square") {
+ this.strokeCap = PConstants.PROJECT;
+ }
+ };
+ /**
+ * @member PShapeSVG
+ * PShapeSVG.parseColors() helper function
+ *
+ * @param {String} opacityText the value to set stroke opacity to
+ *
+ * @see PShapeSVG#parseColors
+ */
+ PShapeSVG.prototype.setStrokeOpacity = function (opacityText) {
+ this.strokeOpacity = parseFloat(opacityText);
+ this.strokeColor = this.strokeOpacity * 255 << 24 |
+ this.strokeColor &
+ 0xFFFFFF;
+ };
+ /**
+ * @member PShapeSVG
+ * The parseRGB() function parses an rbg() color string and returns a color int
+ *
+ * @param {String} color the color to parse in rbg() format
+ *
+ * @return {int} the equivalent color int
+ */
+ PShapeSVG.prototype.parseRGB = function(color) {
+ var sub = color.substring(color.indexOf('(') + 1, color.indexOf(')'));
+ var values = sub.split(", ");
+ return (values[0] << 16) | (values[1] << 8) | (values[2]);
+ };
+ /**
+ * @member PShapeSVG
+ * The parseUnitSize() function parse a size that may have a suffix for its units.
+ * Ignoring cases where this could also be a percentage.
+ * The <A HREF="http://www.w3.org/TR/SVG/coords.html#Units">units</A> spec:
+ * <UL>
+ * <LI>"1pt" equals "1.25px" (and therefore 1.25 user units)
+ * <LI>"1pc" equals "15px" (and therefore 15 user units)
+ * <LI>"1mm" would be "3.543307px" (3.543307 user units)
+ * <LI>"1cm" equals "35.43307px" (and therefore 35.43307 user units)
+ * <LI>"1in" equals "90px" (and therefore 90 user units)
+ * </UL>
+ */
+ PShapeSVG.prototype.parseUnitSize = function (text) {
+ var len = text.length - 2;
+ if (len < 0) { return text; }
+ if (text.indexOf("pt") === len) {
+ return parseFloat(text.substring(0, len)) * 1.25;
+ }
+ if (text.indexOf("pc") === len) {
+ return parseFloat( text.substring( 0, len)) * 15;
+ }
+ if (text.indexOf("mm") === len) {
+ return parseFloat( text.substring(0, len)) * 3.543307;
+ }
+ if (text.indexOf("cm") === len) {
+ return parseFloat(text.substring(0, len)) * 35.43307;
+ }
+ if (text.indexOf("in") === len) {
+ return parseFloat(text.substring(0, len)) * 90;
+ }
+ if (text.indexOf("px") === len) {
+ return parseFloat(text.substring(0, len));
+ }
+ return parseFloat(text);
+ };
+
+ return PShapeSVG;
+};
+
+},{}],18:[function(require,module,exports){
+module.exports = function(options, undef) {
+ var PConstants = options.PConstants;
+
+ function PVector(x, y, z) {
+ this.x = x || 0;
+ this.y = y || 0;
+ this.z = z || 0;
+ }
+
+ PVector.fromAngle = function(angle, v) {
+ if (v === undef || v === null) {
+ v = new PVector();
+ }
+ v.x = Math.cos(angle);
+ v.y = Math.sin(angle);
+ return v;
+ };
+
+ PVector.random2D = function(v) {
+ return PVector.fromAngle(Math.random() * PConstants.TWO_PI, v);
+ };
+
+ PVector.random3D = function(v) {
+ var angle = Math.random() * PConstants.TWO_PI;
+ var vz = Math.random() * 2 - 1;
+ var mult = Math.sqrt(1 - vz * vz);
+ var vx = mult * Math.cos(angle);
+ var vy = mult * Math.sin(angle);
+ if (v === undef || v === null) {
+ v = new PVector(vx, vy, vz);
+ } else {
+ v.set(vx, vy, vz);
+ }
+ return v;
+ };
+
+ PVector.dist = function(v1, v2) {
+ return v1.dist(v2);
+ };
+
+ PVector.dot = function(v1, v2) {
+ return v1.dot(v2);
+ };
+
+ PVector.cross = function(v1, v2) {
+ return v1.cross(v2);
+ };
+
+ PVector.sub = function(v1, v2) {
+ return new PVector(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
+ };
+
+ PVector.angleBetween = function(v1, v2) {
+ return Math.acos(v1.dot(v2) / Math.sqrt(v1.magSq() * v2.magSq()));
+ };
+
+ PVector.lerp = function(v1, v2, amt) {
+ // non-static lerp mutates object, but this version returns a new vector
+ var retval = new PVector(v1.x, v1.y, v1.z);
+ retval.lerp(v2, amt);
+ return retval;
+ };
+
+ // Common vector operations for PVector
+ PVector.prototype = {
+ set: function(v, y, z) {
+ if (arguments.length === 1) {
+ this.set(v.x || v[0] || 0,
+ v.y || v[1] || 0,
+ v.z || v[2] || 0);
+ } else {
+ this.x = v;
+ this.y = y;
+ this.z = z;
+ }
+ },
+ get: function() {
+ return new PVector(this.x, this.y, this.z);
+ },
+ mag: function() {
+ var x = this.x,
+ y = this.y,
+ z = this.z;
+ return Math.sqrt(x * x + y * y + z * z);
+ },
+ magSq: function() {
+ var x = this.x,
+ y = this.y,
+ z = this.z;
+ return (x * x + y * y + z * z);
+ },
+ setMag: function(v_or_len, len) {
+ if (len === undef) {
+ len = v_or_len;
+ this.normalize();
+ this.mult(len);
+ } else {
+ var v = v_or_len;
+ v.normalize();
+ v.mult(len);
+ return v;
+ }
+ },
+ add: function(v, y, z) {
+ if (arguments.length === 1) {
+ this.x += v.x;
+ this.y += v.y;
+ this.z += v.z;
+ } else if (arguments.length === 2) {
+ // 2D Vector
+ this.x += v;
+ this.y += y;
+ } else {
+ this.x += v;
+ this.y += y;
+ this.z += z;
+ }
+ },
+ sub: function(v, y, z) {
+ if (arguments.length === 1) {
+ this.x -= v.x;
+ this.y -= v.y;
+ this.z -= v.z;
+ } else if (arguments.length === 2) {
+ // 2D Vector
+ this.x -= v;
+ this.y -= y;
+ } else {
+ this.x -= v;
+ this.y -= y;
+ this.z -= z;
+ }
+ },
+ mult: function(v) {
+ if (typeof v === 'number') {
+ this.x *= v;
+ this.y *= v;
+ this.z *= v;
+ } else {
+ this.x *= v.x;
+ this.y *= v.y;
+ this.z *= v.z;
+ }
+ },
+ div: function(v) {
+ if (typeof v === 'number') {
+ this.x /= v;
+ this.y /= v;
+ this.z /= v;
+ } else {
+ this.x /= v.x;
+ this.y /= v.y;
+ this.z /= v.z;
+ }
+ },
+ rotate: function(angle) {
+ var prev_x = this.x;
+ var c = Math.cos(angle);
+ var s = Math.sin(angle);
+ this.x = c * this.x - s * this.y;
+ this.y = s * prev_x + c * this.y;
+ },
+ dist: function(v) {
+ var dx = this.x - v.x,
+ dy = this.y - v.y,
+ dz = this.z - v.z;
+ return Math.sqrt(dx * dx + dy * dy + dz * dz);
+ },
+ dot: function(v, y, z) {
+ if (arguments.length === 1) {
+ return (this.x * v.x + this.y * v.y + this.z * v.z);
+ }
+ return (this.x * v + this.y * y + this.z * z);
+ },
+ cross: function(v) {
+ var x = this.x,
+ y = this.y,
+ z = this.z;
+ return new PVector(y * v.z - v.y * z,
+ z * v.x - v.z * x,
+ x * v.y - v.x * y);
+ },
+ lerp: function(v_or_x, amt_or_y, z, amt) {
+ var lerp_val = function(start, stop, amt) {
+ return start + (stop - start) * amt;
+ };
+ var x, y;
+ if (arguments.length === 2) {
+ // given vector and amt
+ amt = amt_or_y;
+ x = v_or_x.x;
+ y = v_or_x.y;
+ z = v_or_x.z;
+ } else {
+ // given x, y, z and amt
+ x = v_or_x;
+ y = amt_or_y;
+ }
+ this.x = lerp_val(this.x, x, amt);
+ this.y = lerp_val(this.y, y, amt);
+ this.z = lerp_val(this.z, z, amt);
+ },
+ normalize: function() {
+ var m = this.mag();
+ if (m > 0) {
+ this.div(m);
+ }
+ },
+ limit: function(high) {
+ if (this.mag() > high) {
+ this.normalize();
+ this.mult(high);
+ }
+ },
+ heading: function() {
+ return (-Math.atan2(-this.y, this.x));
+ },
+ heading2D: function() {
+ return this.heading();
+ },
+ toString: function() {
+ return "[" + this.x + ", " + this.y + ", " + this.z + "]";
+ },
+ array: function() {
+ return [this.x, this.y, this.z];
+ }
+ };
+
+ function createPVectorMethod(method) {
+ return function(v1, v2) {
+ var v = v1.get();
+ v[method](v2);
+ return v;
+ };
+ }
+
+ for (var method in PVector.prototype) {
+ if (PVector.prototype.hasOwnProperty(method) && !PVector.hasOwnProperty(method)) {
+ PVector[method] = createPVectorMethod(method);
+ }
+ }
+
+ return PVector;
+};
+
+},{}],19:[function(require,module,exports){
+/**
+ * XMLAttribute is an attribute of a XML element.
+ *
+ * @param {String} fname the full name of the attribute
+ * @param {String} n the short name of the attribute
+ * @param {String} namespace the namespace URI of the attribute
+ * @param {String} v the value of the attribute
+ * @param {String }t the type of the attribute
+ *
+ * @see XMLElement
+ */
+module.exports = function() {
+
+ var XMLAttribute = function (fname, n, nameSpace, v, t){
+ this.fullName = fname || "";
+ this.name = n || "";
+ this.namespace = nameSpace || "";
+ this.value = v;
+ this.type = t;
+ };
+
+ XMLAttribute.prototype = {
+ /**
+ * @member XMLAttribute
+ * The getName() function returns the short name of the attribute
+ *
+ * @return {String} the short name of the attribute
+ */
+ getName: function() {
+ return this.name;
+ },
+ /**
+ * @member XMLAttribute
+ * The getFullName() function returns the full name of the attribute
+ *
+ * @return {String} the full name of the attribute
+ */
+ getFullName: function() {
+ return this.fullName;
+ },
+ /**
+ * @member XMLAttribute
+ * The getNamespace() function returns the namespace of the attribute
+ *
+ * @return {String} the namespace of the attribute
+ */
+ getNamespace: function() {
+ return this.namespace;
+ },
+ /**
+ * @member XMLAttribute
+ * The getValue() function returns the value of the attribute
+ *
+ * @return {String} the value of the attribute
+ */
+ getValue: function() {
+ return this.value;
+ },
+ /**
+ * @member XMLAttribute
+ * The getValue() function returns the type of the attribute
+ *
+ * @return {String} the type of the attribute
+ */
+ getType: function() {
+ return this.type;
+ },
+ /**
+ * @member XMLAttribute
+ * The setValue() function sets the value of the attribute
+ *
+ * @param {String} newval the new value
+ */
+ setValue: function(newval) {
+ this.value = newval;
+ }
+ };
+
+ return XMLAttribute;
+};
+
+},{}],20:[function(require,module,exports){
+/**
+ * XMLElement is a representation of an XML object. The object is able to parse XML code
+ *
+ * @param {PApplet} parent typically use "this"
+ * @param {String} filename name of the XML/SVG file to load
+ * @param {String} xml the xml/svg string
+ * @param {String} fullname the full name of the element
+ * @param {String} namespace the namespace of the URI
+ * @param {String} systemID the system ID of the XML data where the element starts
+ * @param {Integer }lineNr the line in the XML data where the element starts
+ */
+module.exports = function(options, undef) {
+
+ var Browser = options.Browser,
+ ajax = Browser.ajax,
+ window = Browser.window,
+ XMLHttpRequest = window.XMLHttpRequest,
+ DOMParser = window.DOMParser,
+ XMLAttribute = options. XMLAttribute;
+
+ var XMLElement = function(selector, uri, sysid, line) {
+ this.attributes = [];
+ this.children = [];
+ this.fullName = null;
+ this.name = null;
+ this.namespace = "";
+ this.content = null;
+ this.parent = null;
+ this.lineNr = "";
+ this.systemID = "";
+ this.type = "ELEMENT";
+
+ if (selector) {
+ if (typeof selector === "string") {
+ if (uri === undef && selector.indexOf("<") > -1) {
+ // load XML from text string
+ this.parse(selector);
+ } else {
+ // XMLElement(fullname, namespace, sysid, line) format
+ this.fullName = selector;
+ this.namespace = uri;
+ this.systemId = sysid;
+ this.lineNr = line;
+ }
+ } else {
+ // XMLElement(this, file uri) format
+ this.parse(uri, true);
+ }
+ }
+ };
+ /**
+ * XMLElement methods
+ * missing: enumerateAttributeNames(), enumerateChildren(),
+ * NOTE: parse does not work when a url is passed in
+ */
+ XMLElement.prototype = {
+ /**
+ * @member XMLElement
+ * The parse() function retrieves the file via ajax() and uses DOMParser()
+ * parseFromString method to make an XML document
+ * @addon
+ *
+ * @param {String} filename name of the XML/SVG file to load
+ *
+ * @throws ExceptionType Error loading document
+ *
+ * @see XMLElement#parseChildrenRecursive
+ */
+ parse: function(textstring, stringIsURI) {
+ var xmlDoc;
+ try {
+ if (stringIsURI) {
+ textstring = ajax(textstring);
+ }
+ xmlDoc = new DOMParser().parseFromString(textstring, "text/xml");
+ var elements = xmlDoc.documentElement;
+ if (elements) {
+ this.parseChildrenRecursive(null, elements);
+ } else {
+ throw ("Error loading document");
+ }
+ return this;
+ } catch(e) {
+ throw(e);
+ }
+ },
+ /**
+ * @member XMLElement
+ * Internal helper function for parse().
+ * Loops through the
+ * @addon
+ *
+ * @param {XMLElement} parent the parent node
+ * @param {XML document childNodes} elementpath the remaining nodes that need parsing
+ *
+ * @return {XMLElement} the new element and its children elements
+ */
+ parseChildrenRecursive: function (parent, elementpath){
+ var xmlelement,
+ xmlattribute,
+ tmpattrib,
+ l, m,
+ child;
+ if (!parent) { // this element is the root element
+ this.fullName = elementpath.localName;
+ this.name = elementpath.nodeName;
+ xmlelement = this;
+ } else { // this element has a parent
+ xmlelement = new XMLElement(elementpath.nodeName);
+ xmlelement.parent = parent;
+ }
+
+ // if this is a text node, return a PCData element (parsed character data)
+ if (elementpath.nodeType === 3 && elementpath.textContent !== "") {
+ return this.createPCDataElement(elementpath.textContent);
+ }
+
+ // if this is a CDATA node, return a CData element (unparsed character data)
+ if (elementpath.nodeType === 4) {
+ return this.createCDataElement(elementpath.textContent);
+ }
+
+ // bind all attributes, if there are any
+ if (elementpath.attributes) {
+ for (l = 0, m = elementpath.attributes.length; l < m; l++) {
+ tmpattrib = elementpath.attributes[l];
+ xmlattribute = new XMLAttribute(tmpattrib.getname,
+ tmpattrib.nodeName,
+ tmpattrib.namespaceURI,
+ tmpattrib.nodeValue,
+ tmpattrib.nodeType);
+ xmlelement.attributes.push(xmlattribute);
+ }
+ }
+
+ // bind all children, if there are any
+ if (elementpath.childNodes) {
+ for (l = 0, m = elementpath.childNodes.length; l < m; l++) {
+ var node = elementpath.childNodes[l];
+ child = xmlelement.parseChildrenRecursive(xmlelement, node);
+ if (child !== null) {
+ xmlelement.children.push(child);
+ }
+ }
+ }
+
+ return xmlelement;
+ },
+ /**
+ * @member XMLElement
+ * The createElement() function Creates an empty element
+ *
+ * @param {String} fullName the full name of the element
+ * @param {String} namespace the namespace URI
+ * @param {String} systemID the system ID of the XML data where the element starts
+ * @param {int} lineNr the line in the XML data where the element starts
+ */
+ createElement: function (fullname, namespaceuri, sysid, line) {
+ if (sysid === undef) {
+ return new XMLElement(fullname, namespaceuri);
+ }
+ return new XMLElement(fullname, namespaceuri, sysid, line);
+ },
+ /**
+ * @member XMLElement
+ * The createPCDataElement() function creates an element to be used for #PCDATA content.
+ * Because Processing discards whitespace TEXT nodes, this method will not build an element
+ * if the passed content is empty after trimming for whitespace.
+ *
+ * @return {XMLElement} new "pcdata" XMLElement, or null if content consists only of whitespace
+ */
+ createPCDataElement: function (content, isCDATA) {
+ if (content.replace(/^\s+$/g,"") === "") {
+ return null;
+ }
+ var pcdata = new XMLElement();
+ pcdata.type = "TEXT";
+ pcdata.content = content;
+ return pcdata;
+ },
+ /**
+ * @member XMLElement
+ * The createCDataElement() function creates an element to be used for CDATA content.
+ *
+ * @return {XMLElement} new "cdata" XMLElement, or null if content consists only of whitespace
+ */
+ createCDataElement: function (content) {
+ var cdata = this.createPCDataElement(content);
+ if (cdata === null) {
+ return null;
+ }
+
+ cdata.type = "CDATA";
+ var htmlentities = {"<": "&lt;", ">": "&gt;", "'": "&apos;", '"': "&quot;"},
+ entity;
+ for (entity in htmlentities) {
+ if (!Object.hasOwnProperty(htmlentities,entity)) {
+ content = content.replace(new RegExp(entity, "g"), htmlentities[entity]);
+ }
+ }
+ cdata.cdata = content;
+ return cdata;
+ },
+ /**
+ * @member XMLElement
+ * The hasAttribute() function returns whether an attribute exists
+ *
+ * @param {String} name name of the attribute
+ * @param {String} namespace the namespace URI of the attribute
+ *
+ * @return {boolean} true if the attribute exists
+ */
+ hasAttribute: function () {
+ if (arguments.length === 1) {
+ return this.getAttribute(arguments[0]) !== null;
+ }
+ if (arguments.length === 2) {
+ return this.getAttribute(arguments[0],arguments[1]) !== null;
+ }
+ },
+ /**
+ * @member XMLElement
+ * The equals() function checks to see if the XMLElement being passed in equals another XMLElement
+ *
+ * @param {XMLElement} rawElement the element to compare to
+ *
+ * @return {boolean} true if the element equals another element
+ */
+ equals: function(other) {
+ if (!(other instanceof XMLElement)) {
+ return false;
+ }
+ var i, j;
+ if (this.fullName !== other.fullName) { return false; }
+ if (this.attributes.length !== other.getAttributeCount()) { return false; }
+ // attributes may be ordered differently
+ if (this.attributes.length !== other.attributes.length) { return false; }
+ var attr_name, attr_ns, attr_value, attr_type, attr_other;
+ for (i = 0, j = this.attributes.length; i < j; i++) {
+ attr_name = this.attributes[i].getName();
+ attr_ns = this.attributes[i].getNamespace();
+ attr_other = other.findAttribute(attr_name, attr_ns);
+ if (attr_other === null) { return false; }
+ if (this.attributes[i].getValue() !== attr_other.getValue()) { return false; }
+ if (this.attributes[i].getType() !== attr_other.getType()) { return false; }
+ }
+ // children must be ordered identically
+ if (this.children.length !== other.getChildCount()) { return false; }
+ if (this.children.length>0) {
+ var child1, child2;
+ for (i = 0, j = this.children.length; i < j; i++) {
+ child1 = this.getChild(i);
+ child2 = other.getChild(i);
+ if (!child1.equals(child2)) { return false; }
+ }
+ return true;
+ }
+ return (this.content === other.content);
+ },
+ /**
+ * @member XMLElement
+ * The getContent() function returns the content of an element. If there is no such content, null is returned
+ *
+ * @return {String} the (possibly null) content
+ */
+ getContent: function(){
+ if (this.type === "TEXT" || this.type === "CDATA") {
+ return this.content;
+ }
+ var children = this.children;
+ if (children.length === 1 && (children[0].type === "TEXT" || children[0].type === "CDATA")) {
+ return children[0].content;
+ }
+ return null;
+ },
+ /**
+ * @member XMLElement
+ * The getAttribute() function returns the value of an attribute
+ *
+ * @param {String} name the non-null full name of the attribute
+ * @param {String} namespace the namespace URI, which may be null
+ * @param {String} defaultValue the default value of the attribute
+ *
+ * @return {String} the value, or defaultValue if the attribute does not exist
+ */
+ getAttribute: function (){
+ var attribute;
+ if (arguments.length === 2) {
+ attribute = this.findAttribute(arguments[0]);
+ if (attribute) {
+ return attribute.getValue();
+ }
+ return arguments[1];
+ } else if (arguments.length === 1) {
+ attribute = this.findAttribute(arguments[0]);
+ if (attribute) {
+ return attribute.getValue();
+ }
+ return null;
+ } else if (arguments.length === 3) {
+ attribute = this.findAttribute(arguments[0],arguments[1]);
+ if (attribute) {
+ return attribute.getValue();
+ }
+ return arguments[2];
+ }
+ },
+ /**
+ * @member XMLElement
+ * The getStringAttribute() function returns the string attribute of the element
+ * If the <b>defaultValue</b> parameter is used and the attribute doesn't exist, the <b>defaultValue</b> value is returned.
+ * When calling the function without the <b>defaultValue</b> parameter, if the attribute doesn't exist, the value 0 is returned.
+ *
+ * @param name the name of the attribute
+ * @param defaultValue value returned if the attribute is not found
+ *
+ * @return {String} the value, or defaultValue if the attribute does not exist
+ */
+ getStringAttribute: function() {
+ if (arguments.length === 1) {
+ return this.getAttribute(arguments[0]);
+ }
+ if (arguments.length === 2) {
+ return this.getAttribute(arguments[0], arguments[1]);
+ }
+ return this.getAttribute(arguments[0], arguments[1],arguments[2]);
+ },
+ /**
+ * Processing 1.5 XML API wrapper for the generic String
+ * attribute getter. This may only take one argument.
+ */
+ getString: function(attributeName) {
+ return this.getStringAttribute(attributeName);
+ },
+ /**
+ * @member XMLElement
+ * The getFloatAttribute() function returns the float attribute of the element.
+ * If the <b>defaultValue</b> parameter is used and the attribute doesn't exist, the <b>defaultValue</b> value is returned.
+ * When calling the function without the <b>defaultValue</b> parameter, if the attribute doesn't exist, the value 0 is returned.
+ *
+ * @param name the name of the attribute
+ * @param defaultValue value returned if the attribute is not found
+ *
+ * @return {float} the value, or defaultValue if the attribute does not exist
+ */
+ getFloatAttribute: function() {
+ if (arguments.length === 1 ) {
+ return parseFloat(this.getAttribute(arguments[0], 0));
+ }
+ if (arguments.length === 2 ) {
+ return this.getAttribute(arguments[0], arguments[1]);
+ }
+ return this.getAttribute(arguments[0], arguments[1],arguments[2]);
+ },
+ /**
+ * Processing 1.5 XML API wrapper for the generic float
+ * attribute getter. This may only take one argument.
+ */
+ getFloat: function(attributeName) {
+ return this.getFloatAttribute(attributeName);
+ },
+ /**
+ * @member XMLElement
+ * The getIntAttribute() function returns the integer attribute of the element.
+ * If the <b>defaultValue</b> parameter is used and the attribute doesn't exist, the <b>defaultValue</b> value is returned.
+ * When calling the function without the <b>defaultValue</b> parameter, if the attribute doesn't exist, the value 0 is returned.
+ *
+ * @param name the name of the attribute
+ * @param defaultValue value returned if the attribute is not found
+ *
+ * @return {int} the value, or defaultValue if the attribute does not exist
+ */
+ getIntAttribute: function () {
+ if (arguments.length === 1) {
+ return this.getAttribute( arguments[0], 0 );
+ }
+ if (arguments.length === 2) {
+ return this.getAttribute(arguments[0], arguments[1]);
+ }
+ return this.getAttribute(arguments[0], arguments[1],arguments[2]);
+ },
+ /**
+ * Processing 1.5 XML API wrapper for the generic int
+ * attribute getter. This may only take one argument.
+ */
+ getInt: function(attributeName) {
+ return this.getIntAttribute(attributeName);
+ },
+ /**
+ * @member XMLElement
+ * The hasChildren() function returns whether the element has children.
+ *
+ * @return {boolean} true if the element has children.
+ */
+ hasChildren: function () {
+ return this.children.length > 0 ;
+ },
+ /**
+ * @member XMLElement
+ * The addChild() function adds a child element
+ *
+ * @param {XMLElement} child the non-null child to add.
+ */
+ addChild: function (child) {
+ if (child !== null) {
+ child.parent = this;
+ this.children.push(child);
+ }
+ },
+ /**
+ * @member XMLElement
+ * The insertChild() function inserts a child element at the index provided
+ *
+ * @param {XMLElement} child the non-null child to add.
+ * @param {int} index where to put the child.
+ */
+ insertChild: function (child, index) {
+ if (child) {
+ if ((child.getLocalName() === null) && (! this.hasChildren())) {
+ var lastChild = this.children[this.children.length -1];
+ if (lastChild.getLocalName() === null) {
+ lastChild.setContent(lastChild.getContent() + child.getContent());
+ return;
+ }
+ }
+ child.parent = this;
+ this.children.splice(index,0,child);
+ }
+ },
+ /**
+ * @member XMLElement
+ * The getChild() returns the child XMLElement as specified by the <b>index</b> parameter.
+ * The value of the <b>index</b> parameter must be less than the total number of children to avoid going out of the array storing the child elements.
+ * When the <b>path</b> parameter is specified, then it will return all children that match that path. The path is a series of elements and sub-elements, separated by slashes.
+ *
+ * @param {int} index where to put the child.
+ * @param {String} path path to a particular element
+ *
+ * @return {XMLElement} the element
+ */
+ getChild: function (selector) {
+ if (typeof selector === "number") {
+ return this.children[selector];
+ }
+ if (selector.indexOf('/') !== -1) {
+ // path traversal is required
+ return this.getChildRecursive(selector.split("/"), 0);
+ }
+ var kid, kidName;
+ for (var i = 0, j = this.getChildCount(); i < j; i++) {
+ kid = this.getChild(i);
+ kidName = kid.getName();
+ if (kidName !== null && kidName === selector) {
+ return kid;
+ }
+ }
+ return null;
+ },
+ /**
+ * @member XMLElement
+ * The getChildren() returns all of the children as an XMLElement array.
+ * When the <b>path</b> parameter is specified, then it will return all children that match that path.
+ * The path is a series of elements and sub-elements, separated by slashes.
+ *
+ * @param {String} path element name or path/to/element
+ *
+ * @return {XMLElement} array of child elements that match
+ *
+ * @see XMLElement#getChildCount()
+ * @see XMLElement#getChild()
+ */
+ getChildren: function(){
+ if (arguments.length === 1) {
+ if (typeof arguments[0] === "number") {
+ return this.getChild( arguments[0]);
+ }
+ if (arguments[0].indexOf('/') !== -1) { // path was given
+ return this.getChildrenRecursive( arguments[0].split("/"), 0);
+ }
+ var matches = [];
+ var kid, kidName;
+ for (var i = 0, j = this.getChildCount(); i < j; i++) {
+ kid = this.getChild(i);
+ kidName = kid.getName();
+ if (kidName !== null && kidName === arguments[0]) {
+ matches.push(kid);
+ }
+ }
+ return matches;
+ }
+ return this.children;
+ },
+ /**
+ * @member XMLElement
+ * The getChildCount() returns the number of children for the element.
+ *
+ * @return {int} the count
+ *
+ * @see XMLElement#getChild()
+ * @see XMLElement#getChildren()
+ */
+ getChildCount: function() {
+ return this.children.length;
+ },
+ /**
+ * @member XMLElement
+ * Internal helper function for getChild().
+ *
+ * @param {String[]} items result of splitting the query on slashes
+ * @param {int} offset where in the items[] array we're currently looking
+ *
+ * @return {XMLElement} matching element or null if no match
+ */
+ getChildRecursive: function (items, offset) {
+ // terminating clause: we are the requested candidate
+ if (offset === items.length) {
+ return this;
+ }
+ // continuation clause
+ var kid, kidName, matchName = items[offset];
+ for(var i = 0, j = this.getChildCount(); i < j; i++) {
+ kid = this.getChild(i);
+ kidName = kid.getName();
+ if (kidName !== null && kidName === matchName) {
+ return kid.getChildRecursive(items, offset+1);
+ }
+ }
+ return null;
+ },
+ /**
+ * @member XMLElement
+ * Internal helper function for getChildren().
+ *
+ * @param {String[]} items result of splitting the query on slashes
+ * @param {int} offset where in the items[] array we're currently looking
+ *
+ * @return {XMLElement[]} matching elements or empty array if no match
+ */
+ getChildrenRecursive: function (items, offset) {
+ if (offset === items.length-1) {
+ return this.getChildren(items[offset]);
+ }
+ var matches = this.getChildren(items[offset]);
+ var kidMatches = [];
+ for (var i = 0; i < matches.length; i++) {
+ kidMatches = kidMatches.concat(matches[i].getChildrenRecursive(items, offset+1));
+ }
+ return kidMatches;
+ },
+ /**
+ * @member XMLElement
+ * The isLeaf() function returns whether the element is a leaf element.
+ *
+ * @return {boolean} true if the element has no children.
+ */
+ isLeaf: function() {
+ return !this.hasChildren();
+ },
+ /**
+ * @member XMLElement
+ * The listChildren() function put the names of all children into an array. Same as looping through
+ * each child and calling getName() on each XMLElement.
+ *
+ * @return {String[]} a list of element names.
+ */
+ listChildren: function() {
+ var arr = [];
+ for (var i = 0, j = this.children.length; i < j; i++) {
+ arr.push( this.getChild(i).getName());
+ }
+ return arr;
+ },
+ /**
+ * @member XMLElement
+ * The removeAttribute() function removes an attribute
+ *
+ * @param {String} name the non-null name of the attribute.
+ * @param {String} namespace the namespace URI of the attribute, which may be null.
+ */
+ removeAttribute: function (name , namespace) {
+ this.namespace = namespace || "";
+ for (var i = 0, j = this.attributes.length; i < j; i++) {
+ if (this.attributes[i].getName() === name && this.attributes[i].getNamespace() === this.namespace) {
+ this.attributes.splice(i, 1);
+ break;
+ }
+ }
+ },
+ /**
+ * @member XMLElement
+ * The removeChild() removes a child element.
+ *
+ * @param {XMLElement} child the the non-null child to be renoved
+ */
+ removeChild: function(child) {
+ if (child) {
+ for (var i = 0, j = this.children.length; i < j; i++) {
+ if (this.children[i].equals(child)) {
+ this.children.splice(i, 1);
+ break;
+ }
+ }
+ }
+ },
+ /**
+ * @member XMLElement
+ * The removeChildAtIndex() removes the child located at a certain index
+ *
+ * @param {int} index the index of the child, where the first child has index 0
+ */
+ removeChildAtIndex: function(index) {
+ if (this.children.length > index) { //make sure its not outofbounds
+ this.children.splice(index, 1);
+ }
+ },
+ /**
+ * @member XMLElement
+ * The findAttribute() function searches an attribute
+ *
+ * @param {String} name fullName the non-null full name of the attribute
+ * @param {String} namespace the name space, which may be null
+ *
+ * @return {XMLAttribute} the attribute, or null if the attribute does not exist.
+ */
+ findAttribute: function (name, namespace) {
+ this.namespace = namespace || "";
+ for (var i = 0, j = this.attributes.length; i < j; i++) {
+ if (this.attributes[i].getName() === name && this.attributes[i].getNamespace() === this.namespace) {
+ return this.attributes[i];
+ }
+ }
+ return null;
+ },
+ /**
+ * @member XMLElement
+ * The setAttribute() function sets an attribute.
+ *
+ * @param {String} name the non-null full name of the attribute
+ * @param {String} namespace the non-null value of the attribute
+ */
+ setAttribute: function() {
+ var attr;
+ if (arguments.length === 3) {
+ var index = arguments[0].indexOf(':');
+ var name = arguments[0].substring(index + 1);
+ attr = this.findAttribute(name, arguments[1]);
+ if (attr) {
+ attr.setValue(arguments[2]);
+ } else {
+ attr = new XMLAttribute(arguments[0], name, arguments[1], arguments[2], "CDATA");
+ this.attributes.push(attr);
+ }
+ } else {
+ attr = this.findAttribute(arguments[0]);
+ if (attr) {
+ attr.setValue(arguments[1]);
+ } else {
+ attr = new XMLAttribute(arguments[0], arguments[0], null, arguments[1], "CDATA");
+ this.attributes.push(attr);
+ }
+ }
+ },
+ /**
+ * Processing 1.5 XML API wrapper for the generic String
+ * attribute setter. This must take two arguments.
+ */
+ setString: function(attribute, value) {
+ this.setAttribute(attribute, value);
+ },
+ /**
+ * Processing 1.5 XML API wrapper for the generic int
+ * attribute setter. This must take two arguments.
+ */
+ setInt: function(attribute, value) {
+ this.setAttribute(attribute, value);
+ },
+ /**
+ * Processing 1.5 XML API wrapper for the generic float
+ * attribute setter. This must take two arguments.
+ */
+ setFloat: function(attribute, value) {
+ this.setAttribute(attribute, value);
+ },
+ /**
+ * @member XMLElement
+ * The setContent() function sets the #PCDATA content. It is an error to call this method with a
+ * non-null value if there are child objects.
+ *
+ * @param {String} content the (possibly null) content
+ */
+ setContent: function(content) {
+ if (this.children.length > 0) {
+ Processing.debug("Tried to set content for XMLElement with children"); }
+ this.content = content;
+ },
+ /**
+ * @member XMLElement
+ * The setName() function sets the full name. This method also sets the short name and clears the
+ * namespace URI.
+ *
+ * @param {String} name the non-null name
+ * @param {String} namespace the namespace URI, which may be null.
+ */
+ setName: function() {
+ if (arguments.length === 1) {
+ this.name = arguments[0];
+ this.fullName = arguments[0];
+ this.namespace = null;
+ } else {
+ var index = arguments[0].indexOf(':');
+ if ((arguments[1] === null) || (index < 0)) {
+ this.name = arguments[0];
+ } else {
+ this.name = arguments[0].substring(index + 1);
+ }
+ this.fullName = arguments[0];
+ this.namespace = arguments[1];
+ }
+ },
+ /**
+ * @member XMLElement
+ * The getName() function returns the full name (i.e. the name including an eventual namespace
+ * prefix) of the element.
+ *
+ * @return {String} the name, or null if the element only contains #PCDATA.
+ */
+ getName: function() {
+ return this.fullName;
+ },
+ /**
+ * @member XMLElement
+ * The getLocalName() function returns the local name (i.e. the name excluding an eventual namespace
+ * prefix) of the element.
+ *
+ * @return {String} the name, or null if the element only contains #PCDATA.
+ */
+ getLocalName: function() {
+ return this.name;
+ },
+ /**
+ * @member XMLElement
+ * The getAttributeCount() function returns the number of attributes for the node
+ * that this XMLElement represents.
+ *
+ * @return {int} the number of attributes in this XMLElement
+ */
+ getAttributeCount: function() {
+ return this.attributes.length;
+ },
+ /**
+ * @member XMLElement
+ * The toString() function returns the XML definition of an XMLElement.
+ *
+ * @return {String} the XML definition of this XMLElement
+ */
+ toString: function() {
+ // shortcut for text and cdata nodes
+ if (this.type === "TEXT") {
+ return this.content || "";
+ }
+
+ if (this.type === "CDATA") {
+ return this.cdata || "";
+ }
+
+ // real XMLElements
+ var tagstring = this.fullName;
+ var xmlstring = "<" + tagstring;
+ var a,c;
+
+ // serialize the attributes to XML string
+ for (a = 0; a<this.attributes.length; a++) {
+ var attr = this.attributes[a];
+ xmlstring += " " + attr.getName() + "=" + '"' + attr.getValue() + '"';
+ }
+
+ // serialize all children to XML string
+ if (this.children.length === 0) {
+ if (this.content === "" || this.content === null || this.content === undefined) {
+ xmlstring += "/>";
+ } else {
+ xmlstring += ">" + this.content + "</"+tagstring+">";
+ }
+ } else {
+ xmlstring += ">";
+ for (c = 0; c<this.children.length; c++) {
+ xmlstring += this.children[c].toString();
+ }
+ xmlstring += "</" + tagstring + ">";
+ }
+ return xmlstring;
+ }
+ };
+
+ /**
+ * static Processing 1.5 XML API wrapper for the
+ * parse method. This may only take one argument.
+ */
+ XMLElement.parse = function(xmlstring) {
+ var element = new XMLElement();
+ element.parse(xmlstring);
+ return element;
+ };
+
+ return XMLElement;
+};
+
+},{}],21:[function(require,module,exports){
+/**
+ * web colors, by name
+ */
+module.exports = {
+ aliceblue: "#f0f8ff",
+ antiquewhite: "#faebd7",
+ aqua: "#00ffff",
+ aquamarine: "#7fffd4",
+ azure: "#f0ffff",
+ beige: "#f5f5dc",
+ bisque: "#ffe4c4",
+ black: "#000000",
+ blanchedalmond: "#ffebcd",
+ blue: "#0000ff",
+ blueviolet: "#8a2be2",
+ brown: "#a52a2a",
+ burlywood: "#deb887",
+ cadetblue: "#5f9ea0",
+ chartreuse: "#7fff00",
+ chocolate: "#d2691e",
+ coral: "#ff7f50",
+ cornflowerblue: "#6495ed",
+ cornsilk: "#fff8dc",
+ crimson: "#dc143c",
+ cyan: "#00ffff",
+ darkblue: "#00008b",
+ darkcyan: "#008b8b",
+ darkgoldenrod: "#b8860b",
+ darkgray: "#a9a9a9",
+ darkgreen: "#006400",
+ darkkhaki: "#bdb76b",
+ darkmagenta: "#8b008b",
+ darkolivegreen: "#556b2f",
+ darkorange: "#ff8c00",
+ darkorchid: "#9932cc",
+ darkred: "#8b0000",
+ darksalmon: "#e9967a",
+ darkseagreen: "#8fbc8f",
+ darkslateblue: "#483d8b",
+ darkslategray: "#2f4f4f",
+ darkturquoise: "#00ced1",
+ darkviolet: "#9400d3",
+ deeppink: "#ff1493",
+ deepskyblue: "#00bfff",
+ dimgray: "#696969",
+ dodgerblue: "#1e90ff",
+ firebrick: "#b22222",
+ floralwhite: "#fffaf0",
+ forestgreen: "#228b22",
+ fuchsia: "#ff00ff",
+ gainsboro: "#dcdcdc",
+ ghostwhite: "#f8f8ff",
+ gold: "#ffd700",
+ goldenrod: "#daa520",
+ gray: "#808080",
+ green: "#008000",
+ greenyellow: "#adff2f",
+ honeydew: "#f0fff0",
+ hotpink: "#ff69b4",
+ indianred: "#cd5c5c",
+ indigo: "#4b0082",
+ ivory: "#fffff0",
+ khaki: "#f0e68c",
+ lavender: "#e6e6fa",
+ lavenderblush: "#fff0f5",
+ lawngreen: "#7cfc00",
+ lemonchiffon: "#fffacd",
+ lightblue: "#add8e6",
+ lightcoral: "#f08080",
+ lightcyan: "#e0ffff",
+ lightgoldenrodyellow: "#fafad2",
+ lightgrey: "#d3d3d3",
+ lightgreen: "#90ee90",
+ lightpink: "#ffb6c1",
+ lightsalmon: "#ffa07a",
+ lightseagreen: "#20b2aa",
+ lightskyblue: "#87cefa",
+ lightslategray: "#778899",
+ lightsteelblue: "#b0c4de",
+ lightyellow: "#ffffe0",
+ lime: "#00ff00",
+ limegreen: "#32cd32",
+ linen: "#faf0e6",
+ magenta: "#ff00ff",
+ maroon: "#800000",
+ mediumaquamarine: "#66cdaa",
+ mediumblue: "#0000cd",
+ mediumorchid: "#ba55d3",
+ mediumpurple: "#9370d8",
+ mediumseagreen: "#3cb371",
+ mediumslateblue: "#7b68ee",
+ mediumspringgreen: "#00fa9a",
+ mediumturquoise: "#48d1cc",
+ mediumvioletred: "#c71585",
+ midnightblue: "#191970",
+ mintcream: "#f5fffa",
+ mistyrose: "#ffe4e1",
+ moccasin: "#ffe4b5",
+ navajowhite: "#ffdead",
+ navy: "#000080",
+ oldlace: "#fdf5e6",
+ olive: "#808000",
+ olivedrab: "#6b8e23",
+ orange: "#ffa500",
+ orangered: "#ff4500",
+ orchid: "#da70d6",
+ palegoldenrod: "#eee8aa",
+ palegreen: "#98fb98",
+ paleturquoise: "#afeeee",
+ palevioletred: "#d87093",
+ papayawhip: "#ffefd5",
+ peachpuff: "#ffdab9",
+ peru: "#cd853f",
+ pink: "#ffc0cb",
+ plum: "#dda0dd",
+ powderblue: "#b0e0e6",
+ purple: "#800080",
+ red: "#ff0000",
+ rosybrown: "#bc8f8f",
+ royalblue: "#4169e1",
+ saddlebrown: "#8b4513",
+ salmon: "#fa8072",
+ sandybrown: "#f4a460",
+ seagreen: "#2e8b57",
+ seashell: "#fff5ee",
+ sienna: "#a0522d",
+ silver: "#c0c0c0",
+ skyblue: "#87ceeb",
+ slateblue: "#6a5acd",
+ slategray: "#708090",
+ snow: "#fffafa",
+ springgreen: "#00ff7f",
+ steelblue: "#4682b4",
+ tan: "#d2b48c",
+ teal: "#008080",
+ thistle: "#d8bfd8",
+ tomato: "#ff6347",
+ turquoise: "#40e0d0",
+ violet: "#ee82ee",
+ wheat: "#f5deb3",
+ white: "#ffffff",
+ whitesmoke: "#f5f5f5",
+ yellow: "#ffff00",
+ yellowgreen: "#9acd32"
+ };
+
+},{}],22:[function(require,module,exports){
+module.exports = function(virtHashCode, virtEquals, undef) {
+
+ return function withProxyFunctions(p, removeFirstArgument) {
+ /**
+ * The contains(string) function returns true if the string passed in the parameter
+ * is a substring of this string. It returns false if the string passed
+ * in the parameter is not a substring of this string.
+ *
+ * @param {String} The string to look for in the current string
+ *
+ * @return {boolean} returns true if this string contains
+ * the string passed as parameter. returns false, otherwise.
+ *
+ */
+ p.__contains = function (subject, subStr) {
+ if (typeof subject !== "string") {
+ return subject.contains.apply(subject, removeFirstArgument(arguments));
+ }
+ //Parameter is not null AND
+ //The type of the parameter is the same as this object (string)
+ //The javascript function that finds a substring returns 0 or higher
+ return (
+ (subject !== null) &&
+ (subStr !== null) &&
+ (typeof subStr === "string") &&
+ (subject.indexOf(subStr) > -1)
+ );
+ };
+
+ /**
+ * The __replaceAll() function searches all matches between a substring (or regular expression) and a string,
+ * and replaces the matched substring with a new substring
+ *
+ * @param {String} subject a substring
+ * @param {String} regex a substring or a regular expression
+ * @param {String} replace the string to replace the found value
+ *
+ * @return {String} returns result
+ *
+ * @see #match
+ */
+ p.__replaceAll = function(subject, regex, replacement) {
+ if (typeof subject !== "string") {
+ return subject.replaceAll.apply(subject, removeFirstArgument(arguments));
+ }
+
+ return subject.replace(new RegExp(regex, "g"), replacement);
+ };
+
+ /**
+ * The __replaceFirst() function searches first matche between a substring (or regular expression) and a string,
+ * and replaces the matched substring with a new substring
+ *
+ * @param {String} subject a substring
+ * @param {String} regex a substring or a regular expression
+ * @param {String} replace the string to replace the found value
+ *
+ * @return {String} returns result
+ *
+ * @see #match
+ */
+ p.__replaceFirst = function(subject, regex, replacement) {
+ if (typeof subject !== "string") {
+ return subject.replaceFirst.apply(subject, removeFirstArgument(arguments));
+ }
+
+ return subject.replace(new RegExp(regex, ""), replacement);
+ };
+
+ /**
+ * The __replace() function searches all matches between a substring and a string,
+ * and replaces the matched substring with a new substring
+ *
+ * @param {String} subject a substring
+ * @param {String} what a substring to find
+ * @param {String} replacement the string to replace the found value
+ *
+ * @return {String} returns result
+ */
+ p.__replace = function(subject, what, replacement) {
+ if (typeof subject !== "string") {
+ return subject.replace.apply(subject, removeFirstArgument(arguments));
+ }
+ if (what instanceof RegExp) {
+ return subject.replace(what, replacement);
+ }
+
+ if (typeof what !== "string") {
+ what = what.toString();
+ }
+ if (what === "") {
+ return subject;
+ }
+
+ var i = subject.indexOf(what);
+ if (i < 0) {
+ return subject;
+ }
+
+ var j = 0, result = "";
+ do {
+ result += subject.substring(j, i) + replacement;
+ j = i + what.length;
+ } while ( (i = subject.indexOf(what, j)) >= 0);
+ return result + subject.substring(j);
+ };
+
+ /**
+ * The __equals() function compares two strings (or objects) to see if they are the same.
+ * This method is necessary because it's not possible to compare strings using the equality operator (==).
+ * Returns true if the strings are the same and false if they are not.
+ *
+ * @param {String} subject a string used for comparison
+ * @param {String} other a string used for comparison with
+ *
+ * @return {boolean} true is the strings are the same false otherwise
+ */
+ p.__equals = function(subject, other) {
+ if (subject.equals instanceof Function) {
+ return subject.equals.apply(subject, removeFirstArgument(arguments));
+ }
+
+ return virtEquals(subject, other);
+ };
+
+ /**
+ * The __equalsIgnoreCase() function compares two strings to see if they are the same.
+ * Returns true if the strings are the same, either when forced to all lower case or
+ * all upper case.
+ *
+ * @param {String} subject a string used for comparison
+ * @param {String} other a string used for comparison with
+ *
+ * @return {boolean} true is the strings are the same, ignoring case. false otherwise
+ */
+ p.__equalsIgnoreCase = function(subject, other) {
+ if (typeof subject !== "string") {
+ return subject.equalsIgnoreCase.apply(subject, removeFirstArgument(arguments));
+ }
+
+ return subject.toLowerCase() === other.toLowerCase();
+ };
+
+ /**
+ * The __toCharArray() function splits the string into a char array.
+ *
+ * @param {String} subject The string
+ *
+ * @return {Char[]} a char array
+ */
+ p.__toCharArray = function(subject) {
+ if (typeof subject !== "string") {
+ return subject.toCharArray.apply(subject, removeFirstArgument(arguments));
+ }
+
+ var chars = [];
+ for (var i = 0, len = subject.length; i < len; ++i) {
+ chars[i] = new Char(subject.charAt(i));
+ }
+ return chars;
+ };
+
+ /**
+ * The __split() function splits a string using the regex delimiter
+ * specified. If limit is specified, the resultant array will have number
+ * of elements equal to or less than the limit.
+ *
+ * @param {String} subject string to be split
+ * @param {String} regexp regex string used to split the subject
+ * @param {int} limit max number of tokens to be returned
+ *
+ * @return {String[]} an array of tokens from the split string
+ */
+ p.__split = function(subject, regex, limit) {
+ if (typeof subject !== "string") {
+ return subject.split.apply(subject, removeFirstArgument(arguments));
+ }
+
+ var pattern = new RegExp(regex);
+
+ // If limit is not specified, use JavaScript's built-in String.split.
+ if ((limit === undef) || (limit < 1)) {
+ return subject.split(pattern);
+ }
+
+ // If limit is specified, JavaScript's built-in String.split has a
+ // different behaviour than Java's. A Java-compatible implementation is
+ // provided here.
+ var result = [], currSubject = subject, pos;
+ while (((pos = currSubject.search(pattern)) !== -1) && (result.length < (limit - 1))) {
+ var match = pattern.exec(currSubject).toString();
+ result.push(currSubject.substring(0, pos));
+ currSubject = currSubject.substring(pos + match.length);
+ }
+ if ((pos !== -1) || (currSubject !== "")) {
+ result.push(currSubject);
+ }
+ return result;
+ };
+
+ /**
+ * The codePointAt() function returns the unicode value of the character at a given index of a string.
+ *
+ * @param {int} idx the index of the character
+ *
+ * @return {String} code the String containing the unicode value of the character
+ */
+ p.__codePointAt = function(subject, idx) {
+ var code = subject.charCodeAt(idx),
+ hi,
+ low;
+ if (0xD800 <= code && code <= 0xDBFF) {
+ hi = code;
+ low = subject.charCodeAt(idx + 1);
+ return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
+ }
+ return code;
+ };
+
+ /**
+ * The matches() function checks whether or not a string matches a given regular expression.
+ *
+ * @param {String} str the String on which the match is tested
+ * @param {String} regexp the regexp for which a match is tested
+ *
+ * @return {boolean} true if the string fits the regexp, false otherwise
+ */
+ p.__matches = function(str, regexp) {
+ return (new RegExp(regexp)).test(str);
+ };
+
+ /**
+ * The startsWith() function tests if a string starts with the specified prefix. If the prefix
+ * is the empty String or equal to the subject String, startsWith() will also return true.
+ *
+ * @param {String} prefix the String used to compare against the start of the subject String.
+ * @param {int} toffset (optional) an offset into the subject String where searching should begin.
+ *
+ * @return {boolean} true if the subject String starts with the prefix.
+ */
+ p.__startsWith = function(subject, prefix, toffset) {
+ if (typeof subject !== "string") {
+ return subject.startsWith.apply(subject, removeFirstArgument(arguments));
+ }
+
+ toffset = toffset || 0;
+ if (toffset < 0 || toffset > subject.length) {
+ return false;
+ }
+ return (prefix === '' || prefix === subject) ? true : (subject.indexOf(prefix) === toffset);
+ };
+
+ /**
+ * The endsWith() function tests if a string ends with the specified suffix. If the suffix
+ * is the empty String, endsWith() will also return true.
+ *
+ * @param {String} suffix the String used to compare against the end of the subject String.
+ *
+ * @return {boolean} true if the subject String starts with the prefix.
+ */
+ p.__endsWith = function(subject, suffix) {
+ if (typeof subject !== "string") {
+ return subject.endsWith.apply(subject, removeFirstArgument(arguments));
+ }
+
+ var suffixLen = suffix ? suffix.length : 0;
+ return (suffix === '' || suffix === subject) ? true :
+ (subject.indexOf(suffix) === subject.length - suffixLen);
+ };
+
+ /**
+ * The returns hash code of the.
+ *
+ * @param {Object} subject The string
+ *
+ * @return {int} a hash code
+ */
+ p.__hashCode = function(subject) {
+ if (subject.hashCode instanceof Function) {
+ return subject.hashCode.apply(subject, removeFirstArgument(arguments));
+ }
+ return virtHashCode(subject);
+ };
+
+ /**
+ * The __printStackTrace() prints stack trace to the console.
+ *
+ * @param {Exception} subject The error
+ */
+ p.__printStackTrace = function(subject) {
+ p.println("Exception: " + subject.toString() );
+ };
+ };
+
+};
+
+},{}],23:[function(require,module,exports){
+/**
+ * For many "math" functions, we can delegate
+ * to the Math object. For others, we can't.
+ */
+module.exports = function withMath(p, undef) {
+ var internalRandomGenerator = function() { return Math.random(); };
+
+ /**
+ * Calculates the absolute value (magnitude) of a number. The absolute value of a number is always positive.
+ *
+ * @param {int|float} value int or float
+ *
+ * @returns {int|float}
+ */
+ p.abs = Math.abs;
+
+ /**
+ * Calculates the closest int value that is greater than or equal to the value of the parameter.
+ * For example, ceil(9.03) returns the value 10.
+ *
+ * @param {float} value float
+ *
+ * @returns {int}
+ *
+ * @see floor
+ * @see round
+ */
+ p.ceil = Math.ceil;
+
+ /**
+ * Returns Euler's number e (2.71828...) raised to the power of the value parameter.
+ *
+ * @param {int|float} value int or float: the exponent to raise e to
+ *
+ * @returns {float}
+ */
+ p.exp = Math.exp;
+
+ /**
+ * Calculates the closest int value that is less than or equal to the value of the parameter.
+ *
+ * @param {int|float} value the value to floor
+ *
+ * @returns {int|float}
+ *
+ * @see ceil
+ * @see round
+ */
+ p.floor = Math.floor;
+
+ /**
+ * Calculates the natural logarithm (the base-e logarithm) of a number. This function
+ * expects the values greater than 0.0.
+ *
+ * @param {int|float} value int or float: number must be greater then 0.0
+ *
+ * @returns {float}
+ */
+ p.log = Math.log;
+
+ /**
+ * Facilitates exponential expressions. The pow() function is an efficient way of
+ * multiplying numbers by themselves (or their reciprocal) in large quantities.
+ * For example, pow(3, 5) is equivalent to the expression 3*3*3*3*3 and pow(3, -5)
+ * is equivalent to 1 / 3*3*3*3*3.
+ *
+ * @param {int|float} num base of the exponential expression
+ * @param {int|float} exponent power of which to raise the base
+ *
+ * @returns {float}
+ *
+ * @see sqrt
+ */
+ p.pow = Math.pow;
+
+ /**
+ * Calculates the integer closest to the value parameter. For example, round(9.2) returns the value 9.
+ *
+ * @param {float} value number to round
+ *
+ * @returns {int}
+ *
+ * @see floor
+ * @see ceil
+ */
+ p.round = Math.round;
+ /**
+ * Calculates the square root of a number. The square root of a number is always positive,
+ * even though there may be a valid negative root. The square root s of number a is such
+ * that s*s = a. It is the opposite of squaring.
+ *
+ * @param {float} value int or float, non negative
+ *
+ * @returns {float}
+ *
+ * @see pow
+ * @see sq
+ */
+
+ p.sqrt = Math.sqrt;
+
+ // Trigonometry
+ /**
+ * The inverse of cos(), returns the arc cosine of a value. This function expects the
+ * values in the range of -1 to 1 and values are returned in the range 0 to PI (3.1415927).
+ *
+ * @param {float} value the value whose arc cosine is to be returned
+ *
+ * @returns {float}
+ *
+ * @see cos
+ * @see asin
+ * @see atan
+ */
+ p.acos = Math.acos;
+
+ /**
+ * The inverse of sin(), returns the arc sine of a value. This function expects the values
+ * in the range of -1 to 1 and values are returned in the range -PI/2 to PI/2.
+ *
+ * @param {float} value the value whose arc sine is to be returned
+ *
+ * @returns {float}
+ *
+ * @see sin
+ * @see acos
+ * @see atan
+ */
+ p.asin = Math.asin;
+
+ /**
+ * The inverse of tan(), returns the arc tangent of a value. This function expects the values
+ * in the range of -Infinity to Infinity (exclusive) and values are returned in the range -PI/2 to PI/2 .
+ *
+ * @param {float} value -Infinity to Infinity (exclusive)
+ *
+ * @returns {float}
+ *
+ * @see tan
+ * @see asin
+ * @see acos
+ */
+ p.atan = Math.atan;
+
+ /**
+ * Calculates the angle (in radians) from a specified point to the coordinate origin as measured from
+ * the positive x-axis. Values are returned as a float in the range from PI to -PI. The atan2() function
+ * is most often used for orienting geometry to the position of the cursor. Note: The y-coordinate of the
+ * point is the first parameter and the x-coordinate is the second due the the structure of calculating the tangent.
+ *
+ * @param {float} y y-coordinate of the point
+ * @param {float} x x-coordinate of the point
+ *
+ * @returns {float}
+ *
+ * @see tan
+ */
+ p.atan2 = Math.atan2;
+
+ /**
+ * Calculates the cosine of an angle. This function expects the values of the angle parameter to be provided
+ * in radians (values from 0 to PI*2). Values are returned in the range -1 to 1.
+ *
+ * @param {float} value an angle in radians
+ *
+ * @returns {float}
+ *
+ * @see tan
+ * @see sin
+ */
+ p.cos = Math.cos;
+
+ /**
+ * Calculates the sine of an angle. This function expects the values of the angle parameter to be provided in
+ * radians (values from 0 to 6.28). Values are returned in the range -1 to 1.
+ *
+ * @param {float} value an angle in radians
+ *
+ * @returns {float}
+ *
+ * @see cos
+ * @see radians
+ */
+ p.sin = Math.sin;
+
+ /**
+ * Calculates the ratio of the sine and cosine of an angle. This function expects the values of the angle
+ * parameter to be provided in radians (values from 0 to PI*2). Values are returned in the range infinity to -infinity.
+ *
+ * @param {float} value an angle in radians
+ *
+ * @returns {float}
+ *
+ * @see cos
+ * @see sin
+ * @see radians
+ */
+ p.tan = Math.tan;
+
+ /**
+ * Constrains a value to not exceed a maximum and minimum value.
+ *
+ * @param {int|float} value the value to constrain
+ * @param {int|float} value minimum limit
+ * @param {int|float} value maximum limit
+ *
+ * @returns {int|float}
+ *
+ * @see max
+ * @see min
+ */
+ p.constrain = function(aNumber, aMin, aMax) {
+ return aNumber > aMax ? aMax : aNumber < aMin ? aMin : aNumber;
+ };
+
+ /**
+ * Calculates the distance between two points.
+ *
+ * @param {int|float} x1 int or float: x-coordinate of the first point
+ * @param {int|float} y1 int or float: y-coordinate of the first point
+ * @param {int|float} z1 int or float: z-coordinate of the first point
+ * @param {int|float} x2 int or float: x-coordinate of the second point
+ * @param {int|float} y2 int or float: y-coordinate of the second point
+ * @param {int|float} z2 int or float: z-coordinate of the second point
+ *
+ * @returns {float}
+ */
+ p.dist = function() {
+ var dx, dy, dz;
+ if (arguments.length === 4) {
+ dx = arguments[0] - arguments[2];
+ dy = arguments[1] - arguments[3];
+ return Math.sqrt(dx * dx + dy * dy);
+ }
+ if (arguments.length === 6) {
+ dx = arguments[0] - arguments[3];
+ dy = arguments[1] - arguments[4];
+ dz = arguments[2] - arguments[5];
+ return Math.sqrt(dx * dx + dy * dy + dz * dz);
+ }
+ };
+
+ /**
+ * Calculates a number between two numbers at a specific increment. The amt parameter is the
+ * amount to interpolate between the two values where 0.0 equal to the first point, 0.1 is very
+ * near the first point, 0.5 is half-way in between, etc. The lerp function is convenient for
+ * creating motion along a straight path and for drawing dotted lines.
+ *
+ * @param {int|float} value1 float or int: first value
+ * @param {int|float} value2 float or int: second value
+ * @param {int|float} amt float: between 0.0 and 1.0
+ *
+ * @returns {float}
+ *
+ * @see curvePoint
+ * @see bezierPoint
+ */
+ p.lerp = function(value1, value2, amt) {
+ return ((value2 - value1) * amt) + value1;
+ };
+
+ /**
+ * Calculates the magnitude (or length) of a vector. A vector is a direction in space commonly
+ * used in computer graphics and linear algebra. Because it has no "start" position, the magnitude
+ * of a vector can be thought of as the distance from coordinate (0,0) to its (x,y) value.
+ * Therefore, mag() is a shortcut for writing "dist(0, 0, x, y)".
+ *
+ * @param {int|float} a float or int: first value
+ * @param {int|float} b float or int: second value
+ * @param {int|float} c float or int: third value
+ *
+ * @returns {float}
+ *
+ * @see dist
+ */
+ p.mag = function(a, b, c) {
+ if (c) {
+ return Math.sqrt(a * a + b * b + c * c);
+ }
+
+ return Math.sqrt(a * a + b * b);
+ };
+
+ /**
+ * Re-maps a number from one range to another. In the example above, the number '25' is converted from
+ * a value in the range 0..100 into a value that ranges from the left edge (0) to the right edge (width) of the screen.
+ * Numbers outside the range are not clamped to 0 and 1, because out-of-range values are often intentional and useful.
+ *
+ * @param {float} value The incoming value to be converted
+ * @param {float} istart Lower bound of the value's current range
+ * @param {float} istop Upper bound of the value's current range
+ * @param {float} ostart Lower bound of the value's target range
+ * @param {float} ostop Upper bound of the value's target range
+ *
+ * @returns {float}
+ *
+ * @see norm
+ * @see lerp
+ */
+ p.map = function(value, istart, istop, ostart, ostop) {
+ return ostart + (ostop - ostart) * ((value - istart) / (istop - istart));
+ };
+
+ /**
+ * Determines the largest value in a sequence of numbers.
+ *
+ * @param {int|float} value1 int or float
+ * @param {int|float} value2 int or float
+ * @param {int|float} value3 int or float
+ * @param {int|float} array int or float array
+ *
+ * @returns {int|float}
+ *
+ * @see min
+ */
+ p.max = function() {
+ if (arguments.length === 2) {
+ return arguments[0] < arguments[1] ? arguments[1] : arguments[0];
+ }
+ var numbers = arguments.length === 1 ? arguments[0] : arguments; // if single argument, array is used
+ if (! ("length" in numbers && numbers.length > 0)) {
+ throw "Non-empty array is expected";
+ }
+ var max = numbers[0],
+ count = numbers.length;
+ for (var i = 1; i < count; ++i) {
+ if (max < numbers[i]) {
+ max = numbers[i];
+ }
+ }
+ return max;
+ };
+
+ /**
+ * Determines the smallest value in a sequence of numbers.
+ *
+ * @param {int|float} value1 int or float
+ * @param {int|float} value2 int or float
+ * @param {int|float} value3 int or float
+ * @param {int|float} array int or float array
+ *
+ * @returns {int|float}
+ *
+ * @see max
+ */
+ p.min = function() {
+ if (arguments.length === 2) {
+ return arguments[0] < arguments[1] ? arguments[0] : arguments[1];
+ }
+ var numbers = arguments.length === 1 ? arguments[0] : arguments; // if single argument, array is used
+ if (! ("length" in numbers && numbers.length > 0)) {
+ throw "Non-empty array is expected";
+ }
+ var min = numbers[0],
+ count = numbers.length;
+ for (var i = 1; i < count; ++i) {
+ if (min > numbers[i]) {
+ min = numbers[i];
+ }
+ }
+ return min;
+ };
+
+ /**
+ * Normalizes a number from another range into a value between 0 and 1.
+ * Identical to map(value, low, high, 0, 1);
+ * Numbers outside the range are not clamped to 0 and 1, because out-of-range
+ * values are often intentional and useful.
+ *
+ * @param {float} aNumber The incoming value to be converted
+ * @param {float} low Lower bound of the value's current range
+ * @param {float} high Upper bound of the value's current range
+ *
+ * @returns {float}
+ *
+ * @see map
+ * @see lerp
+ */
+ p.norm = function(aNumber, low, high) {
+ return (aNumber - low) / (high - low);
+ };
+
+ /**
+ * Squares a number (multiplies a number by itself). The result is always a positive number,
+ * as multiplying two negative numbers always yields a positive result. For example, -1 * -1 = 1.
+ *
+ * @param {float} value int or float
+ *
+ * @returns {float}
+ *
+ * @see sqrt
+ */
+ p.sq = function(aNumber) {
+ return aNumber * aNumber;
+ };
+
+ /**
+ * Converts a radian measurement to its corresponding value in degrees. Radians and degrees are two ways of
+ * measuring the same thing. There are 360 degrees in a circle and 2*PI radians in a circle. For example,
+ * 90 degrees = PI/2 = 1.5707964. All trigonometric methods in Processing require their parameters to be specified in radians.
+ *
+ * @param {int|float} value an angle in radians
+ *
+ * @returns {float}
+ *
+ * @see radians
+ */
+ p.degrees = function(aAngle) {
+ return (aAngle * 180) / Math.PI;
+ };
+
+ /**
+ * Generates random numbers. Each time the random() function is called, it returns an unexpected value within
+ * the specified range. If one parameter is passed to the function it will return a float between zero and the
+ * value of the high parameter. The function call random(5) returns values between 0 and 5 (starting at zero,
+ * up to but not including 5). If two parameters are passed, it will return a float with a value between the
+ * parameters. The function call random(-5, 10.2) returns values starting at -5 up to (but not including) 10.2.
+ * To convert a floating-point random number to an integer, use the int() function.
+ *
+ * @param {int|float} value1 if one parameter is used, the top end to random from, if two params the low end
+ * @param {int|float} value2 the top end of the random range
+ *
+ * @returns {float}
+ *
+ * @see randomSeed
+ * @see noise
+ */
+ p.random = function() {
+ if(arguments.length === 0) {
+ return internalRandomGenerator();
+ }
+ if(arguments.length === 1) {
+ return internalRandomGenerator() * arguments[0];
+ }
+ var aMin = arguments[0], aMax = arguments[1];
+ return internalRandomGenerator() * (aMax - aMin) + aMin;
+ };
+
+ // Pseudo-random generator
+ function Marsaglia(i1, i2) {
+ // from http://www.math.uni-bielefeld.de/~sillke/ALGORITHMS/random/marsaglia-c
+ var z=i1 || 362436069, w= i2 || 521288629;
+ var intGenerator = function() {
+ z=(36969*(z&65535)+(z>>>16)) & 0xFFFFFFFF;
+ w=(18000*(w&65535)+(w>>>16)) & 0xFFFFFFFF;
+ return (((z&0xFFFF)<<16) | (w&0xFFFF)) & 0xFFFFFFFF;
+ };
+
+ this.doubleGenerator = function() {
+ var i = intGenerator() / 4294967296;
+ return i < 0 ? 1 + i : i;
+ };
+ this.intGenerator = intGenerator;
+ }
+
+ Marsaglia.createRandomized = function() {
+ var now = new Date();
+ return new Marsaglia((now / 60000) & 0xFFFFFFFF, now & 0xFFFFFFFF);
+ };
+
+ /**
+ * Sets the seed value for random(). By default, random() produces different results each time the
+ * program is run. Set the value parameter to a constant to return the same pseudo-random numbers
+ * each time the software is run.
+ *
+ * @param {int|float} seed int
+ *
+ * @see random
+ * @see noise
+ * @see noiseSeed
+ */
+ p.randomSeed = function(seed) {
+ internalRandomGenerator = (new Marsaglia(seed, (seed<<16)+(seed>>16))).doubleGenerator;
+ this.haveNextNextGaussian = false;
+ };
+
+ /**
+ * Returns a float from a random series of numbers having a mean of 0 and standard deviation of 1. Each time
+ * the randomGaussian() function is called, it returns a number fitting a Gaussian, or normal, distribution.
+ * There is theoretically no minimum or maximum value that randomGaussian() might return. Rather, there is just a
+ * very low probability that values far from the mean will be returned; and a higher probability that numbers
+ * near the mean will be returned.
+ *
+ * @returns {float}
+ *
+ * @see random
+ * @see noise
+ */
+ p.randomGaussian = function() {
+ if (this.haveNextNextGaussian) {
+ this.haveNextNextGaussian = false;
+ return this.nextNextGaussian;
+ }
+ var v1, v2, s;
+ do {
+ v1 = 2 * internalRandomGenerator() - 1; // between -1.0 and 1.0
+ v2 = 2 * internalRandomGenerator() - 1; // between -1.0 and 1.0
+ s = v1 * v1 + v2 * v2;
+ }
+ while (s >= 1 || s === 0);
+
+ var multiplier = Math.sqrt(-2 * Math.log(s) / s);
+ this.nextNextGaussian = v2 * multiplier;
+ this.haveNextNextGaussian = true;
+
+ return v1 * multiplier;
+ };
+
+ // Noise functions and helpers
+ function PerlinNoise(seed) {
+ var rnd = seed !== undef ? new Marsaglia(seed, (seed<<16)+(seed>>16)) : Marsaglia.createRandomized();
+ var i, j;
+ // http://www.noisemachine.com/talk1/17b.html
+ // http://mrl.nyu.edu/~perlin/noise/
+ // generate permutation
+ var perm = new Uint8Array(512);
+ for(i=0;i<256;++i) { perm[i] = i; }
+ for(i=0;i<256;++i) {
+ // NOTE: we can only do this because we've made sure the Marsaglia generator
+ // gives us numbers where the last byte in a pseudo-random number is
+ // still pseudo-random. If no 2nd argument is passed in the constructor,
+ // that is no longer the case and this pair swap will always run identically.
+ var t = perm[j = rnd.intGenerator() & 0xFF];
+ perm[j] = perm[i];
+ perm[i] = t;
+ }
+ // copy to avoid taking mod in perm[0];
+ for(i=0;i<256;++i) { perm[i + 256] = perm[i]; }
+
+ function grad3d(i,x,y,z) {
+ var h = i & 15; // convert into 12 gradient directions
+ var u = h<8 ? x : y,
+ v = h<4 ? y : h===12||h===14 ? x : z;
+ return ((h&1) === 0 ? u : -u) + ((h&2) === 0 ? v : -v);
+ }
+
+ function grad2d(i,x,y) {
+ var v = (i & 1) === 0 ? x : y;
+ return (i&2) === 0 ? -v : v;
+ }
+
+ function grad1d(i,x) {
+ return (i&1) === 0 ? -x : x;
+ }
+
+ function lerp(t,a,b) { return a + t * (b - a); }
+
+ this.noise3d = function(x, y, z) {
+ var X = Math.floor(x)&255, Y = Math.floor(y)&255, Z = Math.floor(z)&255;
+ x -= Math.floor(x); y -= Math.floor(y); z -= Math.floor(z);
+ var fx = (3-2*x)*x*x, fy = (3-2*y)*y*y, fz = (3-2*z)*z*z;
+ var p0 = perm[X]+Y, p00 = perm[p0] + Z, p01 = perm[p0 + 1] + Z,
+ p1 = perm[X + 1] + Y, p10 = perm[p1] + Z, p11 = perm[p1 + 1] + Z;
+ return lerp(fz,
+ lerp(fy, lerp(fx, grad3d(perm[p00], x, y, z), grad3d(perm[p10], x-1, y, z)),
+ lerp(fx, grad3d(perm[p01], x, y-1, z), grad3d(perm[p11], x-1, y-1,z))),
+ lerp(fy, lerp(fx, grad3d(perm[p00 + 1], x, y, z-1), grad3d(perm[p10 + 1], x-1, y, z-1)),
+ lerp(fx, grad3d(perm[p01 + 1], x, y-1, z-1), grad3d(perm[p11 + 1], x-1, y-1,z-1))));
+ };
+
+ this.noise2d = function(x, y) {
+ var X = Math.floor(x)&255, Y = Math.floor(y)&255;
+ x -= Math.floor(x); y -= Math.floor(y);
+ var fx = (3-2*x)*x*x, fy = (3-2*y)*y*y;
+ var p0 = perm[X]+Y, p1 = perm[X + 1] + Y;
+ return lerp(fy,
+ lerp(fx, grad2d(perm[p0], x, y), grad2d(perm[p1], x-1, y)),
+ lerp(fx, grad2d(perm[p0 + 1], x, y-1), grad2d(perm[p1 + 1], x-1, y-1)));
+ };
+
+ this.noise1d = function(x) {
+ var X = Math.floor(x)&255;
+ x -= Math.floor(x);
+ var fx = (3-2*x)*x*x;
+ return lerp(fx, grad1d(perm[X], x), grad1d(perm[X+1], x-1));
+ };
+ }
+
+ // processing defaults
+ var noiseProfile = { generator: undef, octaves: 4, fallout: 0.5, seed: undef};
+
+ /**
+ * Returns the Perlin noise value at specified coordinates. Perlin noise is a random sequence
+ * generator producing a more natural ordered, harmonic succession of numbers compared to the
+ * standard random() function. It was invented by Ken Perlin in the 1980s and been used since
+ * in graphical applications to produce procedural textures, natural motion, shapes, terrains etc.
+ * The main difference to the random() function is that Perlin noise is defined in an infinite
+ * n-dimensional space where each pair of coordinates corresponds to a fixed semi-random value
+ * (fixed only for the lifespan of the program). The resulting value will always be between 0.0
+ * and 1.0. Processing can compute 1D, 2D and 3D noise, depending on the number of coordinates
+ * given. The noise value can be animated by moving through the noise space as demonstrated in
+ * the example above. The 2nd and 3rd dimension can also be interpreted as time.
+ * The actual noise is structured similar to an audio signal, in respect to the function's use
+ * of frequencies. Similar to the concept of harmonics in physics, perlin noise is computed over
+ * several octaves which are added together for the final result.
+ * Another way to adjust the character of the resulting sequence is the scale of the input
+ * coordinates. As the function works within an infinite space the value of the coordinates
+ * doesn't matter as such, only the distance between successive coordinates does (eg. when using
+ * noise() within a loop). As a general rule the smaller the difference between coordinates, the
+ * smoother the resulting noise sequence will be. Steps of 0.005-0.03 work best for most applications,
+ * but this will differ depending on use.
+ *
+ * @param {float} x x coordinate in noise space
+ * @param {float} y y coordinate in noise space
+ * @param {float} z z coordinate in noise space
+ *
+ * @returns {float}
+ *
+ * @see random
+ * @see noiseDetail
+ */
+ p.noise = function(x, y, z) {
+ if(noiseProfile.generator === undef) {
+ // caching
+ noiseProfile.generator = new PerlinNoise(noiseProfile.seed);
+ }
+ var generator = noiseProfile.generator;
+ var effect = 1, k = 1, sum = 0;
+ for(var i=0; i<noiseProfile.octaves; ++i) {
+ effect *= noiseProfile.fallout;
+ switch (arguments.length) {
+ case 1:
+ sum += effect * (1 + generator.noise1d(k*x))/2; break;
+ case 2:
+ sum += effect * (1 + generator.noise2d(k*x, k*y))/2; break;
+ case 3:
+ sum += effect * (1 + generator.noise3d(k*x, k*y, k*z))/2; break;
+ }
+ k *= 2;
+ }
+ return sum;
+ };
+
+ /**
+ * Adjusts the character and level of detail produced by the Perlin noise function.
+ * Similar to harmonics in physics, noise is computed over several octaves. Lower octaves
+ * contribute more to the output signal and as such define the overal intensity of the noise,
+ * whereas higher octaves create finer grained details in the noise sequence. By default,
+ * noise is computed over 4 octaves with each octave contributing exactly half than its
+ * predecessor, starting at 50% strength for the 1st octave. This falloff amount can be
+ * changed by adding an additional function parameter. Eg. a falloff factor of 0.75 means
+ * each octave will now have 75% impact (25% less) of the previous lower octave. Any value
+ * between 0.0 and 1.0 is valid, however note that values greater than 0.5 might result in
+ * greater than 1.0 values returned by noise(). By changing these parameters, the signal
+ * created by the noise() function can be adapted to fit very specific needs and characteristics.
+ *
+ * @param {int} octaves number of octaves to be used by the noise() function
+ * @param {float} falloff falloff factor for each octave
+ *
+ * @see noise
+ */
+ p.noiseDetail = function(octaves, fallout) {
+ noiseProfile.octaves = octaves;
+ if(fallout !== undef) {
+ noiseProfile.fallout = fallout;
+ }
+ };
+
+ /**
+ * Sets the seed value for noise(). By default, noise() produces different results each
+ * time the program is run. Set the value parameter to a constant to return the same
+ * pseudo-random numbers each time the software is run.
+ *
+ * @param {int} seed int
+ *
+ * @returns {float}
+ *
+ * @see random
+ * @see radomSeed
+ * @see noise
+ * @see noiseDetail
+ */
+ p.noiseSeed = function(seed) {
+ noiseProfile.seed = seed;
+ noiseProfile.generator = undef;
+ };
+};
+
+},{}],24:[function(require,module,exports){
+/**
+ * Common functions traditionally on "p" that should be class functions
+ * that get bound to "p" when an instance is actually built, instead.
+ */
+module.exports = (function commonFunctions(undef) {
+
+ var CommonFunctions = {
+ /**
+ * Remove whitespace characters from the beginning and ending
+ * of a String or a String array. Works like String.trim() but includes the
+ * unicode nbsp character as well. If an array is passed in the function will return a new array not effecting the array passed in.
+ *
+ * @param {String} str the string to trim
+ * @param {String[]} str the string array to trim
+ *
+ * @return {String|String[]} retrurns a string or an array will removed whitespaces
+ */
+ trim: function(str) {
+ if (str instanceof Array) {
+ var arr = [];
+ for (var i = 0; i < str.length; i++) {
+ arr.push(str[i].replace(/^\s*/, '').replace(/\s*$/, '').replace(/\r*$/, ''));
+ }
+ return arr;
+ }
+ return str.replace(/^\s*/, '').replace(/\s*$/, '').replace(/\r*$/, '');
+ },
+
+ /**
+ * Converts a degree measurement to its corresponding value in radians. Radians and degrees are two ways of
+ * measuring the same thing. There are 360 degrees in a circle and 2*PI radians in a circle. For example,
+ * 90 degrees = PI/2 = 1.5707964. All trigonometric methods in Processing require their parameters to be specified in radians.
+ *
+ * @param {int|float} value an angle in radians
+ *
+ * @returns {float}
+ *
+ * @see degrees
+ */
+ radians: function(aAngle) {
+ return (aAngle / 180) * Math.PI;
+ },
+
+ /**
+ * Number-to-String formatting function. Prepends "plus" or "minus" depending
+ * on whether the value is positive or negative, respectively, after padding
+ * the value with zeroes on the left and right, the number of zeroes used dictated
+ * by the values 'leftDigits' and 'rightDigits'. 'value' cannot be an array.
+ *
+ * @param {int|float} value the number to format
+ * @param {String} plus the prefix for positive numbers
+ * @param {String} minus the prefix for negative numbers
+ * @param {int} left number of digits to the left of the decimal point
+ * @param {int} right number of digits to the right of the decimal point
+ * @param {String} group string delimited for groups, such as the comma in "1,000"
+ *
+ * @returns {String or String[]}
+ *
+ * @see nfCore
+ */
+ nfCoreScalar: function (value, plus, minus, leftDigits, rightDigits, group) {
+ var sign = (value < 0) ? minus : plus;
+ var autoDetectDecimals = rightDigits === 0;
+ var rightDigitsOfDefault = (rightDigits === undef || rightDigits < 0) ? 0 : rightDigits;
+
+ var absValue = Math.abs(value);
+ if (autoDetectDecimals) {
+ rightDigitsOfDefault = 1;
+ absValue *= 10;
+ while (Math.abs(Math.round(absValue) - absValue) > 1e-6 && rightDigitsOfDefault < 7) {
+ ++rightDigitsOfDefault;
+ absValue *= 10;
+ }
+ } else if (rightDigitsOfDefault !== 0) {
+ absValue *= Math.pow(10, rightDigitsOfDefault);
+ }
+
+ // Using Java's default rounding policy HALF_EVEN. This policy is based
+ // on the idea that 0.5 values round to the nearest even number, and
+ // everything else is rounded normally.
+ var number, doubled = absValue * 2;
+ if (Math.floor(absValue) === absValue) {
+ number = absValue;
+ } else if (Math.floor(doubled) === doubled) {
+ var floored = Math.floor(absValue);
+ number = floored + (floored % 2);
+ } else {
+ number = Math.round(absValue);
+ }
+
+ var buffer = "";
+ var totalDigits = leftDigits + rightDigitsOfDefault;
+ while (totalDigits > 0 || number > 0) {
+ totalDigits--;
+ buffer = "" + (number % 10) + buffer;
+ number = Math.floor(number / 10);
+ }
+ if (group !== undef) {
+ var i = buffer.length - 3 - rightDigitsOfDefault;
+ while(i > 0) {
+ buffer = buffer.substring(0,i) + group + buffer.substring(i);
+ i-=3;
+ }
+ }
+ if (rightDigitsOfDefault > 0) {
+ return sign + buffer.substring(0, buffer.length - rightDigitsOfDefault) +
+ "." + buffer.substring(buffer.length - rightDigitsOfDefault, buffer.length);
+ }
+ return sign + buffer;
+ },
+
+ /**
+ * Number-to-String formatting function. Prepends "plus" or "minus" depending
+ * on whether the value is positive or negative, respectively, after padding
+ * the value with zeroes on the left and right, the number of zeroes used dictated
+ * by the values 'leftDigits' and 'rightDigits'. 'value' can be an array;
+ * if the input is an array, each value in it is formatted separately, and
+ * an array with formatted values is returned.
+ *
+ * @param {int|int[]|float|float[]} value the number(s) to format
+ * @param {String} plus the prefix for positive numbers
+ * @param {String} minus the prefix for negative numbers
+ * @param {int} left number of digits to the left of the decimal point
+ * @param {int} right number of digits to the right of the decimal point
+ * @param {String} group string delimited for groups, such as the comma in "1,000"
+ *
+ * @returns {String or String[]}
+ *
+ * @see nfCoreScalar
+ */
+ nfCore: function(value, plus, minus, leftDigits, rightDigits, group) {
+ if (value instanceof Array) {
+ var arr = [];
+ for (var i = 0, len = value.length; i < len; i++) {
+ arr.push(CommonFunctions.nfCoreScalar(value[i], plus, minus, leftDigits, rightDigits, group));
+ }
+ return arr;
+ }
+ return CommonFunctions.nfCoreScalar(value, plus, minus, leftDigits, rightDigits, group);
+ },
+
+ /**
+ * Utility function for formatting numbers into strings. There are two versions, one for
+ * formatting floats and one for formatting ints. The values for the digits, left, and
+ * right parameters should always be positive integers.
+ * As shown in the above example, nf() is used to add zeros to the left and/or right
+ * of a number. This is typically for aligning a list of numbers. To remove digits from
+ * a floating-point number, use the int(), ceil(), floor(), or round() functions.
+ *
+ * @param {int|int[]|float|float[]} value the number(s) to format
+ * @param {int} left number of digits to the left of the decimal point
+ * @param {int} right number of digits to the right of the decimal point
+ *
+ * @returns {String or String[]}
+ *
+ * @see nfs
+ * @see nfp
+ * @see nfc
+ */
+ nf: function(value, leftDigits, rightDigits) {
+ return CommonFunctions.nfCore(value, "", "-", leftDigits, rightDigits);
+ },
+
+ /**
+ * Utility function for formatting numbers into strings. Similar to nf() but leaves a blank space in front
+ * of positive numbers so they align with negative numbers in spite of the minus symbol. There are two
+ * versions, one for formatting floats and one for formatting ints. The values for the digits, left,
+ * and right parameters should always be positive integers.
+ *
+ * @param {int|int[]|float|float[]} value the number(s) to format
+ * @param {int} left number of digits to the left of the decimal point
+ * @param {int} right number of digits to the right of the decimal point
+ *
+ * @returns {String or String[]}
+ *
+ * @see nf
+ * @see nfp
+ * @see nfc
+ */
+ nfs: function(value, leftDigits, rightDigits) {
+ return CommonFunctions.nfCore(value, " ", "-", leftDigits, rightDigits);
+ },
+
+ /**
+ * Utility function for formatting numbers into strings. Similar to nf() but puts a "+" in front of
+ * positive numbers and a "-" in front of negative numbers. There are two versions, one for formatting
+ * floats and one for formatting ints. The values for the digits, left, and right parameters should
+ * always be positive integers.
+ *
+ * @param {int|int[]|float|float[]} value the number(s) to format
+ * @param {int} left number of digits to the left of the decimal point
+ * @param {int} right number of digits to the right of the decimal point
+ *
+ * @returns {String or String[]}
+ *
+ * @see nfs
+ * @see nf
+ * @see nfc
+ */
+ nfp: function(value, leftDigits, rightDigits) {
+ return CommonFunctions.nfCore(value, "+", "-", leftDigits, rightDigits);
+ },
+
+ /**
+ * Utility function for formatting numbers into strings and placing appropriate commas to mark
+ * units of 1000. There are two versions, one for formatting ints and one for formatting an array
+ * of ints. The value for the digits parameter should always be a positive integer.
+ *
+ * @param {int|int[]|float|float[]} value the number(s) to format
+ * @param {int} left number of digits to the left of the decimal point
+ * @param {int} right number of digits to the right of the decimal point
+ *
+ * @returns {String or String[]}
+ *
+ * @see nf
+ * @see nfs
+ * @see nfp
+ */
+ nfc: function(value, rightDigits) {
+ return CommonFunctions.nfCore(value, "", "-", 0, rightDigits, ",");
+ },
+
+ // used to bind all common functions to "p"
+ withCommonFunctions: function withCommonFunctions(p) {
+ ["trim", "radians", "nf", "nfs", "nfp", "nfc"].forEach(function(f){
+ p[f] = CommonFunctions[f];
+ });
+ }
+ };
+
+ return CommonFunctions;
+}());
+
+},{}],25:[function(require,module,exports){
+/**
+ * Touch and Mouse event handling
+ */
+module.exports = function withTouch(p, curElement, attachEventHandler, document, PConstants, undef) {
+
+ /**
+ * Determine the location of the (mouse) pointer.
+ */
+ function calculateOffset(curElement, event) {
+ var element = curElement,
+ offsetX = 0,
+ offsetY = 0;
+
+ p.pmouseX = p.mouseX;
+ p.pmouseY = p.mouseY;
+
+ // Find element offset
+ if (element.offsetParent) {
+ do {
+ offsetX += element.offsetLeft;
+ offsetY += element.offsetTop;
+ } while (!!(element = element.offsetParent));
+ }
+
+ // Find Scroll offset
+ element = curElement;
+ do {
+ offsetX -= element.scrollLeft || 0;
+ offsetY -= element.scrollTop || 0;
+ } while (!!(element = element.parentNode));
+
+ // Get padding and border style widths for mouse offsets
+ var stylePaddingLeft, stylePaddingTop, styleBorderLeft, styleBorderTop;
+ if (document.defaultView && document.defaultView.getComputedStyle) {
+ stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(curElement, null).paddingLeft, 10) || 0;
+ stylePaddingTop = parseInt(document.defaultView.getComputedStyle(curElement, null).paddingTop, 10) || 0;
+ styleBorderLeft = parseInt(document.defaultView.getComputedStyle(curElement, null).borderLeftWidth, 10) || 0;
+ styleBorderTop = parseInt(document.defaultView.getComputedStyle(curElement, null).borderTopWidth, 10) || 0;
+ }
+
+ // Add padding and border style widths to offset
+ offsetX += stylePaddingLeft;
+ offsetY += stylePaddingTop;
+
+ offsetX += styleBorderLeft;
+ offsetY += styleBorderTop;
+
+ // Take into account any scrolling done
+ offsetX += window.pageXOffset;
+ offsetY += window.pageYOffset;
+
+ return {'X':offsetX,'Y':offsetY};
+ }
+
+ // simple relative position
+ function updateMousePosition(curElement, event) {
+ var offset = calculateOffset(curElement, event);
+ // Dropping support for IE clientX and clientY, switching to pageX and pageY
+ // so we don't have to calculate scroll offset.
+ // Removed in ticket #184. See rev: 2f106d1c7017fed92d045ba918db47d28e5c16f4
+ p.mouseX = event.pageX - offset.X;
+ p.mouseY = event.pageY - offset.Y;
+ }
+
+ /**
+ * Return a TouchEvent with canvas-specific x/y co-ordinates
+ */
+ function addTouchEventOffset(t) {
+ var offset = calculateOffset(t.changedTouches[0].target, t.changedTouches[0]),
+ i;
+
+ for (i = 0; i < t.touches.length; i++) {
+ var touch = t.touches[i];
+ touch.offsetX = touch.pageX - offset.X;
+ touch.offsetY = touch.pageY - offset.Y;
+ }
+ for (i = 0; i < t.targetTouches.length; i++) {
+ var targetTouch = t.targetTouches[i];
+ targetTouch.offsetX = targetTouch.pageX - offset.X;
+ targetTouch.offsetY = targetTouch.pageY - offset.Y;
+ }
+ for (i = 0; i < t.changedTouches.length; i++) {
+ var changedTouch = t.changedTouches[i];
+ changedTouch.offsetX = changedTouch.pageX - offset.X;
+ changedTouch.offsetY = changedTouch.pageY - offset.Y;
+ }
+
+ return t;
+ }
+
+ /**
+ * Touch event support.
+ */
+ attachEventHandler(curElement, "touchstart", function (t) {
+ // Removes unwanted behaviour of the canvas when touching canvas
+ curElement.setAttribute("style","-webkit-user-select: none");
+ curElement.setAttribute("onclick","void(0)");
+ curElement.setAttribute("style","-webkit-tap-highlight-color:rgba(0,0,0,0)");
+ // Loop though eventHandlers and remove mouse listeners
+ for (var i=0, ehl=eventHandlers.length; i<ehl; i++) {
+ var type = eventHandlers[i].type;
+ // Have this function remove itself from the eventHandlers list too
+ if (type === "mouseout" || type === "mousemove" ||
+ type === "mousedown" || type === "mouseup" ||
+ type === "DOMMouseScroll" || type === "mousewheel" || type === "touchstart") {
+ detachEventHandler(eventHandlers[i]);
+ }
+ }
+
+ // If there are any native touch events defined in the sketch, connect all of them
+ // Otherwise, connect all of the emulated mouse events
+ if (p.touchStart !== undef || p.touchMove !== undef ||
+ p.touchEnd !== undef || p.touchCancel !== undef) {
+ attachEventHandler(curElement, "touchstart", function(t) {
+ if (p.touchStart !== undef) {
+ t = addTouchEventOffset(t);
+ p.touchStart(t);
+ }
+ });
+
+ attachEventHandler(curElement, "touchmove", function(t) {
+ if (p.touchMove !== undef) {
+ t.preventDefault(); // Stop the viewport from scrolling
+ t = addTouchEventOffset(t);
+ p.touchMove(t);
+ }
+ });
+
+ attachEventHandler(curElement, "touchend", function(t) {
+ if (p.touchEnd !== undef) {
+ t = addTouchEventOffset(t);
+ p.touchEnd(t);
+ }
+ });
+
+ attachEventHandler(curElement, "touchcancel", function(t) {
+ if (p.touchCancel !== undef) {
+ t = addTouchEventOffset(t);
+ p.touchCancel(t);
+ }
+ });
+
+ } else {
+ // Emulated touch start/mouse down event
+ attachEventHandler(curElement, "touchstart", function(e) {
+ updateMousePosition(curElement, e.touches[0]);
+
+ p.__mousePressed = true;
+ p.mouseDragging = false;
+ p.mouseButton = PConstants.LEFT;
+
+ if (typeof p.mousePressed === "function") {
+ p.mousePressed();
+ }
+ });
+
+ // Emulated touch move/mouse move event
+ attachEventHandler(curElement, "touchmove", function(e) {
+ e.preventDefault();
+ updateMousePosition(curElement, e.touches[0]);
+
+ if (typeof p.mouseMoved === "function" && !p.__mousePressed) {
+ p.mouseMoved();
+ }
+ if (typeof p.mouseDragged === "function" && p.__mousePressed) {
+ p.mouseDragged();
+ p.mouseDragging = true;
+ }
+ });
+
+ // Emulated touch up/mouse up event
+ attachEventHandler(curElement, "touchend", function(e) {
+ p.__mousePressed = false;
+
+ if (typeof p.mouseClicked === "function" && !p.mouseDragging) {
+ p.mouseClicked();
+ }
+
+ if (typeof p.mouseReleased === "function") {
+ p.mouseReleased();
+ }
+ });
+ }
+
+ // Refire the touch start event we consumed in this function
+ curElement.dispatchEvent(t);
+ });
+
+ /**
+ * Context menu toggles. Most often you will not want the
+ * browser's context menu to show on a right click, but
+ * sometimes, you do, so we add two unofficial functions
+ * that can be used to trigger context menu behaviour.
+ */
+ (function() {
+ var enabled = true,
+ contextMenu = function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ };
+
+ p.disableContextMenu = function() {
+ if (!enabled) {
+ return;
+ }
+ attachEventHandler(curElement, 'contextmenu', contextMenu);
+ enabled = false;
+ };
+
+ p.enableContextMenu = function() {
+ if (enabled) {
+ return;
+ }
+ detachEventHandler({elem: curElement, type: 'contextmenu', fn: contextMenu});
+ enabled = true;
+ };
+ }());
+
+ /**
+ * Mouse moved or dragged
+ */
+ attachEventHandler(curElement, "mousemove", function(e) {
+ updateMousePosition(curElement, e);
+ if (typeof p.mouseMoved === "function" && !p.__mousePressed) {
+ p.mouseMoved();
+ }
+ if (typeof p.mouseDragged === "function" && p.__mousePressed) {
+ p.mouseDragged();
+ p.mouseDragging = true;
+ }
+ });
+
+ /**
+ * Unofficial mouse-out handling
+ */
+ attachEventHandler(curElement, "mouseout", function(e) {
+ if (typeof p.mouseOut === "function") {
+ p.mouseOut();
+ }
+ });
+
+ /**
+ * Mouse over
+ */
+ attachEventHandler(curElement, "mouseover", function(e) {
+ updateMousePosition(curElement, e);
+ if (typeof p.mouseOver === "function") {
+ p.mouseOver();
+ }
+ });
+
+ /**
+ * Disable browser's default handling for click-drag of a canvas.
+ */
+ curElement.onmousedown = function () {
+ // make sure focus happens, but nothing else
+ curElement.focus();
+ return false;
+ };
+
+ /**
+ * Mouse pressed or drag
+ */
+ attachEventHandler(curElement, "mousedown", function(e) {
+ p.__mousePressed = true;
+ p.mouseDragging = false;
+ switch (e.which) {
+ case 1:
+ p.mouseButton = PConstants.LEFT;
+ break;
+ case 2:
+ p.mouseButton = PConstants.CENTER;
+ break;
+ case 3:
+ p.mouseButton = PConstants.RIGHT;
+ break;
+ }
+
+ if (typeof p.mousePressed === "function") {
+ p.mousePressed();
+ }
+ });
+
+ /**
+ * Mouse clicked or released
+ */
+ attachEventHandler(curElement, "mouseup", function(e) {
+ p.__mousePressed = false;
+
+ if (typeof p.mouseClicked === "function" && !p.mouseDragging) {
+ p.mouseClicked();
+ }
+
+ if (typeof p.mouseReleased === "function") {
+ p.mouseReleased();
+ }
+ });
+
+ /**
+ * Unofficial scroll wheel handling.
+ */
+ var mouseWheelHandler = function(e) {
+ var delta = 0;
+
+ if (e.wheelDelta) {
+ delta = e.wheelDelta / 120;
+ if (window.opera) {
+ delta = -delta;
+ }
+ } else if (e.detail) {
+ delta = -e.detail / 3;
+ }
+
+ p.mouseScroll = delta;
+
+ if (delta && typeof p.mouseScrolled === 'function') {
+ p.mouseScrolled();
+ }
+ };
+
+ // Support Gecko and non-Gecko scroll events
+ attachEventHandler(document, 'DOMMouseScroll', mouseWheelHandler);
+ attachEventHandler(document, 'mousewheel', mouseWheelHandler);
+
+};
+
+},{}],26:[function(require,module,exports){
+/**
+ * The parser for turning Processing syntax into Pjs JavaScript.
+ * This code is not trivial; unless you know what you're doing,
+ * you shouldn't be changing things in here =)
+ */
+module.exports = function setupParser(Processing, options) {
+
+ var defaultScope = options.defaultScope,
+ PConstants = defaultScope.PConstants,
+ aFunctions = options.aFunctions,
+ Browser = options.Browser,
+ document = Browser.document,
+ undef;
+
+ // Processing global methods and constants for the parser
+ function getGlobalMembers() {
+ // The names array contains the names of everything that is inside "p."
+ // When something new is added to "p." it must also be added to this list.
+ var names = [ /* this code is generated by jsglobals.js */
+ "abs", "acos", "alpha", "ambient", "ambientLight", "append", "applyMatrix",
+ "arc", "arrayCopy", "asin", "atan", "atan2", "background", "beginCamera",
+ "beginDraw", "beginShape", "bezier", "bezierDetail", "bezierPoint",
+ "bezierTangent", "bezierVertex", "binary", "blend", "blendColor",
+ "blit_resize", "blue", "box", "breakShape", "brightness",
+ "camera", "ceil", "Character", "color", "colorMode",
+ "concat", "constrain", "copy", "cos", "createFont",
+ "createGraphics", "createImage", "cursor", "curve", "curveDetail",
+ "curvePoint", "curveTangent", "curveTightness", "curveVertex", "day",
+ "degrees", "directionalLight", "disableContextMenu",
+ "dist", "draw", "ellipse", "ellipseMode", "emissive", "enableContextMenu",
+ "endCamera", "endDraw", "endShape", "exit", "exp", "expand", "externals",
+ "fill", "filter", "floor", "focused", "frameCount", "frameRate", "frustum",
+ "get", "glyphLook", "glyphTable", "green", "height", "hex", "hint", "hour",
+ "hue", "image", "imageMode", "intersect", "join", "key",
+ "keyCode", "keyPressed", "keyReleased", "keyTyped", "lerp", "lerpColor",
+ "lightFalloff", "lights", "lightSpecular", "line", "link", "loadBytes",
+ "loadFont", "loadGlyphs", "loadImage", "loadPixels", "loadShape", "loadXML",
+ "loadStrings", "log", "loop", "mag", "map", "match", "matchAll", "max",
+ "millis", "min", "minute", "mix", "modelX", "modelY", "modelZ", "modes",
+ "month", "mouseButton", "mouseClicked", "mouseDragged", "mouseMoved",
+ "mouseOut", "mouseOver", "mousePressed", "mouseReleased", "mouseScroll",
+ "mouseScrolled", "mouseX", "mouseY", "name", "nf", "nfc", "nfp", "nfs",
+ "noCursor", "noFill", "noise", "noiseDetail", "noiseSeed", "noLights",
+ "noLoop", "norm", "normal", "noSmooth", "noStroke", "noTint", "ortho",
+ "param", "parseBoolean", "parseByte", "parseChar", "parseFloat",
+ "parseInt", "parseXML", "peg", "perspective", "PImage", "pixels",
+ "PMatrix2D", "PMatrix3D", "PMatrixStack", "pmouseX", "pmouseY", "point",
+ "pointLight", "popMatrix", "popStyle", "pow", "print", "printCamera",
+ "println", "printMatrix", "printProjection", "PShape", "PShapeSVG",
+ "pushMatrix", "pushStyle", "quad", "radians", "random", "randomGaussian",
+ "randomSeed", "rect", "rectMode", "red", "redraw", "requestImage",
+ "resetMatrix", "reverse", "rotate", "rotateX", "rotateY", "rotateZ",
+ "round", "saturation", "save", "saveFrame", "saveStrings", "scale",
+ "screenX", "screenY", "screenZ", "second", "set", "setup", "shape",
+ "shapeMode", "shared", "shearX", "shearY", "shininess", "shorten", "sin", "size", "smooth",
+ "sort", "specular", "sphere", "sphereDetail", "splice", "split",
+ "splitTokens", "spotLight", "sq", "sqrt", "status", "str", "stroke",
+ "strokeCap", "strokeJoin", "strokeWeight", "subset", "tan", "text",
+ "textAlign", "textAscent", "textDescent", "textFont", "textLeading",
+ "textMode", "textSize", "texture", "textureMode", "textWidth", "tint", "toImageData",
+ "touchCancel", "touchEnd", "touchMove", "touchStart", "translate", "transform",
+ "triangle", "trim", "unbinary", "unhex", "updatePixels", "use3DContext",
+ "vertex", "width", "XMLElement", "XML", "year", "__contains", "__equals",
+ "__equalsIgnoreCase", "__frameRate", "__hashCode", "__int_cast",
+ "__instanceof", "__keyPressed", "__mousePressed", "__printStackTrace",
+ "__replace", "__replaceAll", "__replaceFirst", "__toCharArray", "__split",
+ "__codePointAt", "__startsWith", "__endsWith", "__matches"];
+
+ // custom functions and properties are added here
+ if(aFunctions) {
+ Object.keys(aFunctions).forEach(function(name) {
+ names.push(name);
+ });
+ }
+
+ // custom libraries that were attached to Processing
+ var members = {};
+ var i, l;
+ for (i = 0, l = names.length; i < l ; ++i) {
+ members[names[i]] = null;
+ }
+ for (var lib in Processing.lib) {
+ if (Processing.lib.hasOwnProperty(lib)) {
+ if (Processing.lib[lib].exports) {
+ var exportedNames = Processing.lib[lib].exports;
+ for (i = 0, l = exportedNames.length; i < l; ++i) {
+ members[exportedNames[i]] = null;
+ }
+ }
+ }
+ }
+ return members;
+ }
+
+ /*
+
+ Parser converts Java-like syntax into JavaScript.
+ Creates an Abstract Syntax Tree -- "Light AST" from the Java-like code.
+
+ It is an object tree. The root object is created from the AstRoot class, which contains statements.
+
+ A statement object can be of type: AstForStatement, AstCatchStatement, AstPrefixStatement, AstMethod, AstClass,
+ AstInterface, AstFunction, AstStatementBlock and AstLabel.
+
+ AstPrefixStatement can be a statement of type: if, switch, while, with, do, else, finally, return, throw, try, break, and continue.
+
+ These object's toString function returns the JavaScript code for the statement.
+
+ Any processing calls need "processing." prepended to them.
+
+ Similarly, calls from inside classes need "$this_1.", prepended to them,
+ with 1 being the depth level for inner classes.
+ This includes members passed down from inheritance.
+
+ The resulting code is then eval'd and run.
+
+ */
+
+ function parseProcessing(code) {
+ var globalMembers = getGlobalMembers();
+
+ // masks parentheses, brackets and braces with '"A5"'
+ // where A is the bracket type, and 5 is the index in an array containing all brackets split into atoms
+ // 'while(true){}' -> 'while"B1""A2"'
+ // parentheses() = B, brackets[] = C and braces{} = A
+ function splitToAtoms(code) {
+ var atoms = [];
+ var items = code.split(/([\{\[\(\)\]\}])/);
+ var result = items[0];
+
+ var stack = [];
+ for(var i=1; i < items.length; i += 2) {
+ var item = items[i];
+ if(item === '[' || item === '{' || item === '(') {
+ stack.push(result); result = item;
+ } else if(item === ']' || item === '}' || item === ')') {
+ var kind = item === '}' ? 'A' : item === ')' ? 'B' : 'C';
+ var index = atoms.length; atoms.push(result + item);
+ result = stack.pop() + '"' + kind + (index + 1) + '"';
+ }
+ result += items[i + 1];
+ }
+ atoms.unshift(result);
+ return atoms;
+ }
+
+ // replaces strings and regexs keyed by index with an array of strings
+ function injectStrings(code, strings) {
+ return code.replace(/'(\d+)'/g, function(all, index) {
+ var val = strings[index];
+ if(val.charAt(0) === "/") {
+ return val;
+ }
+ return (/^'((?:[^'\\\n])|(?:\\.[0-9A-Fa-f]*))'$/).test(val) ? "(new $p.Character(" + val + "))" : val;
+ });
+ }
+
+ // trims off leading and trailing spaces
+ // returns an object. object.left, object.middle, object.right, object.untrim
+ function trimSpaces(string) {
+ var m1 = /^\s*/.exec(string), result;
+ if(m1[0].length === string.length) {
+ result = {left: m1[0], middle: "", right: ""};
+ } else {
+ var m2 = /\s*$/.exec(string);
+ result = {left: m1[0], middle: string.substring(m1[0].length, m2.index), right: m2[0]};
+ }
+ result.untrim = function(t) { return this.left + t + this.right; };
+ return result;
+ }
+
+ // simple trim of leading and trailing spaces
+ function trim(string) {
+ return string.replace(/^\s+/,'').replace(/\s+$/,'');
+ }
+
+ function appendToLookupTable(table, array) {
+ for(var i=0,l=array.length;i<l;++i) {
+ table[array[i]] = null;
+ }
+ return table;
+ }
+
+ function isLookupTableEmpty(table) {
+ for(var i in table) {
+ if(table.hasOwnProperty(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ function getAtomIndex(templ) { return templ.substring(2, templ.length - 1); }
+
+ // remove carriage returns "\r"
+ var codeWoExtraCr = code.replace(/\r\n?|\n\r/g, "\n");
+
+ // masks strings and regexs with "'5'", where 5 is the index in an array containing all strings and regexs
+ // also removes all comments
+ var strings = [];
+ var codeWoStrings = codeWoExtraCr.replace(/("(?:[^"\\\n]|\\.)*")|('(?:[^'\\\n]|\\.)*')|(([\[\(=|&!\^:?]\s*)(\/(?![*\/])(?:[^\/\\\n]|\\.)*\/[gim]*)\b)|(\/\/[^\n]*\n)|(\/\*(?:(?!\*\/)(?:.|\n))*\*\/)/g,
+ function(all, quoted, aposed, regexCtx, prefix, regex, singleComment, comment) {
+ var index;
+ if(quoted || aposed) { // replace strings
+ index = strings.length; strings.push(all);
+ return "'" + index + "'";
+ }
+ if(regexCtx) { // replace RegExps
+ index = strings.length; strings.push(regex);
+ return prefix + "'" + index + "'";
+ }
+ // kill comments
+ return comment !== "" ? " " : "\n";
+ });
+
+ // protect character codes from namespace collision
+ codeWoStrings = codeWoStrings.replace(/__x([0-9A-F]{4})/g, function(all, hexCode) {
+ // $ = __x0024
+ // _ = __x005F
+ // this protects existing character codes from conversion
+ // __x0024 = __x005F_x0024
+ return "__x005F_x" + hexCode;
+ });
+
+ // convert dollar sign to character code
+ codeWoStrings = codeWoStrings.replace(/\$/g, "__x0024");
+
+ // Remove newlines after return statements
+ codeWoStrings = codeWoStrings.replace(/return\s*[\n\r]+/g, "return ");
+
+ // removes generics
+ var genericsWereRemoved;
+ var codeWoGenerics = codeWoStrings;
+ var replaceFunc = function(all, before, types, after) {
+ if(!!before || !!after) {
+ return all;
+ }
+ genericsWereRemoved = true;
+ return "";
+ };
+
+ do {
+ genericsWereRemoved = false;
+ codeWoGenerics = codeWoGenerics.replace(/([<]?)<\s*((?:\?|[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)(?:\[\])*(?:\s+(?:extends|super)\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)?(?:\s*,\s*(?:\?|[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)(?:\[\])*(?:\s+(?:extends|super)\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)?)*)\s*>([=]?)/g, replaceFunc);
+ } while (genericsWereRemoved);
+
+ var atoms = splitToAtoms(codeWoGenerics);
+ var replaceContext;
+ var declaredClasses = {}, currentClassId, classIdSeed = 0;
+
+ function addAtom(text, type) {
+ var lastIndex = atoms.length;
+ atoms.push(text);
+ return '"' + type + lastIndex + '"';
+ }
+
+ function generateClassId() {
+ return "class" + (++classIdSeed);
+ }
+
+ function appendClass(class_, classId, scopeId) {
+ class_.classId = classId;
+ class_.scopeId = scopeId;
+ declaredClasses[classId] = class_;
+ }
+
+ // functions defined below
+ var transformClassBody, transformInterfaceBody, transformStatementsBlock, transformStatements, transformMain, transformExpression;
+
+ var classesRegex = /\b((?:(?:public|private|final|protected|static|abstract)\s+)*)(class|interface)\s+([A-Za-z_$][\w$]*\b)(\s+extends\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*\b)*)?(\s+implements\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*\b)*)?\s*("A\d+")/g;
+ var methodsRegex = /\b((?:(?:public|private|final|protected|static|abstract|synchronized)\s+)*)((?!(?:else|new|return|throw|function|public|private|protected)\b)[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*"C\d+")*)\s*([A-Za-z_$][\w$]*\b)\s*("B\d+")(\s*throws\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)*)?\s*("A\d+"|;)/g;
+ var fieldTest = /^((?:(?:public|private|final|protected|static)\s+)*)((?!(?:else|new|return|throw)\b)[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*"C\d+")*)\s*([A-Za-z_$][\w$]*\b)\s*(?:"C\d+"\s*)*([=,]|$)/;
+ var cstrsRegex = /\b((?:(?:public|private|final|protected|static|abstract)\s+)*)((?!(?:new|return|throw)\b)[A-Za-z_$][\w$]*\b)\s*("B\d+")(\s*throws\s+[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*,\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)*)?\s*("A\d+")/g;
+ var attrAndTypeRegex = /^((?:(?:public|private|final|protected|static)\s+)*)((?!(?:new|return|throw)\b)[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*(?:\s*"C\d+")*)\s*/;
+ var functionsRegex = /\bfunction(?:\s+([A-Za-z_$][\w$]*))?\s*("B\d+")\s*("A\d+")/g;
+
+ // This converts classes, methods and functions into atoms, and adds them to the atoms array.
+ // classes = E, methods = D and functions = H
+ function extractClassesAndMethods(code) {
+ var s = code;
+ s = s.replace(classesRegex, function(all) {
+ return addAtom(all, 'E');
+ });
+ s = s.replace(methodsRegex, function(all) {
+ return addAtom(all, 'D');
+ });
+ s = s.replace(functionsRegex, function(all) {
+ return addAtom(all, 'H');
+ });
+ return s;
+ }
+
+ // This converts constructors into atoms, and adds them to the atoms array.
+ // constructors = G
+ function extractConstructors(code, className) {
+ var result = code.replace(cstrsRegex, function(all, attr, name, params, throws_, body) {
+ if(name !== className) {
+ return all;
+ }
+ return addAtom(all, 'G');
+ });
+ return result;
+ }
+
+ // AstParam contains the name of a parameter inside a function declaration
+ function AstParam(name) {
+ this.name = name;
+ }
+ AstParam.prototype.toString = function() {
+ return this.name;
+ };
+ // AstParams contains an array of AstParam objects
+ function AstParams(params, methodArgsParam) {
+ this.params = params;
+ this.methodArgsParam = methodArgsParam;
+ }
+ AstParams.prototype.getNames = function() {
+ var names = [];
+ for(var i=0,l=this.params.length;i<l;++i) {
+ names.push(this.params[i].name);
+ }
+ return names;
+ };
+ AstParams.prototype.prependMethodArgs = function(body) {
+ if (!this.methodArgsParam) {
+ return body;
+ }
+ return "{\nvar " + this.methodArgsParam.name +
+ " = Array.prototype.slice.call(arguments, " +
+ this.params.length + ");\n" + body.substring(1);
+ };
+ AstParams.prototype.toString = function() {
+ if(this.params.length === 0) {
+ return "()";
+ }
+ var result = "(";
+ for(var i=0,l=this.params.length;i<l;++i) {
+ result += this.params[i] + ", ";
+ }
+ return result.substring(0, result.length - 2) + ")";
+ };
+
+ function transformParams(params) {
+ var paramsWoPars = trim(params.substring(1, params.length - 1));
+ var result = [], methodArgsParam = null;
+ if(paramsWoPars !== "") {
+ var paramList = paramsWoPars.split(",");
+ for(var i=0; i < paramList.length; ++i) {
+ var param = /\b([A-Za-z_$][\w$]*\b)(\s*"[ABC][\d]*")*\s*$/.exec(paramList[i]);
+ if (i === paramList.length - 1 && paramList[i].indexOf('...') >= 0) {
+ methodArgsParam = new AstParam(param[1]);
+ break;
+ }
+ result.push(new AstParam(param[1]));
+ }
+ }
+ return new AstParams(result, methodArgsParam);
+ }
+
+ function preExpressionTransform(expr) {
+ var s = expr;
+ // new type[] {...} --> {...}
+ s = s.replace(/\bnew\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)(?:\s*"C\d+")+\s*("A\d+")/g, function(all, type, init) {
+ return init;
+ });
+ // new Runnable() {...} --> "F???"
+ s = s.replace(/\bnew\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)(?:\s*"B\d+")\s*("A\d+")/g, function(all, type, init) {
+ return addAtom(all, 'F');
+ });
+ // function(...) { } --> "H???"
+ s = s.replace(functionsRegex, function(all) {
+ return addAtom(all, 'H');
+ });
+ // new type[?] --> createJavaArray('type', [?])
+ s = s.replace(/\bnew\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)\s*("C\d+"(?:\s*"C\d+")*)/g, function(all, type, index) {
+ var args = index.replace(/"C(\d+)"/g, function(all, j) { return atoms[j]; })
+ .replace(/\[\s*\]/g, "[null]").replace(/\s*\]\s*\[\s*/g, ", ");
+ var arrayInitializer = "{" + args.substring(1, args.length - 1) + "}";
+ var createArrayArgs = "('" + type + "', " + addAtom(arrayInitializer, 'A') + ")";
+ return '$p.createJavaArray' + addAtom(createArrayArgs, 'B');
+ });
+ // .length() --> .length
+ s = s.replace(/(\.\s*length)\s*"B\d+"/g, "$1");
+ // #000000 --> 0x000000
+ s = s.replace(/#([0-9A-Fa-f]{6})\b/g, function(all, digits) {
+ return "0xFF" + digits;
+ });
+ // delete (type)???, except (int)???
+ s = s.replace(/"B(\d+)"(\s*(?:[\w$']|"B))/g, function(all, index, next) {
+ var atom = atoms[index];
+ if(!/^\(\s*[A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*\s*(?:"C\d+"\s*)*\)$/.test(atom)) {
+ return all;
+ }
+ if(/^\(\s*int\s*\)$/.test(atom)) {
+ return "(int)" + next;
+ }
+ var indexParts = atom.split(/"C(\d+)"/g);
+ if(indexParts.length > 1) {
+ // even items contains atom numbers, can check only first
+ if(! /^\[\s*\]$/.test(atoms[indexParts[1]])) {
+ return all; // fallback - not a cast
+ }
+ }
+ return "" + next;
+ });
+ // (int)??? -> __int_cast(???)
+ s = s.replace(/\(int\)([^,\]\)\}\?\:\*\+\-\/\^\|\%\&\~<\>\=]+)/g, function(all, arg) {
+ var trimmed = trimSpaces(arg);
+ return trimmed.untrim("__int_cast(" + trimmed.middle + ")");
+ });
+ // super() -> $superCstr(), super. -> $super.;
+ s = s.replace(/\bsuper(\s*"B\d+")/g, "$$superCstr$1").replace(/\bsuper(\s*\.)/g, "$$super$1");
+ // 000.43->0.43 and 0010f->10, but not 0010
+ s = s.replace(/\b0+((\d*)(?:\.[\d*])?(?:[eE][\-\+]?\d+)?[fF]?)\b/, function(all, numberWo0, intPart) {
+ if( numberWo0 === intPart) {
+ return all;
+ }
+ return intPart === "" ? "0" + numberWo0 : numberWo0;
+ });
+ // 3.0f -> 3.0
+ s = s.replace(/\b(\.?\d+\.?)[fF]\b/g, "$1");
+ // Weird (?) parsing errors with %
+ s = s.replace(/([^\s])%([^=\s])/g, "$1 % $2");
+ // Since frameRate() and frameRate are different things,
+ // we need to differentiate them somehow. So when we parse
+ // the Processing.js source, replace frameRate so it isn't
+ // confused with frameRate(), as well as keyPressed and mousePressed
+ s = s.replace(/\b(frameRate|keyPressed|mousePressed)\b(?!\s*"B)/g, "__$1");
+ // "boolean", "byte", "int", etc. => "parseBoolean", "parseByte", "parseInt", etc.
+ s = s.replace(/\b(boolean|byte|char|float|int)\s*"B/g, function(all, name) {
+ return "parse" + name.substring(0, 1).toUpperCase() + name.substring(1) + "\"B";
+ });
+ // "pixels" replacements:
+ // pixels[i] = c => pixels.setPixel(i,c) | pixels[i] => pixels.getPixel(i)
+ // pixels.length => pixels.getLength()
+ // pixels = ar => pixels.set(ar) | pixels => pixels.toArray()
+ s = s.replace(/\bpixels\b\s*(("C(\d+)")|\.length)?(\s*=(?!=)([^,\]\)\}]+))?/g,
+ function(all, indexOrLength, index, atomIndex, equalsPart, rightSide) {
+ if(index) {
+ var atom = atoms[atomIndex];
+ if(equalsPart) {
+ return "pixels.setPixel" + addAtom("(" +atom.substring(1, atom.length - 1) +
+ "," + rightSide + ")", 'B');
+ }
+ return "pixels.getPixel" + addAtom("(" + atom.substring(1, atom.length - 1) +
+ ")", 'B');
+ }
+ if(indexOrLength) {
+ // length
+ return "pixels.getLength" + addAtom("()", 'B');
+ }
+ if(equalsPart) {
+ return "pixels.set" + addAtom("(" + rightSide + ")", 'B');
+ }
+ return "pixels.toArray" + addAtom("()", 'B');
+ });
+ // Java method replacements for: replace, replaceAll, replaceFirst, equals, hashCode, etc.
+ // xxx.replace(yyy) -> __replace(xxx, yyy)
+ // "xx".replace(yyy) -> __replace("xx", yyy)
+ var repeatJavaReplacement;
+ function replacePrototypeMethods(all, subject, method, atomIndex) {
+ var atom = atoms[atomIndex];
+ repeatJavaReplacement = true;
+ var trimmed = trimSpaces(atom.substring(1, atom.length - 1));
+ return "__" + method + ( trimmed.middle === "" ? addAtom("(" + subject.replace(/\.\s*$/, "") + ")", 'B') :
+ addAtom("(" + subject.replace(/\.\s*$/, "") + "," + trimmed.middle + ")", 'B') );
+ }
+ do {
+ repeatJavaReplacement = false;
+ s = s.replace(/((?:'\d+'|\b[A-Za-z_$][\w$]*\s*(?:"[BC]\d+")*)\s*\.\s*(?:[A-Za-z_$][\w$]*\s*(?:"[BC]\d+"\s*)*\.\s*)*)(replace|replaceAll|replaceFirst|contains|equals|equalsIgnoreCase|hashCode|toCharArray|printStackTrace|split|startsWith|endsWith|codePointAt|matches)\s*"B(\d+)"/g,
+ replacePrototypeMethods);
+ } while (repeatJavaReplacement);
+ // xxx instanceof yyy -> __instanceof(xxx, yyy)
+ function replaceInstanceof(all, subject, type) {
+ repeatJavaReplacement = true;
+ return "__instanceof" + addAtom("(" + subject + ", " + type + ")", 'B');
+ }
+ do {
+ repeatJavaReplacement = false;
+ s = s.replace(/((?:'\d+'|\b[A-Za-z_$][\w$]*\s*(?:"[BC]\d+")*)\s*(?:\.\s*[A-Za-z_$][\w$]*\s*(?:"[BC]\d+"\s*)*)*)instanceof\s+([A-Za-z_$][\w$]*\s*(?:\.\s*[A-Za-z_$][\w$]*)*)/g,
+ replaceInstanceof);
+ } while (repeatJavaReplacement);
+ // this() -> $constr()
+ s = s.replace(/\bthis(\s*"B\d+")/g, "$$constr$1");
+
+ return s;
+ }
+
+ function AstInlineClass(baseInterfaceName, body) {
+ this.baseInterfaceName = baseInterfaceName;
+ this.body = body;
+ body.owner = this;
+ }
+ AstInlineClass.prototype.toString = function() {
+ return "new (" + this.body + ")";
+ };
+
+ function transformInlineClass(class_) {
+ var m = new RegExp(/\bnew\s*([A-Za-z_$][\w$]*\s*(?:\.\s*[A-Za-z_$][\w$]*)*)\s*"B\d+"\s*"A(\d+)"/).exec(class_);
+ var oldClassId = currentClassId, newClassId = generateClassId();
+ currentClassId = newClassId;
+ var uniqueClassName = m[1] + "$" + newClassId;
+ var inlineClass = new AstInlineClass(uniqueClassName,
+ transformClassBody(atoms[m[2]], uniqueClassName, "", "implements " + m[1]));
+ appendClass(inlineClass, newClassId, oldClassId);
+ currentClassId = oldClassId;
+ return inlineClass;
+ }
+
+ function AstFunction(name, params, body) {
+ this.name = name;
+ this.params = params;
+ this.body = body;
+ }
+ AstFunction.prototype.toString = function() {
+ var oldContext = replaceContext;
+ // saving "this." and parameters
+ var names = appendToLookupTable({"this":null}, this.params.getNames());
+ replaceContext = function (subject) {
+ return names.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
+ };
+ var result = "function";
+ if(this.name) {
+ result += " " + this.name;
+ }
+ var body = this.params.prependMethodArgs(this.body.toString());
+ result += this.params + " " + body;
+ replaceContext = oldContext;
+ return result;
+ };
+
+ function transformFunction(class_) {
+ var m = new RegExp(/\b([A-Za-z_$][\w$]*)\s*"B(\d+)"\s*"A(\d+)"/).exec(class_);
+ return new AstFunction( m[1] !== "function" ? m[1] : null,
+ transformParams(atoms[m[2]]), transformStatementsBlock(atoms[m[3]]));
+ }
+
+ function AstInlineObject(members) {
+ this.members = members;
+ }
+ AstInlineObject.prototype.toString = function() {
+ var oldContext = replaceContext;
+ replaceContext = function (subject) {
+ return subject.name === "this" ? "this" : oldContext(subject); // saving "this."
+ };
+ var result = "";
+ for(var i=0,l=this.members.length;i<l;++i) {
+ if(this.members[i].label) {
+ result += this.members[i].label + ": ";
+ }
+ result += this.members[i].value.toString() + ", ";
+ }
+ replaceContext = oldContext;
+ return result.substring(0, result.length - 2);
+ };
+
+ function transformInlineObject(obj) {
+ var members = obj.split(',');
+ for(var i=0; i < members.length; ++i) {
+ var label = members[i].indexOf(':');
+ if(label < 0) {
+ members[i] = { value: transformExpression(members[i]) };
+ } else {
+ members[i] = { label: trim(members[i].substring(0, label)),
+ value: transformExpression( trim(members[i].substring(label + 1)) ) };
+ }
+ }
+ return new AstInlineObject(members);
+ }
+
+ function expandExpression(expr) {
+ if(expr.charAt(0) === '(' || expr.charAt(0) === '[') {
+ return expr.charAt(0) + expandExpression(expr.substring(1, expr.length - 1)) + expr.charAt(expr.length - 1);
+ }
+ if(expr.charAt(0) === '{') {
+ if(/^\{\s*(?:[A-Za-z_$][\w$]*|'\d+')\s*:/.test(expr)) {
+ return "{" + addAtom(expr.substring(1, expr.length - 1), 'I') + "}";
+ }
+ return "[" + expandExpression(expr.substring(1, expr.length - 1)) + "]";
+ }
+ var trimmed = trimSpaces(expr);
+ var result = preExpressionTransform(trimmed.middle);
+ result = result.replace(/"[ABC](\d+)"/g, function(all, index) {
+ return expandExpression(atoms[index]);
+ });
+ return trimmed.untrim(result);
+ }
+
+ function replaceContextInVars(expr) {
+ return expr.replace(/(\.\s*)?((?:\b[A-Za-z_]|\$)[\w$]*)(\s*\.\s*([A-Za-z_$][\w$]*)(\s*\()?)?/g,
+ function(all, memberAccessSign, identifier, suffix, subMember, callSign) {
+ if(memberAccessSign) {
+ return all;
+ }
+ var subject = { name: identifier, member: subMember, callSign: !!callSign };
+ return replaceContext(subject) + (suffix === undef ? "" : suffix);
+ });
+ }
+
+ function AstExpression(expr, transforms) {
+ this.expr = expr;
+ this.transforms = transforms;
+ }
+ AstExpression.prototype.toString = function() {
+ var transforms = this.transforms;
+ var expr = replaceContextInVars(this.expr);
+ return expr.replace(/"!(\d+)"/g, function(all, index) {
+ return transforms[index].toString();
+ });
+ };
+
+ transformExpression = function(expr) {
+ var transforms = [];
+ var s = expandExpression(expr);
+ s = s.replace(/"H(\d+)"/g, function(all, index) {
+ transforms.push(transformFunction(atoms[index]));
+ return '"!' + (transforms.length - 1) + '"';
+ });
+ s = s.replace(/"F(\d+)"/g, function(all, index) {
+ transforms.push(transformInlineClass(atoms[index]));
+ return '"!' + (transforms.length - 1) + '"';
+ });
+ s = s.replace(/"I(\d+)"/g, function(all, index) {
+ transforms.push(transformInlineObject(atoms[index]));
+ return '"!' + (transforms.length - 1) + '"';
+ });
+
+ return new AstExpression(s, transforms);
+ };
+
+ function AstVarDefinition(name, value, isDefault) {
+ this.name = name;
+ this.value = value;
+ this.isDefault = isDefault;
+ }
+ AstVarDefinition.prototype.toString = function() {
+ return this.name + ' = ' + this.value;
+ };
+
+ function transformVarDefinition(def, defaultTypeValue) {
+ var eqIndex = def.indexOf("=");
+ var name, value, isDefault;
+ if(eqIndex < 0) {
+ name = def;
+ value = defaultTypeValue;
+ isDefault = true;
+ } else {
+ name = def.substring(0, eqIndex);
+ value = transformExpression(def.substring(eqIndex + 1));
+ isDefault = false;
+ }
+ return new AstVarDefinition( trim(name.replace(/(\s*"C\d+")+/g, "")),
+ value, isDefault);
+ }
+
+ function getDefaultValueForType(type) {
+ if(type === "int" || type === "float") {
+ return "0";
+ }
+ if(type === "boolean") {
+ return "false";
+ }
+ if(type === "color") {
+ return "0x00000000";
+ }
+ return "null";
+ }
+
+ function AstVar(definitions, varType) {
+ this.definitions = definitions;
+ this.varType = varType;
+ }
+ AstVar.prototype.getNames = function() {
+ var names = [];
+ for(var i=0,l=this.definitions.length;i<l;++i) {
+ names.push(this.definitions[i].name);
+ }
+ return names;
+ };
+ AstVar.prototype.toString = function() {
+ return "var " + this.definitions.join(",");
+ };
+ function AstStatement(expression) {
+ this.expression = expression;
+ }
+ AstStatement.prototype.toString = function() {
+ return this.expression.toString();
+ };
+
+ function transformStatement(statement) {
+ if(fieldTest.test(statement)) {
+ var attrAndType = attrAndTypeRegex.exec(statement);
+ var definitions = statement.substring(attrAndType[0].length).split(",");
+ var defaultTypeValue = getDefaultValueForType(attrAndType[2]);
+ for(var i=0; i < definitions.length; ++i) {
+ definitions[i] = transformVarDefinition(definitions[i], defaultTypeValue);
+ }
+ return new AstVar(definitions, attrAndType[2]);
+ }
+ return new AstStatement(transformExpression(statement));
+ }
+
+ function AstForExpression(initStatement, condition, step) {
+ this.initStatement = initStatement;
+ this.condition = condition;
+ this.step = step;
+ }
+ AstForExpression.prototype.toString = function() {
+ return "(" + this.initStatement + "; " + this.condition + "; " + this.step + ")";
+ };
+
+ function AstForInExpression(initStatement, container) {
+ this.initStatement = initStatement;
+ this.container = container;
+ }
+ AstForInExpression.prototype.toString = function() {
+ var init = this.initStatement.toString();
+ if(init.indexOf("=") >= 0) { // can be without var declaration
+ init = init.substring(0, init.indexOf("="));
+ }
+ return "(" + init + " in " + this.container + ")";
+ };
+
+ function AstForEachExpression(initStatement, container) {
+ this.initStatement = initStatement;
+ this.container = container;
+ }
+ AstForEachExpression.iteratorId = 0;
+ AstForEachExpression.prototype.toString = function() {
+ var init = this.initStatement.toString();
+ var iterator = "$it" + (AstForEachExpression.iteratorId++);
+ var variableName = init.replace(/^\s*var\s*/, "").split("=")[0];
+ var initIteratorAndVariable = "var " + iterator + " = new $p.ObjectIterator(" + this.container + "), " +
+ variableName + " = void(0)";
+ var nextIterationCondition = iterator + ".hasNext() && ((" +
+ variableName + " = " + iterator + ".next()) || true)";
+ return "(" + initIteratorAndVariable + "; " + nextIterationCondition + ";)";
+ };
+
+ function transformForExpression(expr) {
+ var content;
+ if (/\bin\b/.test(expr)) {
+ content = expr.substring(1, expr.length - 1).split(/\bin\b/g);
+ return new AstForInExpression( transformStatement(trim(content[0])),
+ transformExpression(content[1]));
+ }
+ if (expr.indexOf(":") >= 0 && expr.indexOf(";") < 0) {
+ content = expr.substring(1, expr.length - 1).split(":");
+ return new AstForEachExpression( transformStatement(trim(content[0])),
+ transformExpression(content[1]));
+ }
+ content = expr.substring(1, expr.length - 1).split(";");
+ return new AstForExpression( transformStatement(trim(content[0])),
+ transformExpression(content[1]), transformExpression(content[2]));
+ }
+
+ function sortByWeight(array) {
+ array.sort(function (a,b) {
+ return b.weight - a.weight;
+ });
+ }
+
+ function AstInnerInterface(name, body, isStatic) {
+ this.name = name;
+ this.body = body;
+ this.isStatic = isStatic;
+ body.owner = this;
+ }
+ AstInnerInterface.prototype.toString = function() {
+ return "" + this.body;
+ };
+ function AstInnerClass(name, body, isStatic) {
+ this.name = name;
+ this.body = body;
+ this.isStatic = isStatic;
+ body.owner = this;
+ }
+ AstInnerClass.prototype.toString = function() {
+ return "" + this.body;
+ };
+
+ function transformInnerClass(class_) {
+ var m = classesRegex.exec(class_); // 1 - attr, 2 - class|int, 3 - name, 4 - extends, 5 - implements, 6 - body
+ classesRegex.lastIndex = 0;
+ var isStatic = m[1].indexOf("static") >= 0;
+ var body = atoms[getAtomIndex(m[6])], innerClass;
+ var oldClassId = currentClassId, newClassId = generateClassId();
+ currentClassId = newClassId;
+ if(m[2] === "interface") {
+ innerClass = new AstInnerInterface(m[3], transformInterfaceBody(body, m[3], m[4]), isStatic);
+ } else {
+ innerClass = new AstInnerClass(m[3], transformClassBody(body, m[3], m[4], m[5]), isStatic);
+ }
+ appendClass(innerClass, newClassId, oldClassId);
+ currentClassId = oldClassId;
+ return innerClass;
+ }
+
+ function AstClassMethod(name, params, body, isStatic) {
+ this.name = name;
+ this.params = params;
+ this.body = body;
+ this.isStatic = isStatic;
+ }
+ AstClassMethod.prototype.toString = function(){
+ var paramNames = appendToLookupTable({}, this.params.getNames());
+ var oldContext = replaceContext;
+ replaceContext = function (subject) {
+ return paramNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
+ };
+ var body = this.params.prependMethodArgs(this.body.toString());
+ var result = "function " + this.methodId + this.params + " " + body +"\n";
+ replaceContext = oldContext;
+ return result;
+ };
+
+ function transformClassMethod(method) {
+ var m = methodsRegex.exec(method);
+ methodsRegex.lastIndex = 0;
+ var isStatic = m[1].indexOf("static") >= 0;
+ var body = m[6] !== ';' ? atoms[getAtomIndex(m[6])] : "{}";
+ return new AstClassMethod(m[3], transformParams(atoms[getAtomIndex(m[4])]),
+ transformStatementsBlock(body), isStatic );
+ }
+
+ function AstClassField(definitions, fieldType, isStatic) {
+ this.definitions = definitions;
+ this.fieldType = fieldType;
+ this.isStatic = isStatic;
+ }
+ AstClassField.prototype.getNames = function() {
+ var names = [];
+ for(var i=0,l=this.definitions.length;i<l;++i) {
+ names.push(this.definitions[i].name);
+ }
+ return names;
+ };
+ AstClassField.prototype.toString = function() {
+ var thisPrefix = replaceContext({ name: "[this]" });
+ if(this.isStatic) {
+ var className = this.owner.name;
+ var staticDeclarations = [];
+ for(var i=0,l=this.definitions.length;i<l;++i) {
+ var definition = this.definitions[i];
+ var name = definition.name, staticName = className + "." + name;
+ var declaration = "if(" + staticName + " === void(0)) {\n" +
+ " " + staticName + " = " + definition.value + "; }\n" +
+ "$p.defineProperty(" + thisPrefix + ", " +
+ "'" + name + "', { get: function(){return " + staticName + ";}, " +
+ "set: function(val){" + staticName + " = val;} });\n";
+ staticDeclarations.push(declaration);
+ }
+ return staticDeclarations.join("");
+ }
+ return thisPrefix + "." + this.definitions.join("; " + thisPrefix + ".");
+ };
+
+ function transformClassField(statement) {
+ var attrAndType = attrAndTypeRegex.exec(statement);
+ var isStatic = attrAndType[1].indexOf("static") >= 0;
+ var definitions = statement.substring(attrAndType[0].length).split(/,\s*/g);
+ var defaultTypeValue = getDefaultValueForType(attrAndType[2]);
+ for(var i=0; i < definitions.length; ++i) {
+ definitions[i] = transformVarDefinition(definitions[i], defaultTypeValue);
+ }
+ return new AstClassField(definitions, attrAndType[2], isStatic);
+ }
+
+ function AstConstructor(params, body) {
+ this.params = params;
+ this.body = body;
+ }
+ AstConstructor.prototype.toString = function() {
+ var paramNames = appendToLookupTable({}, this.params.getNames());
+ var oldContext = replaceContext;
+ replaceContext = function (subject) {
+ return paramNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
+ };
+ var prefix = "function $constr_" + this.params.params.length + this.params.toString();
+ var body = this.params.prependMethodArgs(this.body.toString());
+ if(!/\$(superCstr|constr)\b/.test(body)) {
+ body = "{\n$superCstr();\n" + body.substring(1);
+ }
+ replaceContext = oldContext;
+ return prefix + body + "\n";
+ };
+
+ function transformConstructor(cstr) {
+ var m = new RegExp(/"B(\d+)"\s*"A(\d+)"/).exec(cstr);
+ var params = transformParams(atoms[m[1]]);
+
+ return new AstConstructor(params, transformStatementsBlock(atoms[m[2]]));
+ }
+
+ function AstInterfaceBody(name, interfacesNames, methodsNames, fields, innerClasses, misc) {
+ var i,l;
+ this.name = name;
+ this.interfacesNames = interfacesNames;
+ this.methodsNames = methodsNames;
+ this.fields = fields;
+ this.innerClasses = innerClasses;
+ this.misc = misc;
+ for(i=0,l=fields.length; i<l; ++i) {
+ fields[i].owner = this;
+ }
+ }
+ AstInterfaceBody.prototype.getMembers = function(classFields, classMethods, classInners) {
+ if(this.owner.base) {
+ this.owner.base.body.getMembers(classFields, classMethods, classInners);
+ }
+ var i, j, l, m;
+ for(i=0,l=this.fields.length;i<l;++i) {
+ var fieldNames = this.fields[i].getNames();
+ for(j=0,m=fieldNames.length;j<m;++j) {
+ classFields[fieldNames[j]] = this.fields[i];
+ }
+ }
+ for(i=0,l=this.methodsNames.length;i<l;++i) {
+ var methodName = this.methodsNames[i];
+ classMethods[methodName] = true;
+ }
+ for(i=0,l=this.innerClasses.length;i<l;++i) {
+ var innerClass = this.innerClasses[i];
+ classInners[innerClass.name] = innerClass;
+ }
+ };
+ AstInterfaceBody.prototype.toString = function() {
+ function getScopeLevel(p) {
+ var i = 0;
+ while(p) {
+ ++i;
+ p=p.scope;
+ }
+ return i;
+ }
+
+ var scopeLevel = getScopeLevel(this.owner);
+
+ var className = this.name;
+ var staticDefinitions = "";
+ var metadata = "";
+
+ var thisClassFields = {}, thisClassMethods = {}, thisClassInners = {};
+ this.getMembers(thisClassFields, thisClassMethods, thisClassInners);
+
+ var i, l, j, m;
+
+ if (this.owner.interfaces) {
+ // interface name can be present, but interface is not
+ var resolvedInterfaces = [], resolvedInterface;
+ for (i = 0, l = this.interfacesNames.length; i < l; ++i) {
+ if (!this.owner.interfaces[i]) {
+ continue;
+ }
+ resolvedInterface = replaceContext({name: this.interfacesNames[i]});
+ resolvedInterfaces.push(resolvedInterface);
+ staticDefinitions += "$p.extendInterfaceMembers(" + className + ", " + resolvedInterface + ");\n";
+ }
+ metadata += className + ".$interfaces = [" + resolvedInterfaces.join(", ") + "];\n";
+ }
+ metadata += className + ".$isInterface = true;\n";
+ metadata += className + ".$methods = [\'" + this.methodsNames.join("\', \'") + "\'];\n";
+
+ sortByWeight(this.innerClasses);
+ for (i = 0, l = this.innerClasses.length; i < l; ++i) {
+ var innerClass = this.innerClasses[i];
+ if (innerClass.isStatic) {
+ staticDefinitions += className + "." + innerClass.name + " = " + innerClass + ";\n";
+ }
+ }
+
+ for (i = 0, l = this.fields.length; i < l; ++i) {
+ var field = this.fields[i];
+ if (field.isStatic) {
+ staticDefinitions += className + "." + field.definitions.join(";\n" + className + ".") + ";\n";
+ }
+ }
+
+ return "(function() {\n" +
+ "function " + className + "() { throw \'Unable to create the interface\'; }\n" +
+ staticDefinitions +
+ metadata +
+ "return " + className + ";\n" +
+ "})()";
+ };
+
+ transformInterfaceBody = function(body, name, baseInterfaces) {
+ var declarations = body.substring(1, body.length - 1);
+ declarations = extractClassesAndMethods(declarations);
+ declarations = extractConstructors(declarations, name);
+ var methodsNames = [], classes = [];
+ declarations = declarations.replace(/"([DE])(\d+)"/g, function(all, type, index) {
+ if(type === 'D') { methodsNames.push(index); }
+ else if(type === 'E') { classes.push(index); }
+ return "";
+ });
+ var fields = declarations.split(/;(?:\s*;)*/g);
+ var baseInterfaceNames;
+ var i, l;
+
+ if(baseInterfaces !== undef) {
+ baseInterfaceNames = baseInterfaces.replace(/^\s*extends\s+(.+?)\s*$/g, "$1").split(/\s*,\s*/g);
+ }
+
+ for(i = 0, l = methodsNames.length; i < l; ++i) {
+ var method = transformClassMethod(atoms[methodsNames[i]]);
+ methodsNames[i] = method.name;
+ }
+ for(i = 0, l = fields.length - 1; i < l; ++i) {
+ var field = trimSpaces(fields[i]);
+ fields[i] = transformClassField(field.middle);
+ }
+ var tail = fields.pop();
+ for(i = 0, l = classes.length; i < l; ++i) {
+ classes[i] = transformInnerClass(atoms[classes[i]]);
+ }
+
+ return new AstInterfaceBody(name, baseInterfaceNames, methodsNames, fields, classes, { tail: tail });
+ };
+
+ function AstClassBody(name, baseClassName, interfacesNames, functions, methods, fields, cstrs, innerClasses, misc) {
+ var i,l;
+ this.name = name;
+ this.baseClassName = baseClassName;
+ this.interfacesNames = interfacesNames;
+ this.functions = functions;
+ this.methods = methods;
+ this.fields = fields;
+ this.cstrs = cstrs;
+ this.innerClasses = innerClasses;
+ this.misc = misc;
+ for(i=0,l=fields.length; i<l; ++i) {
+ fields[i].owner = this;
+ }
+ }
+ AstClassBody.prototype.getMembers = function(classFields, classMethods, classInners) {
+ if(this.owner.base) {
+ this.owner.base.body.getMembers(classFields, classMethods, classInners);
+ }
+ var i, j, l, m;
+ for(i=0,l=this.fields.length;i<l;++i) {
+ var fieldNames = this.fields[i].getNames();
+ for(j=0,m=fieldNames.length;j<m;++j) {
+ classFields[fieldNames[j]] = this.fields[i];
+ }
+ }
+ for(i=0,l=this.methods.length;i<l;++i) {
+ var method = this.methods[i];
+ classMethods[method.name] = method;
+ }
+ for(i=0,l=this.innerClasses.length;i<l;++i) {
+ var innerClass = this.innerClasses[i];
+ classInners[innerClass.name] = innerClass;
+ }
+ };
+ AstClassBody.prototype.toString = function() {
+ function getScopeLevel(p) {
+ var i = 0;
+ while(p) {
+ ++i;
+ p=p.scope;
+ }
+ return i;
+ }
+
+ var scopeLevel = getScopeLevel(this.owner);
+
+ var selfId = "$this_" + scopeLevel;
+ var className = this.name;
+ var result = "var " + selfId + " = this;\n";
+ var staticDefinitions = "";
+ var metadata = "";
+
+ var thisClassFields = {}, thisClassMethods = {}, thisClassInners = {};
+ this.getMembers(thisClassFields, thisClassMethods, thisClassInners);
+
+ var oldContext = replaceContext;
+ replaceContext = function (subject) {
+ var name = subject.name;
+ if(name === "this") {
+ // returns "$this_N.$self" pointer instead of "this" in cases:
+ // "this()", "this.XXX()", "this", but not for "this.XXX"
+ return subject.callSign || !subject.member ? selfId + ".$self" : selfId;
+ }
+ if(thisClassFields.hasOwnProperty(name)) {
+ return thisClassFields[name].isStatic ? className + "." + name : selfId + "." + name;
+ }
+ if(thisClassInners.hasOwnProperty(name)) {
+ return selfId + "." + name;
+ }
+ if(thisClassMethods.hasOwnProperty(name)) {
+ return thisClassMethods[name].isStatic ? className + "." + name : selfId + ".$self." + name;
+ }
+ return oldContext(subject);
+ };
+
+ var resolvedBaseClassName;
+ if (this.baseClassName) {
+ resolvedBaseClassName = oldContext({name: this.baseClassName});
+ result += "var $super = { $upcast: " + selfId + " };\n";
+ result += "function $superCstr(){" + resolvedBaseClassName +
+ ".apply($super,arguments);if(!('$self' in $super)) $p.extendClassChain($super)}\n";
+ metadata += className + ".$base = " + resolvedBaseClassName + ";\n";
+ } else {
+ result += "function $superCstr(){$p.extendClassChain("+ selfId +")}\n";
+ }
+
+ if (this.owner.base) {
+ // base class name can be present, but class is not
+ staticDefinitions += "$p.extendStaticMembers(" + className + ", " + resolvedBaseClassName + ");\n";
+ }
+
+ var i, l, j, m;
+
+ if (this.owner.interfaces) {
+ // interface name can be present, but interface is not
+ var resolvedInterfaces = [], resolvedInterface;
+ for (i = 0, l = this.interfacesNames.length; i < l; ++i) {
+ if (!this.owner.interfaces[i]) {
+ continue;
+ }
+ resolvedInterface = oldContext({name: this.interfacesNames[i]});
+ resolvedInterfaces.push(resolvedInterface);
+ staticDefinitions += "$p.extendInterfaceMembers(" + className + ", " + resolvedInterface + ");\n";
+ }
+ metadata += className + ".$interfaces = [" + resolvedInterfaces.join(", ") + "];\n";
+ }
+
+ if (this.functions.length > 0) {
+ result += this.functions.join('\n') + '\n';
+ }
+
+ sortByWeight(this.innerClasses);
+ for (i = 0, l = this.innerClasses.length; i < l; ++i) {
+ var innerClass = this.innerClasses[i];
+ if (innerClass.isStatic) {
+ staticDefinitions += className + "." + innerClass.name + " = " + innerClass + ";\n";
+ result += selfId + "." + innerClass.name + " = " + className + "." + innerClass.name + ";\n";
+ } else {
+ result += selfId + "." + innerClass.name + " = " + innerClass + ";\n";
+ }
+ }
+
+ for (i = 0, l = this.fields.length; i < l; ++i) {
+ var field = this.fields[i];
+ if (field.isStatic) {
+ staticDefinitions += className + "." + field.definitions.join(";\n" + className + ".") + ";\n";
+ for (j = 0, m = field.definitions.length; j < m; ++j) {
+ var fieldName = field.definitions[j].name, staticName = className + "." + fieldName;
+ result += "$p.defineProperty(" + selfId + ", '" + fieldName + "', {" +
+ "get: function(){return " + staticName + "}, " +
+ "set: function(val){" + staticName + " = val}});\n";
+ }
+ } else {
+ result += selfId + "." + field.definitions.join(";\n" + selfId + ".") + ";\n";
+ }
+ }
+ var methodOverloads = {};
+ for (i = 0, l = this.methods.length; i < l; ++i) {
+ var method = this.methods[i];
+ var overload = methodOverloads[method.name];
+ var methodId = method.name + "$" + method.params.params.length;
+ var hasMethodArgs = !!method.params.methodArgsParam;
+ if (overload) {
+ ++overload;
+ methodId += "_" + overload;
+ } else {
+ overload = 1;
+ }
+ method.methodId = methodId;
+ methodOverloads[method.name] = overload;
+ if (method.isStatic) {
+ staticDefinitions += method;
+ staticDefinitions += "$p.addMethod(" + className + ", '" + method.name + "', " + methodId + ", " + hasMethodArgs + ");\n";
+ result += "$p.addMethod(" + selfId + ", '" + method.name + "', " + methodId + ", " + hasMethodArgs + ");\n";
+ } else {
+ result += method;
+ result += "$p.addMethod(" + selfId + ", '" + method.name + "', " + methodId + ", " + hasMethodArgs + ");\n";
+ }
+ }
+ result += trim(this.misc.tail);
+
+ if (this.cstrs.length > 0) {
+ result += this.cstrs.join('\n') + '\n';
+ }
+
+ result += "function $constr() {\n";
+ var cstrsIfs = [];
+ for (i = 0, l = this.cstrs.length; i < l; ++i) {
+ var paramsLength = this.cstrs[i].params.params.length;
+ var methodArgsPresent = !!this.cstrs[i].params.methodArgsParam;
+ cstrsIfs.push("if(arguments.length " + (methodArgsPresent ? ">=" : "===") +
+ " " + paramsLength + ") { " +
+ "$constr_" + paramsLength + ".apply(" + selfId + ", arguments); }");
+ }
+ if(cstrsIfs.length > 0) {
+ result += cstrsIfs.join(" else ") + " else ";
+ }
+ // ??? add check if length is 0, otherwise fail
+ result += "$superCstr();\n}\n";
+ result += "$constr.apply(null, arguments);\n";
+
+ replaceContext = oldContext;
+ return "(function() {\n" +
+ "function " + className + "() {\n" + result + "}\n" +
+ staticDefinitions +
+ metadata +
+ "return " + className + ";\n" +
+ "})()";
+ };
+
+ transformClassBody = function(body, name, baseName, interfaces) {
+ var declarations = body.substring(1, body.length - 1);
+ declarations = extractClassesAndMethods(declarations);
+ declarations = extractConstructors(declarations, name);
+ var methods = [], classes = [], cstrs = [], functions = [];
+ declarations = declarations.replace(/"([DEGH])(\d+)"/g, function(all, type, index) {
+ if(type === 'D') { methods.push(index); }
+ else if(type === 'E') { classes.push(index); }
+ else if(type === 'H') { functions.push(index); }
+ else { cstrs.push(index); }
+ return "";
+ });
+ var fields = declarations.replace(/^(?:\s*;)+/, "").split(/;(?:\s*;)*/g);
+ var baseClassName, interfacesNames;
+ var i;
+
+ if(baseName !== undef) {
+ baseClassName = baseName.replace(/^\s*extends\s+([A-Za-z_$][\w$]*\b(?:\s*\.\s*[A-Za-z_$][\w$]*\b)*)\s*$/g, "$1");
+ }
+
+ if(interfaces !== undef) {
+ interfacesNames = interfaces.replace(/^\s*implements\s+(.+?)\s*$/g, "$1").split(/\s*,\s*/g);
+ }
+
+ for(i = 0; i < functions.length; ++i) {
+ functions[i] = transformFunction(atoms[functions[i]]);
+ }
+ for(i = 0; i < methods.length; ++i) {
+ methods[i] = transformClassMethod(atoms[methods[i]]);
+ }
+ for(i = 0; i < fields.length - 1; ++i) {
+ var field = trimSpaces(fields[i]);
+ fields[i] = transformClassField(field.middle);
+ }
+ var tail = fields.pop();
+ for(i = 0; i < cstrs.length; ++i) {
+ cstrs[i] = transformConstructor(atoms[cstrs[i]]);
+ }
+ for(i = 0; i < classes.length; ++i) {
+ classes[i] = transformInnerClass(atoms[classes[i]]);
+ }
+
+ return new AstClassBody(name, baseClassName, interfacesNames, functions, methods, fields, cstrs,
+ classes, { tail: tail });
+ };
+
+ function AstInterface(name, body) {
+ this.name = name;
+ this.body = body;
+ body.owner = this;
+ }
+ AstInterface.prototype.toString = function() {
+ return "var " + this.name + " = " + this.body + ";\n" +
+ "$p." + this.name + " = " + this.name + ";\n";
+ };
+ function AstClass(name, body) {
+ this.name = name;
+ this.body = body;
+ body.owner = this;
+ }
+ AstClass.prototype.toString = function() {
+ return "var " + this.name + " = " + this.body + ";\n" +
+ "$p." + this.name + " = " + this.name + ";\n";
+ };
+
+ function transformGlobalClass(class_) {
+ var m = classesRegex.exec(class_); // 1 - attr, 2 - class|int, 3 - name, 4 - extends, 5 - implements, 6 - body
+ classesRegex.lastIndex = 0;
+ var body = atoms[getAtomIndex(m[6])];
+ var oldClassId = currentClassId, newClassId = generateClassId();
+ currentClassId = newClassId;
+ var globalClass;
+ if(m[2] === "interface") {
+ globalClass = new AstInterface(m[3], transformInterfaceBody(body, m[3], m[4]) );
+ } else {
+ globalClass = new AstClass(m[3], transformClassBody(body, m[3], m[4], m[5]) );
+ }
+ appendClass(globalClass, newClassId, oldClassId);
+ currentClassId = oldClassId;
+ return globalClass;
+ }
+
+ function AstMethod(name, params, body) {
+ this.name = name;
+ this.params = params;
+ this.body = body;
+ }
+ AstMethod.prototype.toString = function(){
+ var paramNames = appendToLookupTable({}, this.params.getNames());
+ var oldContext = replaceContext;
+ replaceContext = function (subject) {
+ return paramNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
+ };
+ var body = this.params.prependMethodArgs(this.body.toString());
+ var result = "function " + this.name + this.params + " " + body + "\n" +
+ "$p." + this.name + " = " + this.name + ";\n" +
+ this.name + " = " + this.name + ".bind($p);";
+// "$p." + this.name + " = " + this.name + ";";
+ replaceContext = oldContext;
+ return result;
+ };
+
+ function transformGlobalMethod(method) {
+ var m = methodsRegex.exec(method);
+ var result =
+ methodsRegex.lastIndex = 0;
+ return new AstMethod(m[3], transformParams(atoms[getAtomIndex(m[4])]),
+ transformStatementsBlock(atoms[getAtomIndex(m[6])]));
+ }
+
+ function preStatementsTransform(statements) {
+ var s = statements;
+ // turns multiple catch blocks into one, because we have no way to properly get into them anyway.
+ s = s.replace(/\b(catch\s*"B\d+"\s*"A\d+")(\s*catch\s*"B\d+"\s*"A\d+")+/g, "$1");
+ return s;
+ }
+
+ function AstForStatement(argument, misc) {
+ this.argument = argument;
+ this.misc = misc;
+ }
+ AstForStatement.prototype.toString = function() {
+ return this.misc.prefix + this.argument.toString();
+ };
+ function AstCatchStatement(argument, misc) {
+ this.argument = argument;
+ this.misc = misc;
+ }
+ AstCatchStatement.prototype.toString = function() {
+ return this.misc.prefix + this.argument.toString();
+ };
+ function AstPrefixStatement(name, argument, misc) {
+ this.name = name;
+ this.argument = argument;
+ this.misc = misc;
+ }
+ AstPrefixStatement.prototype.toString = function() {
+ var result = this.misc.prefix;
+ if(this.argument !== undef) {
+ result += this.argument.toString();
+ }
+ return result;
+ };
+ function AstSwitchCase(expr) {
+ this.expr = expr;
+ }
+ AstSwitchCase.prototype.toString = function() {
+ return "case " + this.expr + ":";
+ };
+ function AstLabel(label) {
+ this.label = label;
+ }
+ AstLabel.prototype.toString = function() {
+ return this.label;
+ };
+
+ transformStatements = function(statements, transformMethod, transformClass) {
+ var nextStatement = new RegExp(/\b(catch|for|if|switch|while|with)\s*"B(\d+)"|\b(do|else|finally|return|throw|try|break|continue)\b|("[ADEH](\d+)")|\b(case)\s+([^:]+):|\b([A-Za-z_$][\w$]*\s*:)|(;)/g);
+ var res = [];
+ statements = preStatementsTransform(statements);
+ var lastIndex = 0, m, space;
+ // m contains the matches from the nextStatement regexp, null if there are no matches.
+ // nextStatement.exec starts searching at nextStatement.lastIndex.
+ while((m = nextStatement.exec(statements)) !== null) {
+ if(m[1] !== undef) { // catch, for ...
+ var i = statements.lastIndexOf('"B', nextStatement.lastIndex);
+ var statementsPrefix = statements.substring(lastIndex, i);
+ if(m[1] === "for") {
+ res.push(new AstForStatement(transformForExpression(atoms[m[2]]),
+ { prefix: statementsPrefix }) );
+ } else if(m[1] === "catch") {
+ res.push(new AstCatchStatement(transformParams(atoms[m[2]]),
+ { prefix: statementsPrefix }) );
+ } else {
+ res.push(new AstPrefixStatement(m[1], transformExpression(atoms[m[2]]),
+ { prefix: statementsPrefix }) );
+ }
+ } else if(m[3] !== undef) { // do, else, ...
+ res.push(new AstPrefixStatement(m[3], undef,
+ { prefix: statements.substring(lastIndex, nextStatement.lastIndex) }) );
+ } else if(m[4] !== undef) { // block, class and methods
+ space = statements.substring(lastIndex, nextStatement.lastIndex - m[4].length);
+ if(trim(space).length !== 0) { continue; } // avoiding new type[] {} construct
+ res.push(space);
+ var kind = m[4].charAt(1), atomIndex = m[5];
+ if(kind === 'D') {
+ res.push(transformMethod(atoms[atomIndex]));
+ } else if(kind === 'E') {
+ res.push(transformClass(atoms[atomIndex]));
+ } else if(kind === 'H') {
+ res.push(transformFunction(atoms[atomIndex]));
+ } else {
+ res.push(transformStatementsBlock(atoms[atomIndex]));
+ }
+ } else if(m[6] !== undef) { // switch case
+ res.push(new AstSwitchCase(transformExpression(trim(m[7]))));
+ } else if(m[8] !== undef) { // label
+ space = statements.substring(lastIndex, nextStatement.lastIndex - m[8].length);
+ if(trim(space).length !== 0) { continue; } // avoiding ?: construct
+ res.push(new AstLabel(statements.substring(lastIndex, nextStatement.lastIndex)) );
+ } else { // semicolon
+ var statement = trimSpaces(statements.substring(lastIndex, nextStatement.lastIndex - 1));
+ res.push(statement.left);
+ res.push(transformStatement(statement.middle));
+ res.push(statement.right + ";");
+ }
+ lastIndex = nextStatement.lastIndex;
+ }
+ var statementsTail = trimSpaces(statements.substring(lastIndex));
+ res.push(statementsTail.left);
+ if(statementsTail.middle !== "") {
+ res.push(transformStatement(statementsTail.middle));
+ res.push(";" + statementsTail.right);
+ }
+ return res;
+ };
+
+ function getLocalNames(statements) {
+ var localNames = [];
+ for(var i=0,l=statements.length;i<l;++i) {
+ var statement = statements[i];
+ if(statement instanceof AstVar) {
+ localNames = localNames.concat(statement.getNames());
+ } else if(statement instanceof AstForStatement &&
+ statement.argument.initStatement instanceof AstVar) {
+ localNames = localNames.concat(statement.argument.initStatement.getNames());
+ } else if(statement instanceof AstInnerInterface || statement instanceof AstInnerClass ||
+ statement instanceof AstInterface || statement instanceof AstClass ||
+ statement instanceof AstMethod || statement instanceof AstFunction) {
+ localNames.push(statement.name);
+ }
+ }
+ return appendToLookupTable({}, localNames);
+ }
+
+ function AstStatementsBlock(statements) {
+ this.statements = statements;
+ }
+ AstStatementsBlock.prototype.toString = function() {
+ var localNames = getLocalNames(this.statements);
+ var oldContext = replaceContext;
+
+ // replacing context only when necessary
+ if(!isLookupTableEmpty(localNames)) {
+ replaceContext = function (subject) {
+ return localNames.hasOwnProperty(subject.name) ? subject.name : oldContext(subject);
+ };
+ }
+
+ var result = "{\n" + this.statements.join('') + "\n}";
+ replaceContext = oldContext;
+ return result;
+ };
+
+ transformStatementsBlock = function(block) {
+ var content = trimSpaces(block.substring(1, block.length - 1));
+ return new AstStatementsBlock(transformStatements(content.middle));
+ };
+
+ function AstRoot(statements) {
+ this.statements = statements;
+ }
+ AstRoot.prototype.toString = function() {
+ var classes = [], otherStatements = [], statement;
+ for (var i = 0, len = this.statements.length; i < len; ++i) {
+ statement = this.statements[i];
+ if (statement instanceof AstClass || statement instanceof AstInterface) {
+ classes.push(statement);
+ } else {
+ otherStatements.push(statement);
+ }
+ }
+ sortByWeight(classes);
+
+ var localNames = getLocalNames(this.statements);
+ replaceContext = function (subject) {
+ var name = subject.name;
+ if(localNames.hasOwnProperty(name)) {
+ return name;
+ }
+ if(globalMembers.hasOwnProperty(name) ||
+ PConstants.hasOwnProperty(name) ||
+ defaultScope.hasOwnProperty(name)) {
+ return "$p." + name;
+ }
+ return name;
+ };
+ var result = "// this code was autogenerated from PJS\n" +
+ "(function($p) {\n" +
+ classes.join('') + "\n" +
+ otherStatements.join('') + "\n})";
+ replaceContext = null;
+ return result;
+ };
+
+ transformMain = function() {
+ var statements = extractClassesAndMethods(atoms[0]);
+ statements = statements.replace(/\bimport\s+[^;]+;/g, "");
+ return new AstRoot( transformStatements(statements,
+ transformGlobalMethod, transformGlobalClass) );
+ };
+
+ function generateMetadata(ast) {
+ var globalScope = {};
+ var id, class_;
+ for(id in declaredClasses) {
+ if(declaredClasses.hasOwnProperty(id)) {
+ class_ = declaredClasses[id];
+ var scopeId = class_.scopeId, name = class_.name;
+ if(scopeId) {
+ var scope = declaredClasses[scopeId];
+ class_.scope = scope;
+ if(scope.inScope === undef) {
+ scope.inScope = {};
+ }
+ scope.inScope[name] = class_;
+ } else {
+ globalScope[name] = class_;
+ }
+ }
+ }
+
+ function findInScopes(class_, name) {
+ var parts = name.split('.');
+ var currentScope = class_.scope, found;
+ while(currentScope) {
+ if(currentScope.hasOwnProperty(parts[0])) {
+ found = currentScope[parts[0]]; break;
+ }
+ currentScope = currentScope.scope;
+ }
+ if(found === undef) {
+ found = globalScope[parts[0]];
+ }
+ for(var i=1,l=parts.length;i<l && found;++i) {
+ found = found.inScope[parts[i]];
+ }
+ return found;
+ }
+
+ for(id in declaredClasses) {
+ if(declaredClasses.hasOwnProperty(id)) {
+ class_ = declaredClasses[id];
+ var baseClassName = class_.body.baseClassName;
+ if(baseClassName) {
+ var parent = findInScopes(class_, baseClassName);
+ if (parent) {
+ class_.base = parent;
+ if (!parent.derived) {
+ parent.derived = [];
+ }
+ parent.derived.push(class_);
+ }
+ }
+ var interfacesNames = class_.body.interfacesNames,
+ interfaces = [], i, l;
+ if (interfacesNames && interfacesNames.length > 0) {
+ for (i = 0, l = interfacesNames.length; i < l; ++i) {
+ var interface_ = findInScopes(class_, interfacesNames[i]);
+ interfaces.push(interface_);
+ if (!interface_) {
+ continue;
+ }
+ if (!interface_.derived) {
+ interface_.derived = [];
+ }
+ interface_.derived.push(class_);
+ }
+ if (interfaces.length > 0) {
+ class_.interfaces = interfaces;
+ }
+ }
+ }
+ }
+ }
+
+ function setWeight(ast) {
+ var queue = [], tocheck = {};
+ var id, scopeId, class_;
+ // queue most inner and non-inherited
+ for (id in declaredClasses) {
+ if (declaredClasses.hasOwnProperty(id)) {
+ class_ = declaredClasses[id];
+ if (!class_.inScope && !class_.derived) {
+ queue.push(id);
+ class_.weight = 0;
+ } else {
+ var dependsOn = [];
+ if (class_.inScope) {
+ for (scopeId in class_.inScope) {
+ if (class_.inScope.hasOwnProperty(scopeId)) {
+ dependsOn.push(class_.inScope[scopeId]);
+ }
+ }
+ }
+ if (class_.derived) {
+ dependsOn = dependsOn.concat(class_.derived);
+ }
+ tocheck[id] = dependsOn;
+ }
+ }
+ }
+ function removeDependentAndCheck(targetId, from) {
+ var dependsOn = tocheck[targetId];
+ if (!dependsOn) {
+ return false; // no need to process
+ }
+ var i = dependsOn.indexOf(from);
+ if (i < 0) {
+ return false;
+ }
+ dependsOn.splice(i, 1);
+ if (dependsOn.length > 0) {
+ return false;
+ }
+ delete tocheck[targetId];
+ return true;
+ }
+ while (queue.length > 0) {
+ id = queue.shift();
+ class_ = declaredClasses[id];
+ if (class_.scopeId && removeDependentAndCheck(class_.scopeId, class_)) {
+ queue.push(class_.scopeId);
+ declaredClasses[class_.scopeId].weight = class_.weight + 1;
+ }
+ if (class_.base && removeDependentAndCheck(class_.base.classId, class_)) {
+ queue.push(class_.base.classId);
+ class_.base.weight = class_.weight + 1;
+ }
+ if (class_.interfaces) {
+ var i, l;
+ for (i = 0, l = class_.interfaces.length; i < l; ++i) {
+ if (!class_.interfaces[i] ||
+ !removeDependentAndCheck(class_.interfaces[i].classId, class_)) {
+ continue;
+ }
+ queue.push(class_.interfaces[i].classId);
+ class_.interfaces[i].weight = class_.weight + 1;
+ }
+ }
+ }
+ }
+
+ var transformed = transformMain();
+ generateMetadata(transformed);
+ setWeight(transformed);
+
+ var redendered = transformed.toString();
+
+ // remove empty extra lines with space
+ redendered = redendered.replace(/\s*\n(?:[\t ]*\n)+/g, "\n\n");
+
+ // convert character codes to characters
+ redendered = redendered.replace(/__x([0-9A-F]{4})/g, function(all, hexCode) {
+ return String.fromCharCode(parseInt(hexCode,16));
+ });
+
+ return injectStrings(redendered, strings);
+ }// Parser ends
+
+ function preprocessCode(aCode, sketch) {
+ // Parse out @pjs directive, if any.
+ var dm = new RegExp(/\/\*\s*@pjs\s+((?:[^\*]|\*+[^\*\/])*)\*\//g).exec(aCode);
+ if (dm && dm.length === 2) {
+ // masks contents of a JSON to be replaced later
+ // to protect the contents from further parsing
+ var jsonItems = [],
+ directives = dm.splice(1, 2)[0].replace(/\{([\s\S]*?)\}/g, (function() {
+ return function(all, item) {
+ jsonItems.push(item);
+ return "{" + (jsonItems.length-1) + "}";
+ };
+ }())).replace('\n', '').replace('\r', '').split(";");
+
+ // We'll L/RTrim, and also remove any surrounding double quotes (e.g., just take string contents)
+ var clean = function(s) {
+ return s.replace(/^\s*["']?/, '').replace(/["']?\s*$/, '');
+ };
+
+ for (var i = 0, dl = directives.length; i < dl; i++) {
+ var pair = directives[i].split('=');
+ if (pair && pair.length === 2) {
+ var key = clean(pair[0]),
+ value = clean(pair[1]),
+ list = [];
+ // A few directives require work beyond storying key/value pairings
+ if (key === "preload") {
+ list = value.split(',');
+ // All pre-loaded images will get put in imageCache, keyed on filename
+ for (var j = 0, jl = list.length; j < jl; j++) {
+ var imageName = clean(list[j]);
+ sketch.imageCache.add(imageName);
+ }
+ // fonts can be declared as a string containing a url,
+ // or a JSON object, containing a font name, and a url
+ } else if (key === "font") {
+ list = value.split(",");
+ for (var x = 0, xl = list.length; x < xl; x++) {
+ var fontName = clean(list[x]),
+ index = /^\{(\d*?)\}$/.exec(fontName);
+ // if index is not null, send JSON, otherwise, send string
+ PFont.preloading.add(index ? JSON.parse("{" + jsonItems[index[1]] + "}") : fontName);
+ }
+ } else if (key === "pauseOnBlur") {
+ sketch.options.pauseOnBlur = value === "true";
+ } else if (key === "globalKeyEvents") {
+ sketch.options.globalKeyEvents = value === "true";
+ } else if (key.substring(0, 6) === "param-") {
+ sketch.params[key.substring(6)] = value;
+ } else {
+ sketch.options[key] = value;
+ }
+ }
+ }
+ }
+ return aCode;
+ }
+
+ // Parse/compiles Processing (Java-like) syntax to JavaScript syntax
+ Processing.compile = function(pdeCode) {
+ var sketch = new Processing.Sketch();
+ var code = preprocessCode(pdeCode, sketch);
+ var compiledPde = parseProcessing(code);
+ sketch.sourceCode = compiledPde;
+ return sketch;
+ };
+
+ var PjsConsole = require("../Helpers/PjsConsole");
+ Processing.logger = new PjsConsole(document);
+
+ // done
+ return Processing;
+};
+
+},{"../Helpers/PjsConsole":5}],27:[function(require,module,exports){
+/**
+ * Processing.js object
+ */
+ module.exports = function(options, undef) {
+ var defaultScope = options.defaultScope,
+ extend = options.extend,
+ Browser = options.Browser,
+ ajax = Browser.ajax,
+ navigator = Browser.navigator,
+ window = Browser.window,
+ XMLHttpRequest = window.XMLHttpRequest,
+ document = Browser.document,
+ noop = options.noop,
+
+ PConstants = defaultScope.PConstants;
+ PFont = defaultScope.PFont,
+ PShapeSVG = defaultScope.PShapeSVG,
+ PVector = defaultScope.PVector,
+ Char = Character = defaultScope.Char,
+ ObjectIterator = defaultScope.ObjectIterator,
+ XMLElement = defaultScope.XMLElement,
+ XML = defaultScope.XML;
+
+ // fascinating "read only" jshint error if we don't start a new var block here.
+ var HTMLCanvasElement = window.HTMLCanvasElement,
+ HTMLImageElement = window.HTMLImageElement;
+
+ // window.localStorage cannot be accessed if a user is blocking cookies.
+ // In that case, we make it a temporary source cache object.
+ var localStorage;
+ try { localStorage = window.localStorage; } catch (e) { localStorage = {}; }
+
+ var isDOMPresent = ("document" in this) && !("fake" in this.document);
+
+ // document.head polyfill for the benefit of Firefox 3.6
+ if (!document.head) {
+ document.head = document.getElementsByTagName('head')[0];
+ }
+
+ var Float32Array = setupTypedArray("Float32Array", "WebGLFloatArray"),
+ Int32Array = setupTypedArray("Int32Array", "WebGLIntArray"),
+ Uint16Array = setupTypedArray("Uint16Array", "WebGLUnsignedShortArray"),
+ Uint8Array = setupTypedArray("Uint8Array", "WebGLUnsignedByteArray");
+
+ // Typed Arrays: fallback to WebGL arrays or Native JS arrays if unavailable
+ function setupTypedArray(name, fallback) {
+ // Check if TypedArray exists, and use if so.
+ if (name in window) {
+ return window[name];
+ }
+
+ // Check if WebGLArray exists
+ if (typeof window[fallback] === "function") {
+ return window[fallback];
+ }
+
+ // Use Native JS array
+ return function(obj) {
+ if (obj instanceof Array) {
+ return obj;
+ }
+ if (typeof obj === "number") {
+ var arr = [];
+ arr.length = obj;
+ return arr;
+ }
+ };
+ }
+
+ /* IE9+ quirks mode check - ticket #1606 */
+ if (document.documentMode >= 9 && !document.doctype) {
+ throw("The doctype directive is missing. The recommended doctype in Internet Explorer is the HTML5 doctype: <!DOCTYPE html>");
+ }
+
+ // Manage multiple Processing instances
+ var processingInstances = [];
+ var processingInstanceIds = {};
+
+ /**
+ * instance tracking - adding new instances
+ */
+ var addInstance = function(processing) {
+ if (processing.externals.canvas.id === undef || !processing.externals.canvas.id.length) {
+ processing.externals.canvas.id = "__processing" + processingInstances.length;
+ }
+ processingInstanceIds[processing.externals.canvas.id] = processingInstances.length;
+ processingInstances.push(processing);
+ };
+
+ /**
+ * instance tracking - removal
+ */
+ var removeInstance = function(id) {
+ processingInstances.splice(processingInstanceIds[id], 1);
+ delete processingInstanceIds[id];
+ };
+
+
+ /**
+ * The Processing object
+ */
+ var Processing = this.Processing = function(aCanvas, aCode, aFunctions) {
+
+ if (!(this instanceof Processing)) {
+ throw("called Processing constructor as if it were a function: missing 'new'.");
+ }
+
+ var curElement = {},
+ pgraphicsMode = (aCanvas === undef && aCode === undef);
+
+ if (pgraphicsMode) {
+ curElement = document.createElement("canvas");
+ } else {
+ // We'll take a canvas element or a string for a canvas element's id
+ curElement = typeof aCanvas === "string" ? document.getElementById(aCanvas) : aCanvas;
+ }
+
+ if (!('getContext' in curElement)) {
+ throw("called Processing constructor without passing canvas element reference or id.");
+ }
+
+ function unimplemented(s) {
+ Processing.debug('Unimplemented - ' + s);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // JavaScript event binding and releasing
+ ////////////////////////////////////////////////////////////////////////////
+
+ var eventHandlers = [];
+
+ function attachEventHandler(elem, type, fn) {
+ if (elem.addEventListener) {
+ elem.addEventListener(type, fn, false);
+ } else {
+ elem.attachEvent("on" + type, fn);
+ }
+ eventHandlers.push({elem: elem, type: type, fn: fn});
+ }
+
+ function detachEventHandler(eventHandler) {
+ var elem = eventHandler.elem,
+ type = eventHandler.type,
+ fn = eventHandler.fn;
+ if (elem.removeEventListener) {
+ elem.removeEventListener(type, fn, false);
+ } else if (elem.detachEvent) {
+ elem.detachEvent("on" + type, fn);
+ }
+ }
+
+ function removeFirstArgument(args) {
+ return Array.prototype.slice.call(args, 1);
+ }
+
+ // When something new is added to "p." it must also be added to the "names" array.
+ // The names array contains the names of everything that is inside "p."
+ var p = this;
+
+ p.Char = p.Character = Char;
+
+ // add in the Processing API functions
+ extend.withCommonFunctions(p);
+ extend.withMath(p);
+ extend.withProxyFunctions(p, removeFirstArgument);
+ extend.withTouch(p, curElement, attachEventHandler, document, PConstants);
+
+ // custom functions and properties are added here
+ if(aFunctions) {
+ Object.keys(aFunctions).forEach(function(name) {
+ p[name] = aFunctions[name];
+ });
+ }
+
+ // PJS specific (non-p5) methods and properties to externalize
+ p.externals = {
+ canvas: curElement,
+ context: undef,
+ sketch: undef,
+ window: window
+ };
+
+ p.name = 'Processing.js Instance'; // Set Processing defaults / environment variables
+ p.use3DContext = false; // default '2d' canvas context
+
+ /**
+ * Confirms if a Processing program is "focused", meaning that it is
+ * active and will accept input from mouse or keyboard. This variable
+ * is "true" if it is focused and "false" if not. This variable is
+ * often used when you want to warn people they need to click on the
+ * browser before it will work.
+ */
+ p.focused = false;
+ p.breakShape = false;
+
+ // Glyph path storage for textFonts
+ p.glyphTable = {};
+
+ // Global vars for tracking mouse position
+ p.pmouseX = 0;
+ p.pmouseY = 0;
+ p.mouseX = 0;
+ p.mouseY = 0;
+ p.mouseButton = 0;
+ p.mouseScroll = 0;
+
+ // Undefined event handlers to be replaced by user when needed
+ p.mouseClicked = undef;
+ p.mouseDragged = undef;
+ p.mouseMoved = undef;
+ p.mousePressed = undef;
+ p.mouseReleased = undef;
+ p.mouseScrolled = undef;
+ p.mouseOver = undef;
+ p.mouseOut = undef;
+ p.touchStart = undef;
+ p.touchEnd = undef;
+ p.touchMove = undef;
+ p.touchCancel = undef;
+ p.key = undef;
+ p.keyCode = undef;
+ p.keyPressed = noop; // needed to remove function checks
+ p.keyReleased = noop;
+ p.keyTyped = noop;
+ p.draw = undef;
+ p.setup = undef;
+
+ // Remapped vars
+ p.__mousePressed = false;
+ p.__keyPressed = false;
+ p.__frameRate = 60;
+
+ // The current animation frame
+ p.frameCount = 0;
+
+ // The height/width of the canvas
+ p.width = 100;
+ p.height = 100;
+
+ // "Private" variables used to maintain state
+ var curContext,
+ curSketch,
+ drawing, // hold a Drawing2D or Drawing3D object
+ doFill = true,
+ fillStyle = [1.0, 1.0, 1.0, 1.0],
+ currentFillColor = 0xFFFFFFFF,
+ isFillDirty = true,
+ doStroke = true,
+ strokeStyle = [0.0, 0.0, 0.0, 1.0],
+ currentStrokeColor = 0xFF000000,
+ isStrokeDirty = true,
+ lineWidth = 1,
+ loopStarted = false,
+ renderSmooth = false,
+ doLoop = true,
+ looping = 0,
+ curRectMode = PConstants.CORNER,
+ curEllipseMode = PConstants.CENTER,
+ normalX = 0,
+ normalY = 0,
+ normalZ = 0,
+ normalMode = PConstants.NORMAL_MODE_AUTO,
+ curFrameRate = 60,
+ curMsPerFrame = 1000/curFrameRate,
+ curCursor = PConstants.ARROW,
+ oldCursor = curElement.style.cursor,
+ curShape = PConstants.POLYGON,
+ curShapeCount = 0,
+ curvePoints = [],
+ curTightness = 0,
+ curveDet = 20,
+ curveInited = false,
+ backgroundObj = -3355444, // rgb(204, 204, 204) is the default gray background colour
+ bezDetail = 20,
+ colorModeA = 255,
+ colorModeX = 255,
+ colorModeY = 255,
+ colorModeZ = 255,
+ pathOpen = false,
+ mouseDragging = false,
+ pmouseXLastFrame = 0,
+ pmouseYLastFrame = 0,
+ curColorMode = PConstants.RGB,
+ curTint = null,
+ curTint3d = null,
+ getLoaded = false,
+ start = Date.now(),
+ timeSinceLastFPS = start,
+ framesSinceLastFPS = 0,
+ textcanvas,
+ curveBasisMatrix,
+ curveToBezierMatrix,
+ curveDrawMatrix,
+ bezierDrawMatrix,
+ bezierBasisInverse,
+ bezierBasisMatrix,
+ curContextCache = { attributes: {}, locations: {} },
+ // Shaders
+ programObject3D,
+ programObject2D,
+ programObjectUnlitShape,
+ boxBuffer,
+ boxNormBuffer,
+ boxOutlineBuffer,
+ rectBuffer,
+ rectNormBuffer,
+ sphereBuffer,
+ lineBuffer,
+ fillBuffer,
+ fillColorBuffer,
+ strokeColorBuffer,
+ pointBuffer,
+ shapeTexVBO,
+ canTex, // texture for createGraphics
+ textTex, // texture for 3d tex
+ curTexture = {width:0,height:0},
+ curTextureMode = PConstants.IMAGE,
+ usingTexture = false,
+ textBuffer,
+ textureBuffer,
+ indexBuffer,
+ // Text alignment
+ horizontalTextAlignment = PConstants.LEFT,
+ verticalTextAlignment = PConstants.BASELINE,
+ textMode = PConstants.MODEL,
+ // Font state
+ curFontName = "Arial",
+ curTextSize = 12,
+ curTextAscent = 9,
+ curTextDescent = 2,
+ curTextLeading = 14,
+ curTextFont = PFont.get(curFontName, curTextSize),
+ // Pixels cache
+ originalContext,
+ proxyContext = null,
+ isContextReplaced = false,
+ setPixelsCached,
+ maxPixelsCached = 1000,
+ pressedKeysMap = [],
+ lastPressedKeyCode = null,
+ codedKeys = [ PConstants.SHIFT, PConstants.CONTROL, PConstants.ALT, PConstants.CAPSLK, PConstants.PGUP, PConstants.PGDN,
+ PConstants.END, PConstants.HOME, PConstants.LEFT, PConstants.UP, PConstants.RIGHT, PConstants.DOWN, PConstants.NUMLK,
+ PConstants.INSERT, PConstants.F1, PConstants.F2, PConstants.F3, PConstants.F4, PConstants.F5, PConstants.F6, PConstants.F7,
+ PConstants.F8, PConstants.F9, PConstants.F10, PConstants.F11, PConstants.F12, PConstants.META ];
+
+ // User can only have MAX_LIGHTS lights
+ var lightCount = 0;
+
+ //sphere stuff
+ var sphereDetailV = 0,
+ sphereDetailU = 0,
+ sphereX = [],
+ sphereY = [],
+ sphereZ = [],
+ sinLUT = new Float32Array(PConstants.SINCOS_LENGTH),
+ cosLUT = new Float32Array(PConstants.SINCOS_LENGTH),
+ sphereVerts,
+ sphereNorms;
+
+ // Camera defaults and settings
+ var cam,
+ cameraInv,
+ modelView,
+ modelViewInv,
+ userMatrixStack,
+ userReverseMatrixStack,
+ inverseCopy,
+ projection,
+ manipulatingCamera = false,
+ frustumMode = false,
+ cameraFOV = 60 * (Math.PI / 180),
+ cameraX = p.width / 2,
+ cameraY = p.height / 2,
+ cameraZ = cameraY / Math.tan(cameraFOV / 2),
+ cameraNear = cameraZ / 10,
+ cameraFar = cameraZ * 10,
+ cameraAspect = p.width / p.height;
+
+ var vertArray = [],
+ curveVertArray = [],
+ curveVertCount = 0,
+ isCurve = false,
+ isBezier = false,
+ firstVert = true;
+
+ //PShape stuff
+ var curShapeMode = PConstants.CORNER;
+
+ // Stores states for pushStyle() and popStyle().
+ var styleArray = [];
+
+ // The vertices for the box cannot be specified using a triangle strip since each
+ // side of the cube must have its own set of normals.
+ // Vertices are specified in a counter-clockwise order.
+ // Triangles are in this order: back, front, right, bottom, left, top.
+ var boxVerts = new Float32Array([
+ 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5,
+ 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5,
+ 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5,
+ 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5,
+ -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5,
+ 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5]);
+
+ var boxOutlineVerts = new Float32Array([
+ 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5,
+ -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5,
+ 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5,
+ -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
+ 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5,
+ -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5]);
+
+ var boxNorms = new Float32Array([
+ 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1,
+ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
+ 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0,
+ -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0,
+ 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0]);
+
+ // These verts are used for the fill and stroke using TRIANGLE_FAN and LINE_LOOP.
+ var rectVerts = new Float32Array([0,0,0, 0,1,0, 1,1,0, 1,0,0]);
+
+ var rectNorms = new Float32Array([0,0,1, 0,0,1, 0,0,1, 0,0,1]);
+
+ // Shader for points and lines in begin/endShape.
+ var vertexShaderSrcUnlitShape =
+ "varying vec4 vFrontColor;" +
+
+ "attribute vec3 aVertex;" +
+ "attribute vec4 aColor;" +
+
+ "uniform mat4 uView;" +
+ "uniform mat4 uProjection;" +
+ "uniform float uPointSize;" +
+
+ "void main(void) {" +
+ " vFrontColor = aColor;" +
+ " gl_PointSize = uPointSize;" +
+ " gl_Position = uProjection * uView * vec4(aVertex, 1.0);" +
+ "}";
+
+ var fragmentShaderSrcUnlitShape =
+ "#ifdef GL_ES\n" +
+ "precision highp float;\n" +
+ "#endif\n" +
+
+ "varying vec4 vFrontColor;" +
+ "uniform bool uSmooth;" +
+
+ "void main(void){" +
+ " if(uSmooth == true){" +
+ " float dist = distance(gl_PointCoord, vec2(0.5));" +
+ " if(dist > 0.5){" +
+ " discard;" +
+ " }" +
+ " }" +
+ " gl_FragColor = vFrontColor;" +
+ "}";
+
+ // Shader for rect, text, box outlines, sphere outlines, point() and line().
+ var vertexShaderSrc2D =
+ "varying vec4 vFrontColor;" +
+
+ "attribute vec3 aVertex;" +
+ "attribute vec2 aTextureCoord;" +
+ "uniform vec4 uColor;" +
+
+ "uniform mat4 uModel;" +
+ "uniform mat4 uView;" +
+ "uniform mat4 uProjection;" +
+ "uniform float uPointSize;" +
+ "varying vec2 vTextureCoord;"+
+
+ "void main(void) {" +
+ " gl_PointSize = uPointSize;" +
+ " vFrontColor = uColor;" +
+ " gl_Position = uProjection * uView * uModel * vec4(aVertex, 1.0);" +
+ " vTextureCoord = aTextureCoord;" +
+ "}";
+
+ var fragmentShaderSrc2D =
+ "#ifdef GL_ES\n" +
+ "precision highp float;\n" +
+ "#endif\n" +
+
+ "varying vec4 vFrontColor;" +
+ "varying vec2 vTextureCoord;"+
+
+ "uniform sampler2D uSampler;"+
+ "uniform int uIsDrawingText;"+
+ "uniform bool uSmooth;" +
+
+ "void main(void){" +
+ // WebGL does not support POINT_SMOOTH, so we do it ourselves
+ " if(uSmooth == true){" +
+ " float dist = distance(gl_PointCoord, vec2(0.5));" +
+ " if(dist > 0.5){" +
+ " discard;" +
+ " }" +
+ " }" +
+
+ " if(uIsDrawingText == 1){" +
+ " float alpha = texture2D(uSampler, vTextureCoord).a;"+
+ " gl_FragColor = vec4(vFrontColor.rgb * alpha, alpha);"+
+ " }" +
+ " else{" +
+ " gl_FragColor = vFrontColor;" +
+ " }" +
+ "}";
+
+ var webglMaxTempsWorkaround = /Windows/.test(navigator.userAgent);
+
+ // Vertex shader for boxes and spheres.
+ var vertexShaderSrc3D =
+ "varying vec4 vFrontColor;" +
+
+ "attribute vec3 aVertex;" +
+ "attribute vec3 aNormal;" +
+ "attribute vec4 aColor;" +
+ "attribute vec2 aTexture;" +
+ "varying vec2 vTexture;" +
+
+ "uniform vec4 uColor;" +
+
+ "uniform bool uUsingMat;" +
+ "uniform vec3 uSpecular;" +
+ "uniform vec3 uMaterialEmissive;" +
+ "uniform vec3 uMaterialAmbient;" +
+ "uniform vec3 uMaterialSpecular;" +
+ "uniform float uShininess;" +
+
+ "uniform mat4 uModel;" +
+ "uniform mat4 uView;" +
+ "uniform mat4 uProjection;" +
+ "uniform mat4 uNormalTransform;" +
+
+ "uniform int uLightCount;" +
+ "uniform vec3 uFalloff;" +
+
+ // Careful changing the order of these fields. Some cards
+ // have issues with memory alignment.
+ "struct Light {" +
+ " int type;" +
+ " vec3 color;" +
+ " vec3 position;" +
+ " vec3 direction;" +
+ " float angle;" +
+ " vec3 halfVector;" +
+ " float concentration;" +
+ "};" +
+
+ // nVidia cards have issues with arrays of structures
+ // so instead we create 8 instances of Light.
+ "uniform Light uLights0;" +
+ "uniform Light uLights1;" +
+ "uniform Light uLights2;" +
+ "uniform Light uLights3;" +
+ "uniform Light uLights4;" +
+ "uniform Light uLights5;" +
+ "uniform Light uLights6;" +
+ "uniform Light uLights7;" +
+
+ // GLSL does not support switch.
+ "Light getLight(int index){" +
+ " if(index == 0) return uLights0;" +
+ " if(index == 1) return uLights1;" +
+ " if(index == 2) return uLights2;" +
+ " if(index == 3) return uLights3;" +
+ " if(index == 4) return uLights4;" +
+ " if(index == 5) return uLights5;" +
+ " if(index == 6) return uLights6;" +
+ // Do not use a conditional for the last return statement
+ // because some video cards will fail and complain that
+ // "not all paths return".
+ " return uLights7;" +
+ "}" +
+
+ "void AmbientLight( inout vec3 totalAmbient, in vec3 ecPos, in Light light ) {" +
+ // Get the vector from the light to the vertex and
+ // get the distance from the current vector to the light position.
+ " float d = length( light.position - ecPos );" +
+ " float attenuation = 1.0 / ( uFalloff[0] + ( uFalloff[1] * d ) + ( uFalloff[2] * d * d ));" +
+ " totalAmbient += light.color * attenuation;" +
+ "}" +
+
+ /*
+ col - accumulated color
+ spec - accumulated specular highlight
+ vertNormal - Normal of the vertex
+ ecPos - eye coordinate position
+ light - light structure
+ */
+ "void DirectionalLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" +
+ " float powerFactor = 0.0;" +
+ " float nDotVP = max(0.0, dot( vertNormal, normalize(-light.position) ));" +
+ " float nDotVH = max(0.0, dot( vertNormal, normalize(-light.position-normalize(ecPos) )));" +
+
+ " if( nDotVP != 0.0 ){" +
+ " powerFactor = pow( nDotVH, uShininess );" +
+ " }" +
+
+ " col += light.color * nDotVP;" +
+ " spec += uSpecular * powerFactor;" +
+ "}" +
+
+ /*
+ col - accumulated color
+ spec - accumulated specular highlight
+ vertNormal - Normal of the vertex
+ ecPos - eye coordinate position
+ light - light structure
+ */
+ "void PointLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" +
+ " float powerFactor;" +
+
+ // Get the vector from the light to the vertex.
+ " vec3 VP = light.position - ecPos;" +
+
+ // Get the distance from the current vector to the light position.
+ " float d = length( VP ); " +
+
+ // Normalize the light ray so it can be used in the dot product operation.
+ " VP = normalize( VP );" +
+
+ " float attenuation = 1.0 / ( uFalloff[0] + ( uFalloff[1] * d ) + ( uFalloff[2] * d * d ));" +
+
+ " float nDotVP = max( 0.0, dot( vertNormal, VP ));" +
+ " vec3 halfVector = normalize( VP - normalize(ecPos) );" +
+ " float nDotHV = max( 0.0, dot( vertNormal, halfVector ));" +
+
+ " if( nDotVP == 0.0 ) {" +
+ " powerFactor = 0.0;" +
+ " }" +
+ " else {" +
+ " powerFactor = pow( nDotHV, uShininess );" +
+ " }" +
+
+ " spec += uSpecular * powerFactor * attenuation;" +
+ " col += light.color * nDotVP * attenuation;" +
+ "}" +
+
+ /*
+ col - accumulated color
+ spec - accumulated specular highlight
+ vertNormal - Normal of the vertex
+ ecPos - eye coordinate position
+ light - light structure
+ */
+ "void SpotLight( inout vec3 col, inout vec3 spec, in vec3 vertNormal, in vec3 ecPos, in Light light ) {" +
+ " float spotAttenuation;" +
+ " float powerFactor = 0.0;" +
+
+ // Calculate the vector from the current vertex to the light.
+ " vec3 VP = light.position - ecPos;" +
+ " vec3 ldir = normalize( -light.direction );" +
+
+ // Get the distance from the spotlight and the vertex
+ " float d = length( VP );" +
+ " VP = normalize( VP );" +
+
+ " float attenuation = 1.0 / ( uFalloff[0] + ( uFalloff[1] * d ) + ( uFalloff[2] * d * d ) );" +
+
+ // Dot product of the vector from vertex to light and light direction.
+ " float spotDot = dot( VP, ldir );" +
+
+ // If the vertex falls inside the cone
+ (webglMaxTempsWorkaround ? // Windows reports max temps error if light.angle is used
+ " spotAttenuation = 1.0; " :
+ " if( spotDot > cos( light.angle ) ) {" +
+ " spotAttenuation = pow( spotDot, light.concentration );" +
+ " }" +
+ " else{" +
+ " spotAttenuation = 0.0;" +
+ " }" +
+ " attenuation *= spotAttenuation;" +
+ "") +
+
+ " float nDotVP = max( 0.0, dot( vertNormal, VP ) );" +
+ " vec3 halfVector = normalize( VP - normalize(ecPos) );" +
+ " float nDotHV = max( 0.0, dot( vertNormal, halfVector ) );" +
+
+ " if( nDotVP != 0.0 ) {" +
+ " powerFactor = pow( nDotHV, uShininess );" +
+ " }" +
+
+ " spec += uSpecular * powerFactor * attenuation;" +
+ " col += light.color * nDotVP * attenuation;" +
+ "}" +
+
+ "void main(void) {" +
+ " vec3 finalAmbient = vec3( 0.0 );" +
+ " vec3 finalDiffuse = vec3( 0.0 );" +
+ " vec3 finalSpecular = vec3( 0.0 );" +
+
+ " vec4 col = uColor;" +
+
+ " if ( uColor[0] == -1.0 ){" +
+ " col = aColor;" +
+ " }" +
+
+ // We use the sphere vertices as the normals when we create the sphere buffer.
+ // But this only works if the sphere vertices are unit length, so we
+ // have to normalize the normals here. Since this is only required for spheres
+ // we could consider placing this in a conditional later on.
+ " vec3 norm = normalize(vec3( uNormalTransform * vec4( aNormal, 0.0 ) ));" +
+
+ " vec4 ecPos4 = uView * uModel * vec4(aVertex, 1.0);" +
+ " vec3 ecPos = (vec3(ecPos4))/ecPos4.w;" +
+
+ // If there were no lights this draw call, just use the
+ // assigned fill color of the shape and the specular value.
+ " if( uLightCount == 0 ) {" +
+ " vFrontColor = col + vec4(uMaterialSpecular, 1.0);" +
+ " }" +
+ " else {" +
+ // WebGL forces us to iterate over a constant value
+ // so we can't iterate using lightCount.
+ " for( int i = 0; i < 8; i++ ) {" +
+ " Light l = getLight(i);" +
+
+ // We can stop iterating if we know we have gone past
+ // the number of lights which are actually on. This gives us a
+ // significant performance increase with high vertex counts.
+ " if( i >= uLightCount ){" +
+ " break;" +
+ " }" +
+
+ " if( l.type == 0 ) {" +
+ " AmbientLight( finalAmbient, ecPos, l );" +
+ " }" +
+ " else if( l.type == 1 ) {" +
+ " DirectionalLight( finalDiffuse, finalSpecular, norm, ecPos, l );" +
+ " }" +
+ " else if( l.type == 2 ) {" +
+ " PointLight( finalDiffuse, finalSpecular, norm, ecPos, l );" +
+ " }" +
+ " else {" +
+ " SpotLight( finalDiffuse, finalSpecular, norm, ecPos, l );" +
+ " }" +
+ " }" +
+
+ " if( uUsingMat == false ) {" +
+ " vFrontColor = vec4(" +
+ " vec3( col ) * finalAmbient +" +
+ " vec3( col ) * finalDiffuse +" +
+ " vec3( col ) * finalSpecular," +
+ " col[3] );" +
+ " }" +
+ " else{" +
+ " vFrontColor = vec4( " +
+ " uMaterialEmissive + " +
+ " (vec3(col) * uMaterialAmbient * finalAmbient ) + " +
+ " (vec3(col) * finalDiffuse) + " +
+ " (uMaterialSpecular * finalSpecular), " +
+ " col[3] );" +
+ " }" +
+ " }" +
+
+ " vTexture.xy = aTexture.xy;" +
+ " gl_Position = uProjection * uView * uModel * vec4( aVertex, 1.0 );" +
+ "}";
+
+ var fragmentShaderSrc3D =
+ "#ifdef GL_ES\n" +
+ "precision highp float;\n" +
+ "#endif\n" +
+
+ "varying vec4 vFrontColor;" +
+
+ "uniform sampler2D uSampler;" +
+ "uniform bool uUsingTexture;" +
+ "varying vec2 vTexture;" +
+
+ // In Processing, when a texture is used, the fill color is ignored
+ // vec4(1.0,1.0,1.0,0.5)
+ "void main(void){" +
+ " if( uUsingTexture ){" +
+ " gl_FragColor = vec4(texture2D(uSampler, vTexture.xy)) * vFrontColor;" +
+ " }"+
+ " else{" +
+ " gl_FragColor = vFrontColor;" +
+ " }" +
+ "}";
+
+ ////////////////////////////////////////////////////////////////////////////
+ // 3D Functions
+ ////////////////////////////////////////////////////////////////////////////
+
+ /*
+ * Sets a uniform variable in a program object to a particular
+ * value. Before calling this function, ensure the correct
+ * program object has been installed as part of the current
+ * rendering state by calling useProgram.
+ *
+ * On some systems, if the variable exists in the shader but isn't used,
+ * the compiler will optimize it out and this function will fail.
+ *
+ * @param {String} cacheId
+ * @param {WebGLProgram} programObj program object returned from
+ * createProgramObject
+ * @param {String} varName the name of the variable in the shader
+ * @param {float | Array} varValue either a scalar value or an Array
+ *
+ * @returns none
+ *
+ * @see uniformi
+ * @see uniformMatrix
+ */
+ function uniformf(cacheId, programObj, varName, varValue) {
+ var varLocation = curContextCache.locations[cacheId];
+ if(varLocation === undef) {
+ varLocation = curContext.getUniformLocation(programObj, varName);
+ curContextCache.locations[cacheId] = varLocation;
+ }
+ // the variable won't be found if it was optimized out.
+ if (varLocation !== null) {
+ if (varValue.length === 4) {
+ curContext.uniform4fv(varLocation, varValue);
+ } else if (varValue.length === 3) {
+ curContext.uniform3fv(varLocation, varValue);
+ } else if (varValue.length === 2) {
+ curContext.uniform2fv(varLocation, varValue);
+ } else {
+ curContext.uniform1f(varLocation, varValue);
+ }
+ }
+ }
+
+ /**
+ * Sets a uniform int or int array in a program object to a particular
+ * value. Before calling this function, ensure the correct
+ * program object has been installed as part of the current
+ * rendering state.
+ *
+ * On some systems, if the variable exists in the shader but isn't used,
+ * the compiler will optimize it out and this function will fail.
+ *
+ * @param {String} cacheId
+ * @param {WebGLProgram} programObj program object returned from
+ * createProgramObject
+ * @param {String} varName the name of the variable in the shader
+ * @param {int | Array} varValue either a scalar value or an Array
+ *
+ * @returns none
+ *
+ * @see uniformf
+ * @see uniformMatrix
+ */
+ function uniformi(cacheId, programObj, varName, varValue) {
+ var varLocation = curContextCache.locations[cacheId];
+ if(varLocation === undef) {
+ varLocation = curContext.getUniformLocation(programObj, varName);
+ curContextCache.locations[cacheId] = varLocation;
+ }
+ // the variable won't be found if it was optimized out.
+ if (varLocation !== null) {
+ if (varValue.length === 4) {
+ curContext.uniform4iv(varLocation, varValue);
+ } else if (varValue.length === 3) {
+ curContext.uniform3iv(varLocation, varValue);
+ } else if (varValue.length === 2) {
+ curContext.uniform2iv(varLocation, varValue);
+ } else {
+ curContext.uniform1i(varLocation, varValue);
+ }
+ }
+ }
+
+ /**
+ * Sets the value of a uniform matrix variable in a program
+ * object. Before calling this function, ensure the correct
+ * program object has been installed as part of the current
+ * rendering state.
+ *
+ * On some systems, if the variable exists in the shader but
+ * isn't used, the compiler will optimize it out and this
+ * function will fail.
+ *
+ * @param {String} cacheId
+ * @param {WebGLProgram} programObj program object returned from
+ * createProgramObject
+ * @param {String} varName the name of the variable in the shader
+ * @param {boolean} transpose must be false
+ * @param {Array} matrix an array of 4, 9 or 16 values
+ *
+ * @returns none
+ *
+ * @see uniformi
+ * @see uniformf
+ */
+ function uniformMatrix(cacheId, programObj, varName, transpose, matrix) {
+ var varLocation = curContextCache.locations[cacheId];
+ if(varLocation === undef) {
+ varLocation = curContext.getUniformLocation(programObj, varName);
+ curContextCache.locations[cacheId] = varLocation;
+ }
+ // The variable won't be found if it was optimized out.
+ if (varLocation !== -1) {
+ if (matrix.length === 16) {
+ curContext.uniformMatrix4fv(varLocation, transpose, matrix);
+ } else if (matrix.length === 9) {
+ curContext.uniformMatrix3fv(varLocation, transpose, matrix);
+ } else {
+ curContext.uniformMatrix2fv(varLocation, transpose, matrix);
+ }
+ }
+ }
+
+ /**
+ * Binds the VBO, sets the vertex attribute data for the program
+ * object and enables the attribute.
+ *
+ * On some systems, if the attribute exists in the shader but
+ * isn't used, the compiler will optimize it out and this
+ * function will fail.
+ *
+ * @param {String} cacheId
+ * @param {WebGLProgram} programObj program object returned from
+ * createProgramObject
+ * @param {String} varName the name of the variable in the shader
+ * @param {int} size the number of components per vertex attribute
+ * @param {WebGLBuffer} VBO Vertex Buffer Object
+ *
+ * @returns none
+ *
+ * @see disableVertexAttribPointer
+ */
+ function vertexAttribPointer(cacheId, programObj, varName, size, VBO) {
+ var varLocation = curContextCache.attributes[cacheId];
+ if(varLocation === undef) {
+ varLocation = curContext.getAttribLocation(programObj, varName);
+ curContextCache.attributes[cacheId] = varLocation;
+ }
+ if (varLocation !== -1) {
+ curContext.bindBuffer(curContext.ARRAY_BUFFER, VBO);
+ curContext.vertexAttribPointer(varLocation, size, curContext.FLOAT, false, 0, 0);
+ curContext.enableVertexAttribArray(varLocation);
+ }
+ }
+
+ /**
+ * Disables a program object attribute from being sent to WebGL.
+ *
+ * @param {String} cacheId
+ * @param {WebGLProgram} programObj program object returned from
+ * createProgramObject
+ * @param {String} varName name of the attribute
+ *
+ * @returns none
+ *
+ * @see vertexAttribPointer
+ */
+ function disableVertexAttribPointer(cacheId, programObj, varName){
+ var varLocation = curContextCache.attributes[cacheId];
+ if(varLocation === undef) {
+ varLocation = curContext.getAttribLocation(programObj, varName);
+ curContextCache.attributes[cacheId] = varLocation;
+ }
+ if (varLocation !== -1) {
+ curContext.disableVertexAttribArray(varLocation);
+ }
+ }
+
+ /**
+ * Creates a WebGL program object.
+ *
+ * @param {String} vetexShaderSource
+ * @param {String} fragmentShaderSource
+ *
+ * @returns {WebGLProgram} A program object
+ */
+ var createProgramObject = function(curContext, vetexShaderSource, fragmentShaderSource) {
+ var vertexShaderObject = curContext.createShader(curContext.VERTEX_SHADER);
+ curContext.shaderSource(vertexShaderObject, vetexShaderSource);
+ curContext.compileShader(vertexShaderObject);
+ if (!curContext.getShaderParameter(vertexShaderObject, curContext.COMPILE_STATUS)) {
+ throw curContext.getShaderInfoLog(vertexShaderObject);
+ }
+
+ var fragmentShaderObject = curContext.createShader(curContext.FRAGMENT_SHADER);
+ curContext.shaderSource(fragmentShaderObject, fragmentShaderSource);
+ curContext.compileShader(fragmentShaderObject);
+ if (!curContext.getShaderParameter(fragmentShaderObject, curContext.COMPILE_STATUS)) {
+ throw curContext.getShaderInfoLog(fragmentShaderObject);
+ }
+
+ var programObject = curContext.createProgram();
+ curContext.attachShader(programObject, vertexShaderObject);
+ curContext.attachShader(programObject, fragmentShaderObject);
+ curContext.linkProgram(programObject);
+ if (!curContext.getProgramParameter(programObject, curContext.LINK_STATUS)) {
+ throw "Error linking shaders.";
+ }
+
+ return programObject;
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // 2D/3D drawing handling
+ ////////////////////////////////////////////////////////////////////////////
+ var imageModeCorner = function(x, y, w, h, whAreSizes) {
+ return {
+ x: x,
+ y: y,
+ w: w,
+ h: h
+ };
+ };
+ var imageModeConvert = imageModeCorner;
+
+ var imageModeCorners = function(x, y, w, h, whAreSizes) {
+ return {
+ x: x,
+ y: y,
+ w: whAreSizes ? w : w - x,
+ h: whAreSizes ? h : h - y
+ };
+ };
+
+ var imageModeCenter = function(x, y, w, h, whAreSizes) {
+ return {
+ x: x - w / 2,
+ y: y - h / 2,
+ w: w,
+ h: h
+ };
+ };
+
+ // Objects for shared, 2D and 3D contexts
+ var DrawingShared = function(){};
+ var Drawing2D = function(){};
+ var Drawing3D = function(){};
+ var DrawingPre = function(){};
+
+ // Setup the prototype chain
+ Drawing2D.prototype = new DrawingShared();
+ Drawing2D.prototype.constructor = Drawing2D;
+ Drawing3D.prototype = new DrawingShared();
+ Drawing3D.prototype.constructor = Drawing3D;
+ DrawingPre.prototype = new DrawingShared();
+ DrawingPre.prototype.constructor = DrawingPre;
+
+ // A no-op function for when the user calls 3D functions from a 2D sketch
+ // We can change this to a throw or console.error() later if we want
+ DrawingShared.prototype.a3DOnlyFunction = noop;
+
+ /**
+ * The shape() function displays shapes to the screen.
+ * Processing currently works with SVG shapes only.
+ * The <b>shape</b> parameter specifies the shape to display and the <b>x</b>
+ * and <b>y</b> parameters define the location of the shape from its
+ * upper-left corner.
+ * The shape is displayed at its original size unless the <b>width</b>
+ * and <b>height</b> parameters specify a different size.
+ * The <b>shapeMode()</b> function changes the way the parameters work.
+ * A call to <b>shapeMode(CORNERS)</b>, for example, will change the width
+ * and height parameters to define the x and y values of the opposite corner
+ * of the shape.
+ * <br><br>
+ * Note complex shapes may draw awkwardly with P2D, P3D, and OPENGL. Those
+ * renderers do not yet support shapes that have holes or complicated breaks.
+ *
+ * @param {PShape} shape the shape to display
+ * @param {int|float} x x-coordinate of the shape
+ * @param {int|float} y y-coordinate of the shape
+ * @param {int|float} width width to display the shape
+ * @param {int|float} height height to display the shape
+ *
+ * @see PShape
+ * @see loadShape()
+ * @see shapeMode()
+ */
+ p.shape = function(shape, x, y, width, height) {
+ if (arguments.length >= 1 && arguments[0] !== null) {
+ if (shape.isVisible()) {
+ p.pushMatrix();
+ if (curShapeMode === PConstants.CENTER) {
+ if (arguments.length === 5) {
+ p.translate(x - width/2, y - height/2);
+ p.scale(width / shape.getWidth(), height / shape.getHeight());
+ } else if (arguments.length === 3) {
+ p.translate(x - shape.getWidth()/2, - shape.getHeight()/2);
+ } else {
+ p.translate(-shape.getWidth()/2, -shape.getHeight()/2);
+ }
+ } else if (curShapeMode === PConstants.CORNER) {
+ if (arguments.length === 5) {
+ p.translate(x, y);
+ p.scale(width / shape.getWidth(), height / shape.getHeight());
+ } else if (arguments.length === 3) {
+ p.translate(x, y);
+ }
+ } else if (curShapeMode === PConstants.CORNERS) {
+ if (arguments.length === 5) {
+ width -= x;
+ height -= y;
+ p.translate(x, y);
+ p.scale(width / shape.getWidth(), height / shape.getHeight());
+ } else if (arguments.length === 3) {
+ p.translate(x, y);
+ }
+ }
+ shape.draw(p);
+ if ((arguments.length === 1 && curShapeMode === PConstants.CENTER ) || arguments.length > 1) {
+ p.popMatrix();
+ }
+ }
+ }
+ };
+
+ /**
+ * The shapeMode() function modifies the location from which shapes draw.
+ * The default mode is <b>shapeMode(CORNER)</b>, which specifies the
+ * location to be the upper left corner of the shape and uses the third
+ * and fourth parameters of <b>shape()</b> to specify the width and height.
+ * The syntax <b>shapeMode(CORNERS)</b> uses the first and second parameters
+ * of <b>shape()</b> to set the location of one corner and uses the third
+ * and fourth parameters to set the opposite corner.
+ * The syntax <b>shapeMode(CENTER)</b> draws the shape from its center point
+ * and uses the third and forth parameters of <b>shape()</b> to specify the
+ * width and height.
+ * The parameter must be written in "ALL CAPS" because Processing syntax
+ * is case sensitive.
+ *
+ * @param {int} mode One of CORNER, CORNERS, CENTER
+ *
+ * @see shape()
+ * @see rectMode()
+ */
+ p.shapeMode = function (mode) {
+ curShapeMode = mode;
+ };
+
+ /**
+ * The loadShape() function loads vector shapes into a variable of type PShape. Currently, only SVG files may be loaded.
+ * In most cases, <b>loadShape()</b> should be used inside <b>setup()</b> because loading shapes inside <b>draw()</b> will reduce the speed of a sketch.
+ *
+ * @param {String} filename an SVG file
+ *
+ * @return {PShape} a object of type PShape or null
+ * @see PShape
+ * @see PApplet#shape()
+ * @see PApplet#shapeMode()
+ */
+ p.loadShape = function (filename) {
+ if (arguments.length === 1) {
+ if (filename.indexOf(".svg") > -1) {
+ return new PShapeSVG(null, filename);
+ }
+ }
+ return null;
+ };
+
+ /**
+ * Processing 2.0 function for loading XML files.
+ *
+ * @param {String} uri The uri for the xml file to load.
+ *
+ * @return {XML} An XML object representing the xml data.
+ */
+ p.loadXML = function(uri) {
+ return new XML(p, uri);
+ };
+
+ /**
+ * Processing 2.0 function for creating XML elements from string
+ *
+ * @param {String} xml the XML source code
+ *
+ * @return {XML} An XML object representation of the input XML markup.
+ */
+ p.parseXML = function(xmlstring) {
+ var element = new XML();
+ element.parse(xmlstring);
+ return element;
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // 2D Matrix
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Helper function for printMatrix(). Finds the largest scalar
+ * in the matrix, then number of digits left of the decimal.
+ * Call from PMatrix2D and PMatrix3D's print() function.
+ */
+ var printMatrixHelper = function(elements) {
+ var big = 0;
+ for (var i = 0; i < elements.length; i++) {
+ if (i !== 0) {
+ big = Math.max(big, Math.abs(elements[i]));
+ } else {
+ big = Math.abs(elements[i]);
+ }
+ }
+
+ var digits = (big + "").indexOf(".");
+ if (digits === 0) {
+ digits = 1;
+ } else if (digits === -1) {
+ digits = (big + "").length;
+ }
+
+ return digits;
+ };
+ /**
+ * PMatrix2D is a 3x2 affine matrix implementation. The constructor accepts another PMatrix2D or a list of six float elements.
+ * If no parameters are provided the matrix is set to the identity matrix.
+ *
+ * @param {PMatrix2D} matrix the initial matrix to set to
+ * @param {float} m00 the first element of the matrix
+ * @param {float} m01 the second element of the matrix
+ * @param {float} m02 the third element of the matrix
+ * @param {float} m10 the fourth element of the matrix
+ * @param {float} m11 the fifth element of the matrix
+ * @param {float} m12 the sixth element of the matrix
+ */
+ var PMatrix2D = p.PMatrix2D = function() {
+ if (arguments.length === 0) {
+ this.reset();
+ } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
+ this.set(arguments[0].array());
+ } else if (arguments.length === 6) {
+ this.set(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
+ }
+ };
+ /**
+ * PMatrix2D methods
+ */
+ PMatrix2D.prototype = {
+ /**
+ * @member PMatrix2D
+ * The set() function sets the matrix elements. The function accepts either another PMatrix2D, an array of elements, or a list of six floats.
+ *
+ * @param {PMatrix2D} matrix the matrix to set this matrix to
+ * @param {float[]} elements an array of elements to set this matrix to
+ * @param {float} m00 the first element of the matrix
+ * @param {float} m01 the third element of the matrix
+ * @param {float} m10 the fourth element of the matrix
+ * @param {float} m11 the fith element of the matrix
+ * @param {float} m12 the sixth element of the matrix
+ */
+ set: function() {
+ if (arguments.length === 6) {
+ var a = arguments;
+ this.set([a[0], a[1], a[2],
+ a[3], a[4], a[5]]);
+ } else if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
+ this.elements = arguments[0].array();
+ } else if (arguments.length === 1 && arguments[0] instanceof Array) {
+ this.elements = arguments[0].slice();
+ }
+ },
+ /**
+ * @member PMatrix2D
+ * The get() function returns a copy of this PMatrix2D.
+ *
+ * @return {PMatrix2D} a copy of this PMatrix2D
+ */
+ get: function() {
+ var outgoing = new PMatrix2D();
+ outgoing.set(this.elements);
+ return outgoing;
+ },
+ /**
+ * @member PMatrix2D
+ * The reset() function sets this PMatrix2D to the identity matrix.
+ */
+ reset: function() {
+ this.set([1, 0, 0, 0, 1, 0]);
+ },
+ /**
+ * @member PMatrix2D
+ * The array() function returns a copy of the element values.
+ * @addon
+ *
+ * @return {float[]} returns a copy of the element values
+ */
+ array: function array() {
+ return this.elements.slice();
+ },
+ /**
+ * @member PMatrix2D
+ * The translate() function translates this matrix by moving the current coordinates to the location specified by tx and ty.
+ *
+ * @param {float} tx the x-axis coordinate to move to
+ * @param {float} ty the y-axis coordinate to move to
+ */
+ translate: function(tx, ty) {
+ this.elements[2] = tx * this.elements[0] + ty * this.elements[1] + this.elements[2];
+ this.elements[5] = tx * this.elements[3] + ty * this.elements[4] + this.elements[5];
+ },
+ /**
+ * @member PMatrix2D
+ * The invTranslate() function translates this matrix by moving the current coordinates to the negative location specified by tx and ty.
+ *
+ * @param {float} tx the x-axis coordinate to move to
+ * @param {float} ty the y-axis coordinate to move to
+ */
+ invTranslate: function(tx, ty) {
+ this.translate(-tx, -ty);
+ },
+ /**
+ * @member PMatrix2D
+ * The transpose() function is not used in processingjs.
+ */
+ transpose: function() {
+ // Does nothing in Processing.
+ },
+ /**
+ * @member PMatrix2D
+ * The mult() function multiplied this matrix.
+ * If two array elements are passed in the function will multiply a two element vector against this matrix.
+ * If target is null or not length four, a new float array will be returned.
+ * The values for vec and target can be the same (though that's less efficient).
+ * If two PVectors are passed in the function multiply the x and y coordinates of a PVector against this matrix.
+ *
+ * @param {PVector} source, target the PVectors used to multiply this matrix
+ * @param {float[]} source, target the arrays used to multiply this matrix
+ *
+ * @return {PVector|float[]} returns a PVector or an array representing the new matrix
+ */
+ mult: function(source, target) {
+ var x, y;
+ if (source instanceof PVector) {
+ x = source.x;
+ y = source.y;
+ if (!target) {
+ target = new PVector();
+ }
+ } else if (source instanceof Array) {
+ x = source[0];
+ y = source[1];
+ if (!target) {
+ target = [];
+ }
+ }
+ if (target instanceof Array) {
+ target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2];
+ target[1] = this.elements[3] * x + this.elements[4] * y + this.elements[5];
+ } else if (target instanceof PVector) {
+ target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2];
+ target.y = this.elements[3] * x + this.elements[4] * y + this.elements[5];
+ target.z = 0;
+ }
+ return target;
+ },
+ /**
+ * @member PMatrix2D
+ * The multX() function calculates the x component of a vector from a transformation.
+ *
+ * @param {float} x the x component of the vector being transformed
+ * @param {float} y the y component of the vector being transformed
+ *
+ * @return {float} returnes the result of the calculation
+ */
+ multX: function(x, y) {
+ return (x * this.elements[0] + y * this.elements[1] + this.elements[2]);
+ },
+ /**
+ * @member PMatrix2D
+ * The multY() function calculates the y component of a vector from a transformation.
+ *
+ * @param {float} x the x component of the vector being transformed
+ * @param {float} y the y component of the vector being transformed
+ *
+ * @return {float} returnes the result of the calculation
+ */
+ multY: function(x, y) {
+ return (x * this.elements[3] + y * this.elements[4] + this.elements[5]);
+ },
+ /**
+ * @member PMatrix2D
+ * The skewX() function skews the matrix along the x-axis the amount specified by the angle parameter.
+ * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
+ *
+ * @param {float} angle angle of skew specified in radians
+ */
+ skewX: function(angle) {
+ this.apply(1, 0, 1, angle, 0, 0);
+ },
+ /**
+ * @member PMatrix2D
+ * The skewY() function skews the matrix along the y-axis the amount specified by the angle parameter.
+ * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
+ *
+ * @param {float} angle angle of skew specified in radians
+ */
+ skewY: function(angle) {
+ this.apply(1, 0, 1, 0, angle, 0);
+ },
+ /**
+ * @member PMatrix2D
+ * The shearX() function shears the matrix along the x-axis the amount specified by the angle parameter.
+ * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
+ *
+ * @param {float} angle angle of skew specified in radians
+ */
+ shearX: function(angle) {
+ this.apply(1, 0, 1, Math.tan(angle) , 0, 0);
+ },
+ /**
+ * @member PMatrix2D
+ * The shearY() function shears the matrix along the y-axis the amount specified by the angle parameter.
+ * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
+ *
+ * @param {float} angle angle of skew specified in radians
+ */
+ shearY: function(angle) {
+ this.apply(1, 0, 1, 0, Math.tan(angle), 0);
+ },
+ /**
+ * @member PMatrix2D
+ * The determinant() function calvculates the determinant of this matrix.
+ *
+ * @return {float} the determinant of the matrix
+ */
+ determinant: function() {
+ return (this.elements[0] * this.elements[4] - this.elements[1] * this.elements[3]);
+ },
+ /**
+ * @member PMatrix2D
+ * The invert() function inverts this matrix
+ *
+ * @return {boolean} true if successful
+ */
+ invert: function() {
+ var d = this.determinant();
+ if (Math.abs( d ) > PConstants.MIN_INT) {
+ var old00 = this.elements[0];
+ var old01 = this.elements[1];
+ var old02 = this.elements[2];
+ var old10 = this.elements[3];
+ var old11 = this.elements[4];
+ var old12 = this.elements[5];
+ this.elements[0] = old11 / d;
+ this.elements[3] = -old10 / d;
+ this.elements[1] = -old01 / d;
+ this.elements[4] = old00 / d;
+ this.elements[2] = (old01 * old12 - old11 * old02) / d;
+ this.elements[5] = (old10 * old02 - old00 * old12) / d;
+ return true;
+ }
+ return false;
+ },
+ /**
+ * @member PMatrix2D
+ * The scale() function increases or decreases the size of a shape by expanding and contracting vertices. When only one parameter is specified scale will occur in all dimensions.
+ * This is equivalent to a two parameter call.
+ *
+ * @param {float} sx the amount to scale on the x-axis
+ * @param {float} sy the amount to scale on the y-axis
+ */
+ scale: function(sx, sy) {
+ if (sx && !sy) {
+ sy = sx;
+ }
+ if (sx && sy) {
+ this.elements[0] *= sx;
+ this.elements[1] *= sy;
+ this.elements[3] *= sx;
+ this.elements[4] *= sy;
+ }
+ },
+ /**
+ * @member PMatrix2D
+ * The invScale() function decreases or increases the size of a shape by contracting and expanding vertices. When only one parameter is specified scale will occur in all dimensions.
+ * This is equivalent to a two parameter call.
+ *
+ * @param {float} sx the amount to scale on the x-axis
+ * @param {float} sy the amount to scale on the y-axis
+ */
+ invScale: function(sx, sy) {
+ if (sx && !sy) {
+ sy = sx;
+ }
+ this.scale(1 / sx, 1 / sy);
+ },
+ /**
+ * @member PMatrix2D
+ * The apply() function multiplies the current matrix by the one specified through the parameters. Note that either a PMatrix2D or a list of floats can be passed in.
+ *
+ * @param {PMatrix2D} matrix the matrix to apply this matrix to
+ * @param {float} m00 the first element of the matrix
+ * @param {float} m01 the third element of the matrix
+ * @param {float} m10 the fourth element of the matrix
+ * @param {float} m11 the fith element of the matrix
+ * @param {float} m12 the sixth element of the matrix
+ */
+ apply: function() {
+ var source;
+ if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
+ source = arguments[0].array();
+ } else if (arguments.length === 6) {
+ source = Array.prototype.slice.call(arguments);
+ } else if (arguments.length === 1 && arguments[0] instanceof Array) {
+ source = arguments[0];
+ }
+
+ var result = [0, 0, this.elements[2],
+ 0, 0, this.elements[5]];
+ var e = 0;
+ for (var row = 0; row < 2; row++) {
+ for (var col = 0; col < 3; col++, e++) {
+ result[e] += this.elements[row * 3 + 0] * source[col + 0] +
+ this.elements[row * 3 + 1] * source[col + 3];
+ }
+ }
+ this.elements = result.slice();
+ },
+ /**
+ * @member PMatrix2D
+ * The preApply() function applies another matrix to the left of this one. Note that either a PMatrix2D or elements of a matrix can be passed in.
+ *
+ * @param {PMatrix2D} matrix the matrix to apply this matrix to
+ * @param {float} m00 the first element of the matrix
+ * @param {float} m01 the third element of the matrix
+ * @param {float} m10 the fourth element of the matrix
+ * @param {float} m11 the fith element of the matrix
+ * @param {float} m12 the sixth element of the matrix
+ */
+ preApply: function() {
+ var source;
+ if (arguments.length === 1 && arguments[0] instanceof PMatrix2D) {
+ source = arguments[0].array();
+ } else if (arguments.length === 6) {
+ source = Array.prototype.slice.call(arguments);
+ } else if (arguments.length === 1 && arguments[0] instanceof Array) {
+ source = arguments[0];
+ }
+ var result = [0, 0, source[2],
+ 0, 0, source[5]];
+ result[2] = source[2] + this.elements[2] * source[0] + this.elements[5] * source[1];
+ result[5] = source[5] + this.elements[2] * source[3] + this.elements[5] * source[4];
+ result[0] = this.elements[0] * source[0] + this.elements[3] * source[1];
+ result[3] = this.elements[0] * source[3] + this.elements[3] * source[4];
+ result[1] = this.elements[1] * source[0] + this.elements[4] * source[1];
+ result[4] = this.elements[1] * source[3] + this.elements[4] * source[4];
+ this.elements = result.slice();
+ },
+ /**
+ * @member PMatrix2D
+ * The rotate() function rotates the matrix.
+ *
+ * @param {float} angle the angle of rotation in radiants
+ */
+ rotate: function(angle) {
+ var c = Math.cos(angle);
+ var s = Math.sin(angle);
+ var temp1 = this.elements[0];
+ var temp2 = this.elements[1];
+ this.elements[0] = c * temp1 + s * temp2;
+ this.elements[1] = -s * temp1 + c * temp2;
+ temp1 = this.elements[3];
+ temp2 = this.elements[4];
+ this.elements[3] = c * temp1 + s * temp2;
+ this.elements[4] = -s * temp1 + c * temp2;
+ },
+ /**
+ * @member PMatrix2D
+ * The rotateZ() function rotates the matrix.
+ *
+ * @param {float} angle the angle of rotation in radiants
+ */
+ rotateZ: function(angle) {
+ this.rotate(angle);
+ },
+ /**
+ * @member PMatrix2D
+ * The invRotateZ() function rotates the matrix in opposite direction.
+ *
+ * @param {float} angle the angle of rotation in radiants
+ */
+ invRotateZ: function(angle) {
+ this.rotateZ(angle - Math.PI);
+ },
+ /**
+ * @member PMatrix2D
+ * The print() function prints out the elements of this matrix
+ */
+ print: function() {
+ var digits = printMatrixHelper(this.elements);
+ var output = "" + p.nfs(this.elements[0], digits, 4) + " " +
+ p.nfs(this.elements[1], digits, 4) + " " +
+ p.nfs(this.elements[2], digits, 4) + "\n" +
+ p.nfs(this.elements[3], digits, 4) + " " +
+ p.nfs(this.elements[4], digits, 4) + " " +
+ p.nfs(this.elements[5], digits, 4) + "\n\n";
+ p.println(output);
+ }
+ };
+
+ /**
+ * PMatrix3D is a 4x4 matrix implementation. The constructor accepts another PMatrix3D or a list of six or sixteen float elements.
+ * If no parameters are provided the matrix is set to the identity matrix.
+ */
+ var PMatrix3D = p.PMatrix3D = function() {
+ // When a matrix is created, it is set to an identity matrix
+ this.reset();
+ };
+ /**
+ * PMatrix3D methods
+ */
+ PMatrix3D.prototype = {
+ /**
+ * @member PMatrix2D
+ * The set() function sets the matrix elements. The function accepts either another PMatrix3D, an array of elements, or a list of six or sixteen floats.
+ *
+ * @param {PMatrix3D} matrix the initial matrix to set to
+ * @param {float[]} elements an array of elements to set this matrix to
+ * @param {float} m00 the first element of the matrix
+ * @param {float} m01 the second element of the matrix
+ * @param {float} m02 the third element of the matrix
+ * @param {float} m03 the fourth element of the matrix
+ * @param {float} m10 the fifth element of the matrix
+ * @param {float} m11 the sixth element of the matrix
+ * @param {float} m12 the seventh element of the matrix
+ * @param {float} m13 the eight element of the matrix
+ * @param {float} m20 the nineth element of the matrix
+ * @param {float} m21 the tenth element of the matrix
+ * @param {float} m22 the eleventh element of the matrix
+ * @param {float} m23 the twelveth element of the matrix
+ * @param {float} m30 the thirteenth element of the matrix
+ * @param {float} m31 the fourtheenth element of the matrix
+ * @param {float} m32 the fivetheenth element of the matrix
+ * @param {float} m33 the sixteenth element of the matrix
+ */
+ set: function() {
+ if (arguments.length === 16) {
+ this.elements = Array.prototype.slice.call(arguments);
+ } else if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
+ this.elements = arguments[0].array();
+ } else if (arguments.length === 1 && arguments[0] instanceof Array) {
+ this.elements = arguments[0].slice();
+ }
+ },
+ /**
+ * @member PMatrix3D
+ * The get() function returns a copy of this PMatrix3D.
+ *
+ * @return {PMatrix3D} a copy of this PMatrix3D
+ */
+ get: function() {
+ var outgoing = new PMatrix3D();
+ outgoing.set(this.elements);
+ return outgoing;
+ },
+ /**
+ * @member PMatrix3D
+ * The reset() function sets this PMatrix3D to the identity matrix.
+ */
+ reset: function() {
+ this.elements = [1,0,0,0,
+ 0,1,0,0,
+ 0,0,1,0,
+ 0,0,0,1];
+ },
+ /**
+ * @member PMatrix3D
+ * The array() function returns a copy of the element values.
+ * @addon
+ *
+ * @return {float[]} returns a copy of the element values
+ */
+ array: function array() {
+ return this.elements.slice();
+ },
+ /**
+ * @member PMatrix3D
+ * The translate() function translates this matrix by moving the current coordinates to the location specified by tx, ty, and tz.
+ *
+ * @param {float} tx the x-axis coordinate to move to
+ * @param {float} ty the y-axis coordinate to move to
+ * @param {float} tz the z-axis coordinate to move to
+ */
+ translate: function(tx, ty, tz) {
+ if (tz === undef) {
+ tz = 0;
+ }
+
+ this.elements[3] += tx * this.elements[0] + ty * this.elements[1] + tz * this.elements[2];
+ this.elements[7] += tx * this.elements[4] + ty * this.elements[5] + tz * this.elements[6];
+ this.elements[11] += tx * this.elements[8] + ty * this.elements[9] + tz * this.elements[10];
+ this.elements[15] += tx * this.elements[12] + ty * this.elements[13] + tz * this.elements[14];
+ },
+ /**
+ * @member PMatrix3D
+ * The transpose() function transpose this matrix.
+ */
+ transpose: function() {
+ var temp = this.elements[4];
+ this.elements[4] = this.elements[1];
+ this.elements[1] = temp;
+
+ temp = this.elements[8];
+ this.elements[8] = this.elements[2];
+ this.elements[2] = temp;
+
+ temp = this.elements[6];
+ this.elements[6] = this.elements[9];
+ this.elements[9] = temp;
+
+ temp = this.elements[3];
+ this.elements[3] = this.elements[12];
+ this.elements[12] = temp;
+
+ temp = this.elements[7];
+ this.elements[7] = this.elements[13];
+ this.elements[13] = temp;
+
+ temp = this.elements[11];
+ this.elements[11] = this.elements[14];
+ this.elements[14] = temp;
+ },
+ /**
+ * @member PMatrix3D
+ * The mult() function multiplied this matrix.
+ * If two array elements are passed in the function will multiply a two element vector against this matrix.
+ * If target is null or not length four, a new float array will be returned.
+ * The values for vec and target can be the same (though that's less efficient).
+ * If two PVectors are passed in the function multiply the x and y coordinates of a PVector against this matrix.
+ *
+ * @param {PVector} source, target the PVectors used to multiply this matrix
+ * @param {float[]} source, target the arrays used to multiply this matrix
+ *
+ * @return {PVector|float[]} returns a PVector or an array representing the new matrix
+ */
+ mult: function(source, target) {
+ var x, y, z, w;
+ if (source instanceof PVector) {
+ x = source.x;
+ y = source.y;
+ z = source.z;
+ w = 1;
+ if (!target) {
+ target = new PVector();
+ }
+ } else if (source instanceof Array) {
+ x = source[0];
+ y = source[1];
+ z = source[2];
+ w = source[3] || 1;
+
+ if ( !target || (target.length !== 3 && target.length !== 4) ) {
+ target = [0, 0, 0];
+ }
+ }
+
+ if (target instanceof Array) {
+ if (target.length === 3) {
+ target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
+ target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
+ target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
+ } else if (target.length === 4) {
+ target[0] = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w;
+ target[1] = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w;
+ target[2] = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w;
+ target[3] = this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w;
+ }
+ }
+ if (target instanceof PVector) {
+ target.x = this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
+ target.y = this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
+ target.z = this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
+ }
+ return target;
+ },
+ /**
+ * @member PMatrix3D
+ * The preApply() function applies another matrix to the left of this one. Note that either a PMatrix3D or elements of a matrix can be passed in.
+ *
+ * @param {PMatrix3D} matrix the matrix to apply this matrix to
+ * @param {float} m00 the first element of the matrix
+ * @param {float} m01 the second element of the matrix
+ * @param {float} m02 the third element of the matrix
+ * @param {float} m03 the fourth element of the matrix
+ * @param {float} m10 the fifth element of the matrix
+ * @param {float} m11 the sixth element of the matrix
+ * @param {float} m12 the seventh element of the matrix
+ * @param {float} m13 the eight element of the matrix
+ * @param {float} m20 the nineth element of the matrix
+ * @param {float} m21 the tenth element of the matrix
+ * @param {float} m22 the eleventh element of the matrix
+ * @param {float} m23 the twelveth element of the matrix
+ * @param {float} m30 the thirteenth element of the matrix
+ * @param {float} m31 the fourtheenth element of the matrix
+ * @param {float} m32 the fivetheenth element of the matrix
+ * @param {float} m33 the sixteenth element of the matrix
+ */
+ preApply: function() {
+ var source;
+ if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
+ source = arguments[0].array();
+ } else if (arguments.length === 16) {
+ source = Array.prototype.slice.call(arguments);
+ } else if (arguments.length === 1 && arguments[0] instanceof Array) {
+ source = arguments[0];
+ }
+
+ var result = [0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0];
+ var e = 0;
+ for (var row = 0; row < 4; row++) {
+ for (var col = 0; col < 4; col++, e++) {
+ result[e] += this.elements[col + 0] * source[row * 4 + 0] + this.elements[col + 4] *
+ source[row * 4 + 1] + this.elements[col + 8] * source[row * 4 + 2] +
+ this.elements[col + 12] * source[row * 4 + 3];
+ }
+ }
+ this.elements = result.slice();
+ },
+ /**
+ * @member PMatrix3D
+ * The apply() function multiplies the current matrix by the one specified through the parameters. Note that either a PMatrix3D or a list of floats can be passed in.
+ *
+ * @param {PMatrix3D} matrix the matrix to apply this matrix to
+ * @param {float} m00 the first element of the matrix
+ * @param {float} m01 the second element of the matrix
+ * @param {float} m02 the third element of the matrix
+ * @param {float} m03 the fourth element of the matrix
+ * @param {float} m10 the fifth element of the matrix
+ * @param {float} m11 the sixth element of the matrix
+ * @param {float} m12 the seventh element of the matrix
+ * @param {float} m13 the eight element of the matrix
+ * @param {float} m20 the nineth element of the matrix
+ * @param {float} m21 the tenth element of the matrix
+ * @param {float} m22 the eleventh element of the matrix
+ * @param {float} m23 the twelveth element of the matrix
+ * @param {float} m30 the thirteenth element of the matrix
+ * @param {float} m31 the fourtheenth element of the matrix
+ * @param {float} m32 the fivetheenth element of the matrix
+ * @param {float} m33 the sixteenth element of the matrix
+ */
+ apply: function() {
+ var source;
+ if (arguments.length === 1 && arguments[0] instanceof PMatrix3D) {
+ source = arguments[0].array();
+ } else if (arguments.length === 16) {
+ source = Array.prototype.slice.call(arguments);
+ } else if (arguments.length === 1 && arguments[0] instanceof Array) {
+ source = arguments[0];
+ }
+
+ var result = [0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0];
+ var e = 0;
+ for (var row = 0; row < 4; row++) {
+ for (var col = 0; col < 4; col++, e++) {
+ result[e] += this.elements[row * 4 + 0] * source[col + 0] + this.elements[row * 4 + 1] *
+ source[col + 4] + this.elements[row * 4 + 2] * source[col + 8] +
+ this.elements[row * 4 + 3] * source[col + 12];
+ }
+ }
+ this.elements = result.slice();
+ },
+ /**
+ * @member PMatrix3D
+ * The rotate() function rotates the matrix.
+ *
+ * @param {float} angle the angle of rotation in radiants
+ */
+ rotate: function(angle, v0, v1, v2) {
+ if (arguments.length < 4) {
+ this.rotateZ(angle);
+ } else {
+ var v = new PVector(v0, v1, v2);
+ var m = v.mag();
+ if (m === 0) {
+ return;
+ } else if (m != 1) {
+ v.normalize();
+ v0 = v.x;
+ v1 = v.y;
+ v2 = v.z;
+ }
+ var c = p.cos(angle);
+ var s = p.sin(angle);
+ var t = 1.0 - c;
+
+ this.apply((t * v0 * v0) + c,
+ (t * v0 * v1) - (s * v2),
+ (t * v0 * v2) + (s * v1),
+ 0,
+ (t * v0 * v1) + (s * v2),
+ (t * v1 * v1) + c,
+ (t * v1 * v2) - (s * v0),
+ 0,
+ (t * v0 * v2) - (s * v1),
+ (t * v1 * v2) + (s * v0),
+ (t * v2 * v2) + c,
+ 0,
+ 0, 0, 0, 1);
+ }
+ },
+ /**
+ * @member PMatrix3D
+ * The invApply() function applies the inverted matrix to this matrix.
+ *
+ * @param {float} m00 the first element of the matrix
+ * @param {float} m01 the second element of the matrix
+ * @param {float} m02 the third element of the matrix
+ * @param {float} m03 the fourth element of the matrix
+ * @param {float} m10 the fifth element of the matrix
+ * @param {float} m11 the sixth element of the matrix
+ * @param {float} m12 the seventh element of the matrix
+ * @param {float} m13 the eight element of the matrix
+ * @param {float} m20 the nineth element of the matrix
+ * @param {float} m21 the tenth element of the matrix
+ * @param {float} m22 the eleventh element of the matrix
+ * @param {float} m23 the twelveth element of the matrix
+ * @param {float} m30 the thirteenth element of the matrix
+ * @param {float} m31 the fourtheenth element of the matrix
+ * @param {float} m32 the fivetheenth element of the matrix
+ * @param {float} m33 the sixteenth element of the matrix
+ *
+ * @return {boolean} returns true if the operation was successful.
+ */
+ invApply: function() {
+ if (inverseCopy === undef) {
+ inverseCopy = new PMatrix3D();
+ }
+ var a = arguments;
+ inverseCopy.set(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8],
+ a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
+
+ if (!inverseCopy.invert()) {
+ return false;
+ }
+ this.preApply(inverseCopy);
+ return true;
+ },
+ /**
+ * @member PMatrix3D
+ * The rotateZ() function rotates the matrix.
+ *
+ * @param {float} angle the angle of rotation in radiants
+ */
+ rotateX: function(angle) {
+ var c = p.cos(angle);
+ var s = p.sin(angle);
+ this.apply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]);
+ },
+ /**
+ * @member PMatrix3D
+ * The rotateY() function rotates the matrix.
+ *
+ * @param {float} angle the angle of rotation in radiants
+ */
+ rotateY: function(angle) {
+ var c = p.cos(angle);
+ var s = p.sin(angle);
+ this.apply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]);
+ },
+ /**
+ * @member PMatrix3D
+ * The rotateZ() function rotates the matrix.
+ *
+ * @param {float} angle the angle of rotation in radiants
+ */
+ rotateZ: function(angle) {
+ var c = Math.cos(angle);
+ var s = Math.sin(angle);
+ this.apply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
+ },
+ /**
+ * @member PMatrix3D
+ * The scale() function increases or decreases the size of a matrix by expanding and contracting vertices. When only one parameter is specified scale will occur in all dimensions.
+ * This is equivalent to a three parameter call.
+ *
+ * @param {float} sx the amount to scale on the x-axis
+ * @param {float} sy the amount to scale on the y-axis
+ * @param {float} sz the amount to scale on the z-axis
+ */
+ scale: function(sx, sy, sz) {
+ if (sx && !sy && !sz) {
+ sy = sz = sx;
+ } else if (sx && sy && !sz) {
+ sz = 1;
+ }
+
+ if (sx && sy && sz) {
+ this.elements[0] *= sx;
+ this.elements[1] *= sy;
+ this.elements[2] *= sz;
+ this.elements[4] *= sx;
+ this.elements[5] *= sy;
+ this.elements[6] *= sz;
+ this.elements[8] *= sx;
+ this.elements[9] *= sy;
+ this.elements[10] *= sz;
+ this.elements[12] *= sx;
+ this.elements[13] *= sy;
+ this.elements[14] *= sz;
+ }
+ },
+ /**
+ * @member PMatrix3D
+ * The skewX() function skews the matrix along the x-axis the amount specified by the angle parameter.
+ * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
+ *
+ * @param {float} angle angle of skew specified in radians
+ */
+ skewX: function(angle) {
+ var t = Math.tan(angle);
+ this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+ },
+ /**
+ * @member PMatrix3D
+ * The skewY() function skews the matrix along the y-axis the amount specified by the angle parameter.
+ * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
+ *
+ * @param {float} angle angle of skew specified in radians
+ */
+ skewY: function(angle) {
+ var t = Math.tan(angle);
+ this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+ },
+ /**
+ * @member PMatrix3D
+ * The shearX() function shears the matrix along the x-axis the amount specified by the angle parameter.
+ * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
+ *
+ * @param {float} angle angle of shear specified in radians
+ */
+ shearX: function(angle) {
+ var t = Math.tan(angle);
+ this.apply(1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+ },
+ /**
+ * @member PMatrix3D
+ * The shearY() function shears the matrix along the y-axis the amount specified by the angle parameter.
+ * Angles should be specified in radians (values from 0 to PI*2) or converted to radians with the <b>radians()</b> function.
+ *
+ * @param {float} angle angle of shear specified in radians
+ */
+ shearY: function(angle) {
+ var t = Math.tan(angle);
+ this.apply(1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+ },
+ multX: function(x, y, z, w) {
+ if (!z) {
+ return this.elements[0] * x + this.elements[1] * y + this.elements[3];
+ }
+ if (!w) {
+ return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3];
+ }
+ return this.elements[0] * x + this.elements[1] * y + this.elements[2] * z + this.elements[3] * w;
+ },
+ multY: function(x, y, z, w) {
+ if (!z) {
+ return this.elements[4] * x + this.elements[5] * y + this.elements[7];
+ }
+ if (!w) {
+ return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7];
+ }
+ return this.elements[4] * x + this.elements[5] * y + this.elements[6] * z + this.elements[7] * w;
+ },
+ multZ: function(x, y, z, w) {
+ if (!w) {
+ return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11];
+ }
+ return this.elements[8] * x + this.elements[9] * y + this.elements[10] * z + this.elements[11] * w;
+ },
+ multW: function(x, y, z, w) {
+ if (!w) {
+ return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15];
+ }
+ return this.elements[12] * x + this.elements[13] * y + this.elements[14] * z + this.elements[15] * w;
+ },
+ /**
+ * @member PMatrix3D
+ * The invert() function inverts this matrix
+ *
+ * @return {boolean} true if successful
+ */
+ invert: function() {
+ var fA0 = this.elements[0] * this.elements[5] - this.elements[1] * this.elements[4];
+ var fA1 = this.elements[0] * this.elements[6] - this.elements[2] * this.elements[4];
+ var fA2 = this.elements[0] * this.elements[7] - this.elements[3] * this.elements[4];
+ var fA3 = this.elements[1] * this.elements[6] - this.elements[2] * this.elements[5];
+ var fA4 = this.elements[1] * this.elements[7] - this.elements[3] * this.elements[5];
+ var fA5 = this.elements[2] * this.elements[7] - this.elements[3] * this.elements[6];
+ var fB0 = this.elements[8] * this.elements[13] - this.elements[9] * this.elements[12];
+ var fB1 = this.elements[8] * this.elements[14] - this.elements[10] * this.elements[12];
+ var fB2 = this.elements[8] * this.elements[15] - this.elements[11] * this.elements[12];
+ var fB3 = this.elements[9] * this.elements[14] - this.elements[10] * this.elements[13];
+ var fB4 = this.elements[9] * this.elements[15] - this.elements[11] * this.elements[13];
+ var fB5 = this.elements[10] * this.elements[15] - this.elements[11] * this.elements[14];
+
+ // Determinant
+ var fDet = fA0 * fB5 - fA1 * fB4 + fA2 * fB3 + fA3 * fB2 - fA4 * fB1 + fA5 * fB0;
+
+ // Account for a very small value
+ // return false if not successful.
+ if (Math.abs(fDet) <= 1e-9) {
+ return false;
+ }
+
+ var kInv = [];
+ kInv[0] = +this.elements[5] * fB5 - this.elements[6] * fB4 + this.elements[7] * fB3;
+ kInv[4] = -this.elements[4] * fB5 + this.elements[6] * fB2 - this.elements[7] * fB1;
+ kInv[8] = +this.elements[4] * fB4 - this.elements[5] * fB2 + this.elements[7] * fB0;
+ kInv[12] = -this.elements[4] * fB3 + this.elements[5] * fB1 - this.elements[6] * fB0;
+ kInv[1] = -this.elements[1] * fB5 + this.elements[2] * fB4 - this.elements[3] * fB3;
+ kInv[5] = +this.elements[0] * fB5 - this.elements[2] * fB2 + this.elements[3] * fB1;
+ kInv[9] = -this.elements[0] * fB4 + this.elements[1] * fB2 - this.elements[3] * fB0;
+ kInv[13] = +this.elements[0] * fB3 - this.elements[1] * fB1 + this.elements[2] * fB0;
+ kInv[2] = +this.elements[13] * fA5 - this.elements[14] * fA4 + this.elements[15] * fA3;
+ kInv[6] = -this.elements[12] * fA5 + this.elements[14] * fA2 - this.elements[15] * fA1;
+ kInv[10] = +this.elements[12] * fA4 - this.elements[13] * fA2 + this.elements[15] * fA0;
+ kInv[14] = -this.elements[12] * fA3 + this.elements[13] * fA1 - this.elements[14] * fA0;
+ kInv[3] = -this.elements[9] * fA5 + this.elements[10] * fA4 - this.elements[11] * fA3;
+ kInv[7] = +this.elements[8] * fA5 - this.elements[10] * fA2 + this.elements[11] * fA1;
+ kInv[11] = -this.elements[8] * fA4 + this.elements[9] * fA2 - this.elements[11] * fA0;
+ kInv[15] = +this.elements[8] * fA3 - this.elements[9] * fA1 + this.elements[10] * fA0;
+
+ // Inverse using Determinant
+ var fInvDet = 1.0 / fDet;
+ kInv[0] *= fInvDet;
+ kInv[1] *= fInvDet;
+ kInv[2] *= fInvDet;
+ kInv[3] *= fInvDet;
+ kInv[4] *= fInvDet;
+ kInv[5] *= fInvDet;
+ kInv[6] *= fInvDet;
+ kInv[7] *= fInvDet;
+ kInv[8] *= fInvDet;
+ kInv[9] *= fInvDet;
+ kInv[10] *= fInvDet;
+ kInv[11] *= fInvDet;
+ kInv[12] *= fInvDet;
+ kInv[13] *= fInvDet;
+ kInv[14] *= fInvDet;
+ kInv[15] *= fInvDet;
+
+ this.elements = kInv.slice();
+ return true;
+ },
+ toString: function() {
+ var str = "";
+ for (var i = 0; i < 15; i++) {
+ str += this.elements[i] + ", ";
+ }
+ str += this.elements[15];
+ return str;
+ },
+ /**
+ * @member PMatrix3D
+ * The print() function prints out the elements of this matrix
+ */
+ print: function() {
+ var digits = printMatrixHelper(this.elements);
+
+ var output = "" + p.nfs(this.elements[0], digits, 4) + " " + p.nfs(this.elements[1], digits, 4) +
+ " " + p.nfs(this.elements[2], digits, 4) + " " + p.nfs(this.elements[3], digits, 4) +
+ "\n" + p.nfs(this.elements[4], digits, 4) + " " + p.nfs(this.elements[5], digits, 4) +
+ " " + p.nfs(this.elements[6], digits, 4) + " " + p.nfs(this.elements[7], digits, 4) +
+ "\n" + p.nfs(this.elements[8], digits, 4) + " " + p.nfs(this.elements[9], digits, 4) +
+ " " + p.nfs(this.elements[10], digits, 4) + " " + p.nfs(this.elements[11], digits, 4) +
+ "\n" + p.nfs(this.elements[12], digits, 4) + " " + p.nfs(this.elements[13], digits, 4) +
+ " " + p.nfs(this.elements[14], digits, 4) + " " + p.nfs(this.elements[15], digits, 4) + "\n\n";
+ p.println(output);
+ },
+ invTranslate: function(tx, ty, tz) {
+ this.preApply(1, 0, 0, -tx, 0, 1, 0, -ty, 0, 0, 1, -tz, 0, 0, 0, 1);
+ },
+ invRotateX: function(angle) {
+ var c = Math.cos(-angle);
+ var s = Math.sin(-angle);
+ this.preApply([1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1]);
+ },
+ invRotateY: function(angle) {
+ var c = Math.cos(-angle);
+ var s = Math.sin(-angle);
+ this.preApply([c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1]);
+ },
+ invRotateZ: function(angle) {
+ var c = Math.cos(-angle);
+ var s = Math.sin(-angle);
+ this.preApply([c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
+ },
+ invScale: function(x, y, z) {
+ this.preApply([1 / x, 0, 0, 0, 0, 1 / y, 0, 0, 0, 0, 1 / z, 0, 0, 0, 0, 1]);
+ }
+ };
+
+ /**
+ * @private
+ * The matrix stack stores the transformations and translations that occur within the space.
+ */
+ var PMatrixStack = p.PMatrixStack = function() {
+ this.matrixStack = [];
+ };
+
+ /**
+ * @member PMatrixStack
+ * load pushes the matrix given in the function into the stack
+ *
+ * @param {Object | Array} matrix the matrix to be pushed into the stack
+ */
+ PMatrixStack.prototype.load = function() {
+ var tmpMatrix = drawing.$newPMatrix();
+
+ if (arguments.length === 1) {
+ tmpMatrix.set(arguments[0]);
+ } else {
+ tmpMatrix.set(arguments);
+ }
+ this.matrixStack.push(tmpMatrix);
+ };
+
+ Drawing2D.prototype.$newPMatrix = function() {
+ return new PMatrix2D();
+ };
+
+ Drawing3D.prototype.$newPMatrix = function() {
+ return new PMatrix3D();
+ };
+
+ /**
+ * @member PMatrixStack
+ * push adds a duplicate of the top of the stack onto the stack - uses the peek function
+ */
+ PMatrixStack.prototype.push = function() {
+ this.matrixStack.push(this.peek());
+ };
+
+ /**
+ * @member PMatrixStack
+ * pop removes returns the matrix at the top of the stack
+ *
+ * @returns {Object} the matrix at the top of the stack
+ */
+ PMatrixStack.prototype.pop = function() {
+ return this.matrixStack.pop();
+ };
+
+ /**
+ * @member PMatrixStack
+ * peek returns but doesn't remove the matrix at the top of the stack
+ *
+ * @returns {Object} the matrix at the top of the stack
+ */
+ PMatrixStack.prototype.peek = function() {
+ var tmpMatrix = drawing.$newPMatrix();
+
+ tmpMatrix.set(this.matrixStack[this.matrixStack.length - 1]);
+ return tmpMatrix;
+ };
+
+ /**
+ * @member PMatrixStack
+ * this function multiplies the matrix at the top of the stack with the matrix given as a parameter
+ *
+ * @param {Object | Array} matrix the matrix to be multiplied into the stack
+ */
+ PMatrixStack.prototype.mult = function(matrix) {
+ this.matrixStack[this.matrixStack.length - 1].apply(matrix);
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Array handling
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * The split() function breaks a string into pieces using a character or string
+ * as the divider. The delim parameter specifies the character or characters that
+ * mark the boundaries between each piece. A String[] array is returned that contains
+ * each of the pieces.
+ * If the result is a set of numbers, you can convert the String[] array to to a float[]
+ * or int[] array using the datatype conversion functions int() and float() (see example above).
+ * The splitTokens() function works in a similar fashion, except that it splits using a range
+ * of characters instead of a specific character or sequence.
+ *
+ * @param {String} str the String to be split
+ * @param {String} delim the character or String used to separate the data
+ *
+ * @returns {string[]} The new string array
+ *
+ * @see splitTokens
+ * @see join
+ * @see trim
+ */
+ p.split = function(str, delim) {
+ return str.split(delim);
+ };
+
+ /**
+ * The splitTokens() function splits a String at one or many character "tokens." The tokens
+ * parameter specifies the character or characters to be used as a boundary.
+ * If no tokens character is specified, any whitespace character is used to split.
+ * Whitespace characters include tab (\t), line feed (\n), carriage return (\r), form
+ * feed (\f), and space. To convert a String to an array of integers or floats, use the
+ * datatype conversion functions int() and float() to convert the array of Strings.
+ *
+ * @param {String} str the String to be split
+ * @param {Char[]} tokens list of individual characters that will be used as separators
+ *
+ * @returns {string[]} The new string array
+ *
+ * @see split
+ * @see join
+ * @see trim
+ */
+ p.splitTokens = function(str, tokens) {
+ if (tokens === undef) {
+ return str.split(/\s+/g);
+ }
+
+ var chars = tokens.split(/()/g),
+ buffer = "",
+ len = str.length,
+ i, c,
+ tokenized = [];
+
+ for (i = 0; i < len; i++) {
+ c = str[i];
+ if (chars.indexOf(c) > -1) {
+ if (buffer !== "") {
+ tokenized.push(buffer);
+ }
+ buffer = "";
+ } else {
+ buffer += c;
+ }
+ }
+
+ if (buffer !== "") {
+ tokenized.push(buffer);
+ }
+
+ return tokenized;
+ };
+
+ /**
+ * Expands an array by one element and adds data to the new position. The datatype of
+ * the element parameter must be the same as the datatype of the array.
+ * When using an array of objects, the data returned from the function must be cast to
+ * the object array's data type. For example: SomeClass[] items = (SomeClass[])
+ * append(originalArray, element).
+ *
+ * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array boolean[],
+ * byte[], char[], int[], float[], or String[], or an array of objects
+ * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} element new data for the array
+ *
+ * @returns Array (the same datatype as the input)
+ *
+ * @see shorten
+ * @see expand
+ */
+ p.append = function(array, element) {
+ array[array.length] = element;
+ return array;
+ };
+
+ /**
+ * Concatenates two arrays. For example, concatenating the array { 1, 2, 3 } and the
+ * array { 4, 5, 6 } yields { 1, 2, 3, 4, 5, 6 }. Both parameters must be arrays of the
+ * same datatype.
+ * When using an array of objects, the data returned from the function must be cast to the
+ * object array's data type. For example: SomeClass[] items = (SomeClass[]) concat(array1, array2).
+ *
+ * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array1 boolean[],
+ * byte[], char[], int[], float[], String[], or an array of objects
+ * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array2 boolean[],
+ * byte[], char[], int[], float[], String[], or an array of objects
+ *
+ * @returns Array (the same datatype as the input)
+ *
+ * @see splice
+ */
+ p.concat = function(array1, array2) {
+ return array1.concat(array2);
+ };
+
+ /**
+ * Sorts an array of numbers from smallest to largest and puts an array of
+ * words in alphabetical order. The original array is not modified, a
+ * re-ordered array is returned. The count parameter states the number of
+ * elements to sort. For example if there are 12 elements in an array and
+ * if count is the value 5, only the first five elements on the array will
+ * be sorted. Alphabetical ordering is case insensitive.
+ *
+ * @param {String[] | int[] | float[]} array Array of elements to sort
+ * @param {int} numElem Number of elements to sort
+ *
+ * @returns {String[] | int[] | float[]} Array (same datatype as the input)
+ *
+ * @see reverse
+ */
+ p.sort = function(array, numElem) {
+ var ret = [];
+
+ // depending on the type used (int, float) or string
+ // we'll need to use a different compare function
+ if (array.length > 0) {
+ // copy since we need to return another array
+ var elemsToCopy = numElem > 0 ? numElem : array.length;
+ for (var i = 0; i < elemsToCopy; i++) {
+ ret.push(array[i]);
+ }
+ if (typeof array[0] === "string") {
+ ret.sort();
+ }
+ // int or float
+ else {
+ ret.sort(function(a, b) {
+ return a - b;
+ });
+ }
+
+ // copy on the rest of the elements that were not sorted in case the user
+ // only wanted a subset of an array to be sorted.
+ if (numElem > 0) {
+ for (var j = ret.length; j < array.length; j++) {
+ ret.push(array[j]);
+ }
+ }
+ }
+ return ret;
+ };
+
+ /**
+ * Inserts a value or array of values into an existing array. The first two parameters must
+ * be of the same datatype. The array parameter defines the array which will be modified
+ * and the second parameter defines the data which will be inserted. When using an array
+ * of objects, the data returned from the function must be cast to the object array's data
+ * type. For example: SomeClass[] items = (SomeClass[]) splice(array1, array2, index).
+ *
+ * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array boolean[],
+ * byte[], char[], int[], float[], String[], or an array of objects
+ * @param {boolean|byte|char|int|float|String|boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects}
+ * value boolean, byte, char, int, float, String, boolean[], byte[], char[], int[],
+ * float[], String[], or other Object: value or an array of objects to be spliced in
+ * @param {int} index position in the array from which to insert data
+ *
+ * @returns Array (the same datatype as the input)
+ *
+ * @see contract
+ * @see subset
+ */
+ p.splice = function(array, value, index) {
+
+ // Trying to splice an empty array into "array" in P5 won't do
+ // anything, just return the original.
+ if(value.length === 0)
+ {
+ return array;
+ }
+
+ // If the second argument was an array, we'll need to iterate over all
+ // the "value" elements and add one by one because
+ // array.splice(index, 0, value);
+ // would create a multi-dimensional array which isn't what we want.
+ if(value instanceof Array) {
+ for(var i = 0, j = index; i < value.length; j++,i++) {
+ array.splice(j, 0, value[i]);
+ }
+ } else {
+ array.splice(index, 0, value);
+ }
+
+ return array;
+ };
+
+ /**
+ * Extracts an array of elements from an existing array. The array parameter defines the
+ * array from which the elements will be copied and the offset and length parameters determine
+ * which elements to extract. If no length is given, elements will be extracted from the offset
+ * to the end of the array. When specifying the offset remember the first array element is 0.
+ * This function does not change the source array.
+ * When using an array of objects, the data returned from the function must be cast to the
+ * object array's data type.
+ *
+ * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array boolean[],
+ * byte[], char[], int[], float[], String[], or an array of objects
+ * @param {int} offset position to begin
+ * @param {int} length number of values to extract
+ *
+ * @returns Array (the same datatype as the input)
+ *
+ * @see splice
+ */
+ p.subset = function(array, offset, length) {
+ var end = (length !== undef) ? offset + length : array.length;
+ return array.slice(offset, end);
+ };
+
+ /**
+ * Combines an array of Strings into one String, each separated by the character(s) used for
+ * the separator parameter. To join arrays of ints or floats, it's necessary to first convert
+ * them to strings using nf() or nfs().
+ *
+ * @param {Array} array array of Strings
+ * @param {char|String} separator char or String to be placed between each item
+ *
+ * @returns {String} The combined string
+ *
+ * @see split
+ * @see trim
+ * @see nf
+ * @see nfs
+ */
+ p.join = function(array, seperator) {
+ return array.join(seperator);
+ };
+
+ /**
+ * Decreases an array by one element and returns the shortened array. When using an
+ * array of objects, the data returned from the function must be cast to the object array's
+ * data type. For example: SomeClass[] items = (SomeClass[]) shorten(originalArray).
+ *
+ * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} array
+ * boolean[], byte[], char[], int[], float[], or String[], or an array of objects
+ *
+ * @returns Array (the same datatype as the input)
+ *
+ * @see append
+ * @see expand
+ */
+ p.shorten = function(ary) {
+ var newary = [];
+
+ // copy array into new array
+ var len = ary.length;
+ for (var i = 0; i < len; i++) {
+ newary[i] = ary[i];
+ }
+ newary.pop();
+
+ return newary;
+ };
+
+ /**
+ * Increases the size of an array. By default, this function doubles the size of the array,
+ * but the optional newSize parameter provides precise control over the increase in size.
+ * When using an array of objects, the data returned from the function must be cast to the
+ * object array's data type. For example: SomeClass[] items = (SomeClass[]) expand(originalArray).
+ *
+ * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]|array of objects} ary
+ * boolean[], byte[], char[], int[], float[], String[], or an array of objects
+ * @param {int} newSize positive int: new size for the array
+ *
+ * @returns Array (the same datatype as the input)
+ *
+ * @see contract
+ */
+ p.expand = function(ary, targetSize) {
+ var temp = ary.slice(0),
+ newSize = targetSize || ary.length * 2;
+ temp.length = newSize;
+ return temp;
+ };
+
+ /**
+ * Copies an array (or part of an array) to another array. The src array is copied to the
+ * dst array, beginning at the position specified by srcPos and into the position specified
+ * by dstPos. The number of elements to copy is determined by length. The simplified version
+ * with two arguments copies an entire array to another of the same size. It is equivalent
+ * to "arrayCopy(src, 0, dst, 0, src.length)". This function is far more efficient for copying
+ * array data than iterating through a for and copying each element.
+ *
+ * @param {Array} src an array of any data type: the source array
+ * @param {Array} dest an array of any data type (as long as it's the same as src): the destination array
+ * @param {int} srcPos starting position in the source array
+ * @param {int} destPos starting position in the destination array
+ * @param {int} length number of array elements to be copied
+ *
+ * @returns none
+ */
+ p.arrayCopy = function() { // src, srcPos, dest, destPos, length) {
+ var src, srcPos = 0, dest, destPos = 0, length;
+
+ if (arguments.length === 2) {
+ // recall itself and copy src to dest from start index 0 to 0 of src.length
+ src = arguments[0];
+ dest = arguments[1];
+ length = src.length;
+ } else if (arguments.length === 3) {
+ // recall itself and copy src to dest from start index 0 to 0 of length
+ src = arguments[0];
+ dest = arguments[1];
+ length = arguments[2];
+ } else if (arguments.length === 5) {
+ src = arguments[0];
+ srcPos = arguments[1];
+ dest = arguments[2];
+ destPos = arguments[3];
+ length = arguments[4];
+ }
+
+ // copy src to dest from index srcPos to index destPos of length recursivly on objects
+ for (var i = srcPos, j = destPos; i < length + srcPos; i++, j++) {
+ if (dest[j] !== undef) {
+ dest[j] = src[i];
+ } else {
+ throw "array index out of bounds exception";
+ }
+ }
+ };
+
+ /**
+ * Reverses the order of an array.
+ *
+ * @param {boolean[]|byte[]|char[]|int[]|float[]|String[]} array
+ * boolean[], byte[], char[], int[], float[], or String[]
+ *
+ * @returns Array (the same datatype as the input)
+ *
+ * @see sort
+ */
+ p.reverse = function(array) {
+ return array.reverse();
+ };
+
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Color functions
+ ////////////////////////////////////////////////////////////////////////////
+
+ // helper functions for internal blending modes
+ p.mix = function(a, b, f) {
+ return a + (((b - a) * f) >> 8);
+ };
+
+ p.peg = function(n) {
+ return (n < 0) ? 0 : ((n > 255) ? 255 : n);
+ };
+
+ // blending modes
+ /**
+ * These are internal blending modes used for BlendColor()
+ *
+ * @param {Color} c1 First Color to blend
+ * @param {Color} c2 Second Color to blend
+ *
+ * @returns {Color} The blended Color
+ *
+ * @see BlendColor
+ * @see Blend
+ */
+ p.modes = (function() {
+ var ALPHA_MASK = PConstants.ALPHA_MASK,
+ RED_MASK = PConstants.RED_MASK,
+ GREEN_MASK = PConstants.GREEN_MASK,
+ BLUE_MASK = PConstants.BLUE_MASK,
+ min = Math.min,
+ max = Math.max;
+
+ function applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb) {
+ var a = min(((c1 & 0xff000000) >>> 24) + f, 0xff) << 24;
+
+ var r = (ar + (((cr - ar) * f) >> 8));
+ r = ((r < 0) ? 0 : ((r > 255) ? 255 : r)) << 16;
+
+ var g = (ag + (((cg - ag) * f) >> 8));
+ g = ((g < 0) ? 0 : ((g > 255) ? 255 : g)) << 8;
+
+ var b = ab + (((cb - ab) * f) >> 8);
+ b = (b < 0) ? 0 : ((b > 255) ? 255 : b);
+
+ return (a | r | g | b);
+ }
+
+ return {
+ replace: function(c1, c2) {
+ return c2;
+ },
+ blend: function(c1, c2) {
+ var f = (c2 & ALPHA_MASK) >>> 24,
+ ar = (c1 & RED_MASK),
+ ag = (c1 & GREEN_MASK),
+ ab = (c1 & BLUE_MASK),
+ br = (c2 & RED_MASK),
+ bg = (c2 & GREEN_MASK),
+ bb = (c2 & BLUE_MASK);
+
+ return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
+ (ar + (((br - ar) * f) >> 8)) & RED_MASK |
+ (ag + (((bg - ag) * f) >> 8)) & GREEN_MASK |
+ (ab + (((bb - ab) * f) >> 8)) & BLUE_MASK);
+ },
+ add: function(c1, c2) {
+ var f = (c2 & ALPHA_MASK) >>> 24;
+ return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
+ min(((c1 & RED_MASK) + ((c2 & RED_MASK) >> 8) * f), RED_MASK) & RED_MASK |
+ min(((c1 & GREEN_MASK) + ((c2 & GREEN_MASK) >> 8) * f), GREEN_MASK) & GREEN_MASK |
+ min((c1 & BLUE_MASK) + (((c2 & BLUE_MASK) * f) >> 8), BLUE_MASK));
+ },
+ subtract: function(c1, c2) {
+ var f = (c2 & ALPHA_MASK) >>> 24;
+ return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
+ max(((c1 & RED_MASK) - ((c2 & RED_MASK) >> 8) * f), GREEN_MASK) & RED_MASK |
+ max(((c1 & GREEN_MASK) - ((c2 & GREEN_MASK) >> 8) * f), BLUE_MASK) & GREEN_MASK |
+ max((c1 & BLUE_MASK) - (((c2 & BLUE_MASK) * f) >> 8), 0));
+ },
+ lightest: function(c1, c2) {
+ var f = (c2 & ALPHA_MASK) >>> 24;
+ return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
+ max(c1 & RED_MASK, ((c2 & RED_MASK) >> 8) * f) & RED_MASK |
+ max(c1 & GREEN_MASK, ((c2 & GREEN_MASK) >> 8) * f) & GREEN_MASK |
+ max(c1 & BLUE_MASK, ((c2 & BLUE_MASK) * f) >> 8));
+ },
+ darkest: function(c1, c2) {
+ var f = (c2 & ALPHA_MASK) >>> 24,
+ ar = (c1 & RED_MASK),
+ ag = (c1 & GREEN_MASK),
+ ab = (c1 & BLUE_MASK),
+ br = min(c1 & RED_MASK, ((c2 & RED_MASK) >> 8) * f),
+ bg = min(c1 & GREEN_MASK, ((c2 & GREEN_MASK) >> 8) * f),
+ bb = min(c1 & BLUE_MASK, ((c2 & BLUE_MASK) * f) >> 8);
+
+ return (min(((c1 & ALPHA_MASK) >>> 24) + f, 0xff) << 24 |
+ (ar + (((br - ar) * f) >> 8)) & RED_MASK |
+ (ag + (((bg - ag) * f) >> 8)) & GREEN_MASK |
+ (ab + (((bb - ab) * f) >> 8)) & BLUE_MASK);
+ },
+ difference: function(c1, c2) {
+ var f = (c2 & ALPHA_MASK) >>> 24,
+ ar = (c1 & RED_MASK) >> 16,
+ ag = (c1 & GREEN_MASK) >> 8,
+ ab = (c1 & BLUE_MASK),
+ br = (c2 & RED_MASK) >> 16,
+ bg = (c2 & GREEN_MASK) >> 8,
+ bb = (c2 & BLUE_MASK),
+ cr = (ar > br) ? (ar - br) : (br - ar),
+ cg = (ag > bg) ? (ag - bg) : (bg - ag),
+ cb = (ab > bb) ? (ab - bb) : (bb - ab);
+
+ return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
+ },
+ exclusion: function(c1, c2) {
+ var f = (c2 & ALPHA_MASK) >>> 24,
+ ar = (c1 & RED_MASK) >> 16,
+ ag = (c1 & GREEN_MASK) >> 8,
+ ab = (c1 & BLUE_MASK),
+ br = (c2 & RED_MASK) >> 16,
+ bg = (c2 & GREEN_MASK) >> 8,
+ bb = (c2 & BLUE_MASK),
+ cr = ar + br - ((ar * br) >> 7),
+ cg = ag + bg - ((ag * bg) >> 7),
+ cb = ab + bb - ((ab * bb) >> 7);
+
+ return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
+ },
+ multiply: function(c1, c2) {
+ var f = (c2 & ALPHA_MASK) >>> 24,
+ ar = (c1 & RED_MASK) >> 16,
+ ag = (c1 & GREEN_MASK) >> 8,
+ ab = (c1 & BLUE_MASK),
+ br = (c2 & RED_MASK) >> 16,
+ bg = (c2 & GREEN_MASK) >> 8,
+ bb = (c2 & BLUE_MASK),
+ cr = (ar * br) >> 8,
+ cg = (ag * bg) >> 8,
+ cb = (ab * bb) >> 8;
+
+ return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
+ },
+ screen: function(c1, c2) {
+ var f = (c2 & ALPHA_MASK) >>> 24,
+ ar = (c1 & RED_MASK) >> 16,
+ ag = (c1 & GREEN_MASK) >> 8,
+ ab = (c1 & BLUE_MASK),
+ br = (c2 & RED_MASK) >> 16,
+ bg = (c2 & GREEN_MASK) >> 8,
+ bb = (c2 & BLUE_MASK),
+ cr = 255 - (((255 - ar) * (255 - br)) >> 8),
+ cg = 255 - (((255 - ag) * (255 - bg)) >> 8),
+ cb = 255 - (((255 - ab) * (255 - bb)) >> 8);
+
+ return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
+ },
+ hard_light: function(c1, c2) {
+ var f = (c2 & ALPHA_MASK) >>> 24,
+ ar = (c1 & RED_MASK) >> 16,
+ ag = (c1 & GREEN_MASK) >> 8,
+ ab = (c1 & BLUE_MASK),
+ br = (c2 & RED_MASK) >> 16,
+ bg = (c2 & GREEN_MASK) >> 8,
+ bb = (c2 & BLUE_MASK),
+ cr = (br < 128) ? ((ar * br) >> 7) : (255 - (((255 - ar) * (255 - br)) >> 7)),
+ cg = (bg < 128) ? ((ag * bg) >> 7) : (255 - (((255 - ag) * (255 - bg)) >> 7)),
+ cb = (bb < 128) ? ((ab * bb) >> 7) : (255 - (((255 - ab) * (255 - bb)) >> 7));
+
+ return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
+ },
+ soft_light: function(c1, c2) {
+ var f = (c2 & ALPHA_MASK) >>> 24,
+ ar = (c1 & RED_MASK) >> 16,
+ ag = (c1 & GREEN_MASK) >> 8,
+ ab = (c1 & BLUE_MASK),
+ br = (c2 & RED_MASK) >> 16,
+ bg = (c2 & GREEN_MASK) >> 8,
+ bb = (c2 & BLUE_MASK),
+ cr = ((ar * br) >> 7) + ((ar * ar) >> 8) - ((ar * ar * br) >> 15),
+ cg = ((ag * bg) >> 7) + ((ag * ag) >> 8) - ((ag * ag * bg) >> 15),
+ cb = ((ab * bb) >> 7) + ((ab * ab) >> 8) - ((ab * ab * bb) >> 15);
+
+ return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
+ },
+ overlay: function(c1, c2) {
+ var f = (c2 & ALPHA_MASK) >>> 24,
+ ar = (c1 & RED_MASK) >> 16,
+ ag = (c1 & GREEN_MASK) >> 8,
+ ab = (c1 & BLUE_MASK),
+ br = (c2 & RED_MASK) >> 16,
+ bg = (c2 & GREEN_MASK) >> 8,
+ bb = (c2 & BLUE_MASK),
+ cr = (ar < 128) ? ((ar * br) >> 7) : (255 - (((255 - ar) * (255 - br)) >> 7)),
+ cg = (ag < 128) ? ((ag * bg) >> 7) : (255 - (((255 - ag) * (255 - bg)) >> 7)),
+ cb = (ab < 128) ? ((ab * bb) >> 7) : (255 - (((255 - ab) * (255 - bb)) >> 7));
+
+ return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
+ },
+ dodge: function(c1, c2) {
+ var f = (c2 & ALPHA_MASK) >>> 24,
+ ar = (c1 & RED_MASK) >> 16,
+ ag = (c1 & GREEN_MASK) >> 8,
+ ab = (c1 & BLUE_MASK),
+ br = (c2 & RED_MASK) >> 16,
+ bg = (c2 & GREEN_MASK) >> 8,
+ bb = (c2 & BLUE_MASK);
+
+ var cr = 255;
+ if (br !== 255) {
+ cr = (ar << 8) / (255 - br);
+ cr = (cr < 0) ? 0 : ((cr > 255) ? 255 : cr);
+ }
+
+ var cg = 255;
+ if (bg !== 255) {
+ cg = (ag << 8) / (255 - bg);
+ cg = (cg < 0) ? 0 : ((cg > 255) ? 255 : cg);
+ }
+
+ var cb = 255;
+ if (bb !== 255) {
+ cb = (ab << 8) / (255 - bb);
+ cb = (cb < 0) ? 0 : ((cb > 255) ? 255 : cb);
+ }
+
+ return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
+ },
+ burn: function(c1, c2) {
+ var f = (c2 & ALPHA_MASK) >>> 24,
+ ar = (c1 & RED_MASK) >> 16,
+ ag = (c1 & GREEN_MASK) >> 8,
+ ab = (c1 & BLUE_MASK),
+ br = (c2 & RED_MASK) >> 16,
+ bg = (c2 & GREEN_MASK) >> 8,
+ bb = (c2 & BLUE_MASK);
+
+ var cr = 0;
+ if (br !== 0) {
+ cr = ((255 - ar) << 8) / br;
+ cr = 255 - ((cr < 0) ? 0 : ((cr > 255) ? 255 : cr));
+ }
+
+ var cg = 0;
+ if (bg !== 0) {
+ cg = ((255 - ag) << 8) / bg;
+ cg = 255 - ((cg < 0) ? 0 : ((cg > 255) ? 255 : cg));
+ }
+
+ var cb = 0;
+ if (bb !== 0) {
+ cb = ((255 - ab) << 8) / bb;
+ cb = 255 - ((cb < 0) ? 0 : ((cb > 255) ? 255 : cb));
+ }
+
+ return applyMode(c1, f, ar, ag, ab, br, bg, bb, cr, cg, cb);
+ }
+ };
+ }());
+
+ function color$4(aValue1, aValue2, aValue3, aValue4) {
+ var r, g, b, a;
+
+ if (curColorMode === PConstants.HSB) {
+ var rgb = p.color.toRGB(aValue1, aValue2, aValue3);
+ r = rgb[0];
+ g = rgb[1];
+ b = rgb[2];
+ } else {
+ r = Math.round(255 * (aValue1 / colorModeX));
+ g = Math.round(255 * (aValue2 / colorModeY));
+ b = Math.round(255 * (aValue3 / colorModeZ));
+ }
+
+ a = Math.round(255 * (aValue4 / colorModeA));
+
+ // Limit values less than 0 and greater than 255
+ r = (r < 0) ? 0 : r;
+ g = (g < 0) ? 0 : g;
+ b = (b < 0) ? 0 : b;
+ a = (a < 0) ? 0 : a;
+ r = (r > 255) ? 255 : r;
+ g = (g > 255) ? 255 : g;
+ b = (b > 255) ? 255 : b;
+ a = (a > 255) ? 255 : a;
+
+ // Create color int
+ return (a << 24) & PConstants.ALPHA_MASK | (r << 16) & PConstants.RED_MASK | (g << 8) & PConstants.GREEN_MASK | b & PConstants.BLUE_MASK;
+ }
+
+ function color$2(aValue1, aValue2) {
+ var a;
+
+ // Color int and alpha
+ if (aValue1 & PConstants.ALPHA_MASK) {
+ a = Math.round(255 * (aValue2 / colorModeA));
+ // Limit values less than 0 and greater than 255
+ a = (a > 255) ? 255 : a;
+ a = (a < 0) ? 0 : a;
+
+ return aValue1 - (aValue1 & PConstants.ALPHA_MASK) + ((a << 24) & PConstants.ALPHA_MASK);
+ }
+ // Grayscale and alpha
+ if (curColorMode === PConstants.RGB) {
+ return color$4(aValue1, aValue1, aValue1, aValue2);
+ }
+ if (curColorMode === PConstants.HSB) {
+ return color$4(0, 0, (aValue1 / colorModeX) * colorModeZ, aValue2);
+ }
+ }
+
+ function color$1(aValue1) {
+ // Grayscale
+ if (aValue1 <= colorModeX && aValue1 >= 0) {
+ if (curColorMode === PConstants.RGB) {
+ return color$4(aValue1, aValue1, aValue1, colorModeA);
+ }
+ if (curColorMode === PConstants.HSB) {
+ return color$4(0, 0, (aValue1 / colorModeX) * colorModeZ, colorModeA);
+ }
+ }
+ // Color int
+ if (aValue1) {
+ if (aValue1 > 2147483647) {
+ // Java Overflow
+ aValue1 -= 4294967296;
+ }
+ return aValue1;
+ }
+ }
+
+ /**
+ * Creates colors for storing in variables of the color datatype. The parameters are
+ * interpreted as RGB or HSB values depending on the current colorMode(). The default
+ * mode is RGB values from 0 to 255 and therefore, the function call color(255, 204, 0)
+ * will return a bright yellow color. More about how colors are stored can be found in
+ * the reference for the color datatype.
+ *
+ * @param {int|float} aValue1 red or hue or grey values relative to the current color range.
+ * Also can be color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
+ * @param {int|float} aValue2 green or saturation values relative to the current color range
+ * @param {int|float} aValue3 blue or brightness values relative to the current color range
+ * @param {int|float} aValue4 relative to current color range. Represents alpha
+ *
+ * @returns {color} the color
+ *
+ * @see colorMode
+ */
+ p.color = function(aValue1, aValue2, aValue3, aValue4) {
+
+ // 4 arguments: (R, G, B, A) or (H, S, B, A)
+ if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef && aValue4 !== undef) {
+ return color$4(aValue1, aValue2, aValue3, aValue4);
+ }
+
+ // 3 arguments: (R, G, B) or (H, S, B)
+ if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef) {
+ return color$4(aValue1, aValue2, aValue3, colorModeA);
+ }
+
+ // 2 arguments: (Color, A) or (Grayscale, A)
+ if (aValue1 !== undef && aValue2 !== undef) {
+ return color$2(aValue1, aValue2);
+ }
+
+ // 1 argument: (Grayscale) or (Color)
+ if (typeof aValue1 === "number") {
+ return color$1(aValue1);
+ }
+
+ // Default
+ return color$4(colorModeX, colorModeY, colorModeZ, colorModeA);
+ };
+
+ // Ease of use function to extract the colour bits into a string
+ p.color.toString = function(colorInt) {
+ return "rgba(" + ((colorInt & PConstants.RED_MASK) >>> 16) + "," + ((colorInt & PConstants.GREEN_MASK) >>> 8) +
+ "," + ((colorInt & PConstants.BLUE_MASK)) + "," + ((colorInt & PConstants.ALPHA_MASK) >>> 24) / 255 + ")";
+ };
+
+ // Easy of use function to pack rgba values into a single bit-shifted color int.
+ p.color.toInt = function(r, g, b, a) {
+ return (a << 24) & PConstants.ALPHA_MASK | (r << 16) & PConstants.RED_MASK | (g << 8) & PConstants.GREEN_MASK | b & PConstants.BLUE_MASK;
+ };
+
+ // Creates a simple array in [R, G, B, A] format, [255, 255, 255, 255]
+ p.color.toArray = function(colorInt) {
+ return [(colorInt & PConstants.RED_MASK) >>> 16, (colorInt & PConstants.GREEN_MASK) >>> 8,
+ colorInt & PConstants.BLUE_MASK, (colorInt & PConstants.ALPHA_MASK) >>> 24];
+ };
+
+ // Creates a WebGL color array in [R, G, B, A] format. WebGL wants the color ranges between 0 and 1, [1, 1, 1, 1]
+ p.color.toGLArray = function(colorInt) {
+ return [((colorInt & PConstants.RED_MASK) >>> 16) / 255, ((colorInt & PConstants.GREEN_MASK) >>> 8) / 255,
+ (colorInt & PConstants.BLUE_MASK) / 255, ((colorInt & PConstants.ALPHA_MASK) >>> 24) / 255];
+ };
+
+ // HSB conversion function from Mootools, MIT Licensed
+ p.color.toRGB = function(h, s, b) {
+ // Limit values greater than range
+ h = (h > colorModeX) ? colorModeX : h;
+ s = (s > colorModeY) ? colorModeY : s;
+ b = (b > colorModeZ) ? colorModeZ : b;
+
+ h = (h / colorModeX) * 360;
+ s = (s / colorModeY) * 100;
+ b = (b / colorModeZ) * 100;
+
+ var br = Math.round(b / 100 * 255);
+
+ if (s === 0) { // Grayscale
+ return [br, br, br];
+ }
+ var hue = h % 360;
+ var f = hue % 60;
+ var p = Math.round((b * (100 - s)) / 10000 * 255);
+ var q = Math.round((b * (6000 - s * f)) / 600000 * 255);
+ var t = Math.round((b * (6000 - s * (60 - f))) / 600000 * 255);
+ switch (Math.floor(hue / 60)) {
+ case 0:
+ return [br, t, p];
+ case 1:
+ return [q, br, p];
+ case 2:
+ return [p, br, t];
+ case 3:
+ return [p, q, br];
+ case 4:
+ return [t, p, br];
+ case 5:
+ return [br, p, q];
+ }
+ };
+
+ function colorToHSB(colorInt) {
+ var red, green, blue;
+
+ red = ((colorInt & PConstants.RED_MASK) >>> 16) / 255;
+ green = ((colorInt & PConstants.GREEN_MASK) >>> 8) / 255;
+ blue = (colorInt & PConstants.BLUE_MASK) / 255;
+
+ var max = p.max(p.max(red,green), blue),
+ min = p.min(p.min(red,green), blue),
+ hue, saturation;
+
+ if (min === max) {
+ return [0, 0, max*colorModeZ];
+ }
+ saturation = (max - min) / max;
+
+ if (red === max) {
+ hue = (green - blue) / (max - min);
+ } else if (green === max) {
+ hue = 2 + ((blue - red) / (max - min));
+ } else {
+ hue = 4 + ((red - green) / (max - min));
+ }
+
+ hue /= 6;
+
+ if (hue < 0) {
+ hue += 1;
+ } else if (hue > 1) {
+ hue -= 1;
+ }
+ return [hue*colorModeX, saturation*colorModeY, max*colorModeZ];
+ }
+
+ /**
+ * Extracts the brightness value from a color.
+ *
+ * @param {color} colInt any value of the color datatype
+ *
+ * @returns {float} The brightness color value.
+ *
+ * @see red
+ * @see green
+ * @see blue
+ * @see hue
+ * @see saturation
+ */
+ p.brightness = function(colInt){
+ return colorToHSB(colInt)[2];
+ };
+
+ /**
+ * Extracts the saturation value from a color.
+ *
+ * @param {color} colInt any value of the color datatype
+ *
+ * @returns {float} The saturation color value.
+ *
+ * @see red
+ * @see green
+ * @see blue
+ * @see hue
+ * @see brightness
+ */
+ p.saturation = function(colInt){
+ return colorToHSB(colInt)[1];
+ };
+
+ /**
+ * Extracts the hue value from a color.
+ *
+ * @param {color} colInt any value of the color datatype
+ *
+ * @returns {float} The hue color value.
+ *
+ * @see red
+ * @see green
+ * @see blue
+ * @see saturation
+ * @see brightness
+ */
+ p.hue = function(colInt){
+ return colorToHSB(colInt)[0];
+ };
+
+ /**
+ * Extracts the red value from a color, scaled to match current colorMode().
+ * This value is always returned as a float so be careful not to assign it to an int value.
+ *
+ * @param {color} aColor any value of the color datatype
+ *
+ * @returns {float} The red color value.
+ *
+ * @see green
+ * @see blue
+ * @see alpha
+ * @see >> right shift
+ * @see hue
+ * @see saturation
+ * @see brightness
+ */
+ p.red = function(aColor) {
+ return ((aColor & PConstants.RED_MASK) >>> 16) / 255 * colorModeX;
+ };
+
+ /**
+ * Extracts the green value from a color, scaled to match current colorMode().
+ * This value is always returned as a float so be careful not to assign it to an int value.
+ *
+ * @param {color} aColor any value of the color datatype
+ *
+ * @returns {float} The green color value.
+ *
+ * @see red
+ * @see blue
+ * @see alpha
+ * @see >> right shift
+ * @see hue
+ * @see saturation
+ * @see brightness
+ */
+ p.green = function(aColor) {
+ return ((aColor & PConstants.GREEN_MASK) >>> 8) / 255 * colorModeY;
+ };
+
+ /**
+ * Extracts the blue value from a color, scaled to match current colorMode().
+ * This value is always returned as a float so be careful not to assign it to an int value.
+ *
+ * @param {color} aColor any value of the color datatype
+ *
+ * @returns {float} The blue color value.
+ *
+ * @see red
+ * @see green
+ * @see alpha
+ * @see >> right shift
+ * @see hue
+ * @see saturation
+ * @see brightness
+ */
+ p.blue = function(aColor) {
+ return (aColor & PConstants.BLUE_MASK) / 255 * colorModeZ;
+ };
+
+ /**
+ * Extracts the alpha value from a color, scaled to match current colorMode().
+ * This value is always returned as a float so be careful not to assign it to an int value.
+ *
+ * @param {color} aColor any value of the color datatype
+ *
+ * @returns {float} The alpha color value.
+ *
+ * @see red
+ * @see green
+ * @see blue
+ * @see >> right shift
+ * @see hue
+ * @see saturation
+ * @see brightness
+ */
+ p.alpha = function(aColor) {
+ return ((aColor & PConstants.ALPHA_MASK) >>> 24) / 255 * colorModeA;
+ };
+
+ /**
+ * Calculates a color or colors between two colors at a specific increment.
+ * The amt parameter is the amount to interpolate between the two values where 0.0
+ * equal to the first point, 0.1 is very near the first point, 0.5 is half-way in between, etc.
+ *
+ * @param {color} c1 interpolate from this color
+ * @param {color} c2 interpolate to this color
+ * @param {float} amt between 0.0 and 1.0
+ *
+ * @returns {float} The blended color.
+ *
+ * @see blendColor
+ * @see color
+ */
+ p.lerpColor = function(c1, c2, amt) {
+ var r, g, b, a, r1, g1, b1, a1, r2, g2, b2, a2;
+ var hsb1, hsb2, rgb, h, s;
+ var colorBits1 = p.color(c1);
+ var colorBits2 = p.color(c2);
+
+ if (curColorMode === PConstants.HSB) {
+ // Special processing for HSB mode.
+ // Get HSB and Alpha values for Color 1 and 2
+ hsb1 = colorToHSB(colorBits1);
+ a1 = ((colorBits1 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
+ hsb2 = colorToHSB(colorBits2);
+ a2 = ((colorBits2 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
+
+ // Return lerp value for each channel, for HSB components
+ h = p.lerp(hsb1[0], hsb2[0], amt);
+ s = p.lerp(hsb1[1], hsb2[1], amt);
+ b = p.lerp(hsb1[2], hsb2[2], amt);
+ rgb = p.color.toRGB(h, s, b);
+ // ... and for Alpha-range
+ a = (p.lerp(a1, a2, amt) * colorModeA + 0.5) | 0;
+
+ return (a << 24) & PConstants.ALPHA_MASK |
+ (rgb[0] << 16) & PConstants.RED_MASK |
+ (rgb[1] << 8) & PConstants.GREEN_MASK |
+ rgb[2] & PConstants.BLUE_MASK;
+ }
+
+ // Get RGBA values for Color 1 to floats
+ r1 = (colorBits1 & PConstants.RED_MASK) >>> 16;
+ g1 = (colorBits1 & PConstants.GREEN_MASK) >>> 8;
+ b1 = (colorBits1 & PConstants.BLUE_MASK);
+ a1 = ((colorBits1 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
+
+ // Get RGBA values for Color 2 to floats
+ r2 = (colorBits2 & PConstants.RED_MASK) >>> 16;
+ g2 = (colorBits2 & PConstants.GREEN_MASK) >>> 8;
+ b2 = (colorBits2 & PConstants.BLUE_MASK);
+ a2 = ((colorBits2 & PConstants.ALPHA_MASK) >>> 24) / colorModeA;
+
+ // Return lerp value for each channel, INT for color, Float for Alpha-range
+ r = (p.lerp(r1, r2, amt) + 0.5) | 0;
+ g = (p.lerp(g1, g2, amt) + 0.5) | 0;
+ b = (p.lerp(b1, b2, amt) + 0.5) | 0;
+ a = (p.lerp(a1, a2, amt) * colorModeA + 0.5) | 0;
+
+ return (a << 24) & PConstants.ALPHA_MASK |
+ (r << 16) & PConstants.RED_MASK |
+ (g << 8) & PConstants.GREEN_MASK |
+ b & PConstants.BLUE_MASK;
+ };
+
+ /**
+ * Changes the way Processing interprets color data. By default, fill(), stroke(), and background()
+ * colors are set by values between 0 and 255 using the RGB color model. It is possible to change the
+ * numerical range used for specifying colors and to switch color systems. For example, calling colorMode(RGB, 1.0)
+ * will specify that values are specified between 0 and 1. The limits for defining colors are altered by setting the
+ * parameters range1, range2, range3, and range 4.
+ *
+ * @param {MODE} mode Either RGB or HSB, corresponding to Red/Green/Blue and Hue/Saturation/Brightness
+ * @param {int|float} range range for all color elements
+ * @param {int|float} range1 range for the red or hue depending on the current color mode
+ * @param {int|float} range2 range for the green or saturation depending on the current color mode
+ * @param {int|float} range3 range for the blue or brightness depending on the current color mode
+ * @param {int|float} range4 range for the alpha
+ *
+ * @returns none
+ *
+ * @see background
+ * @see fill
+ * @see stroke
+ */
+ p.colorMode = function() { // mode, range1, range2, range3, range4
+ curColorMode = arguments[0];
+ if (arguments.length > 1) {
+ colorModeX = arguments[1];
+ colorModeY = arguments[2] || arguments[1];
+ colorModeZ = arguments[3] || arguments[1];
+ colorModeA = arguments[4] || arguments[1];
+ }
+ };
+
+ /**
+ * Blends two color values together based on the blending mode given as the MODE parameter.
+ * The possible modes are described in the reference for the blend() function.
+ *
+ * @param {color} c1 color: the first color to blend
+ * @param {color} c2 color: the second color to blend
+ * @param {MODE} MODE Either BLEND, ADD, SUBTRACT, DARKEST, LIGHTEST, DIFFERENCE, EXCLUSION, MULTIPLY,
+ * SCREEN, OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, or BURN
+ *
+ * @returns {float} The blended color.
+ *
+ * @see blend
+ * @see color
+ */
+ p.blendColor = function(c1, c2, mode) {
+ if (mode === PConstants.REPLACE) {
+ return p.modes.replace(c1, c2);
+ } else if (mode === PConstants.BLEND) {
+ return p.modes.blend(c1, c2);
+ } else if (mode === PConstants.ADD) {
+ return p.modes.add(c1, c2);
+ } else if (mode === PConstants.SUBTRACT) {
+ return p.modes.subtract(c1, c2);
+ } else if (mode === PConstants.LIGHTEST) {
+ return p.modes.lightest(c1, c2);
+ } else if (mode === PConstants.DARKEST) {
+ return p.modes.darkest(c1, c2);
+ } else if (mode === PConstants.DIFFERENCE) {
+ return p.modes.difference(c1, c2);
+ } else if (mode === PConstants.EXCLUSION) {
+ return p.modes.exclusion(c1, c2);
+ } else if (mode === PConstants.MULTIPLY) {
+ return p.modes.multiply(c1, c2);
+ } else if (mode === PConstants.SCREEN) {
+ return p.modes.screen(c1, c2);
+ } else if (mode === PConstants.HARD_LIGHT) {
+ return p.modes.hard_light(c1, c2);
+ } else if (mode === PConstants.SOFT_LIGHT) {
+ return p.modes.soft_light(c1, c2);
+ } else if (mode === PConstants.OVERLAY) {
+ return p.modes.overlay(c1, c2);
+ } else if (mode === PConstants.DODGE) {
+ return p.modes.dodge(c1, c2);
+ } else if (mode === PConstants.BURN) {
+ return p.modes.burn(c1, c2);
+ }
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Canvas-Matrix manipulation
+ ////////////////////////////////////////////////////////////////////////////
+
+ function saveContext() {
+ curContext.save();
+ }
+
+ function restoreContext() {
+ curContext.restore();
+ isStrokeDirty = true;
+ isFillDirty = true;
+ }
+
+ /**
+ * Prints the current matrix to the text window.
+ *
+ * @returns none
+ *
+ * @see pushMatrix
+ * @see popMatrix
+ * @see resetMatrix
+ * @see applyMatrix
+ */
+ p.printMatrix = function() {
+ modelView.print();
+ };
+
+ /**
+ * Specifies an amount to displace objects within the display window. The x parameter specifies left/right translation,
+ * the y parameter specifies up/down translation, and the z parameter specifies translations toward/away from the screen.
+ * Using this function with the z parameter requires using the P3D or OPENGL parameter in combination with size as shown
+ * in the above example. Transformations apply to everything that happens after and subsequent calls to the function
+ * accumulates the effect. For example, calling translate(50, 0) and then translate(20, 0) is the same as translate(70, 0).
+ * If translate() is called within draw(), the transformation is reset when the loop begins again.
+ * This function can be further controlled by the pushMatrix() and popMatrix().
+ *
+ * @param {int|float} x left/right translation
+ * @param {int|float} y up/down translation
+ * @param {int|float} z forward/back translation
+ *
+ * @returns none
+ *
+ * @see pushMatrix
+ * @see popMatrix
+ * @see scale
+ * @see rotate
+ * @see rotateX
+ * @see rotateY
+ * @see rotateZ
+ */
+ Drawing2D.prototype.translate = function(x, y) {
+ modelView.translate(x, y);
+ modelViewInv.invTranslate(x, y);
+ curContext.translate(x, y);
+ };
+
+ Drawing3D.prototype.translate = function(x, y, z) {
+ modelView.translate(x, y, z);
+ modelViewInv.invTranslate(x, y, z);
+ };
+
+ /**
+ * Increases or decreases the size of a shape by expanding and contracting vertices. Objects always scale from their
+ * relative origin to the coordinate system. Scale values are specified as decimal percentages. For example, the
+ * function call scale(2.0) increases the dimension of a shape by 200%. Transformations apply to everything that
+ * happens after and subsequent calls to the function multiply the effect. For example, calling scale(2.0) and
+ * then scale(1.5) is the same as scale(3.0). If scale() is called within draw(), the transformation is reset when
+ * the loop begins again. Using this fuction with the z parameter requires passing P3D or OPENGL into the size()
+ * parameter as shown in the example above. This function can be further controlled by pushMatrix() and popMatrix().
+ *
+ * @param {int|float} size percentage to scale the object
+ * @param {int|float} x percentage to scale the object in the x-axis
+ * @param {int|float} y percentage to scale the object in the y-axis
+ * @param {int|float} z percentage to scale the object in the z-axis
+ *
+ * @returns none
+ *
+ * @see pushMatrix
+ * @see popMatrix
+ * @see translate
+ * @see rotate
+ * @see rotateX
+ * @see rotateY
+ * @see rotateZ
+ */
+ Drawing2D.prototype.scale = function(x, y) {
+ modelView.scale(x, y);
+ modelViewInv.invScale(x, y);
+ curContext.scale(x, y || x);
+ };
+
+ Drawing3D.prototype.scale = function(x, y, z) {
+ modelView.scale(x, y, z);
+ modelViewInv.invScale(x, y, z);
+ };
+
+
+ /**
+ * helper function for applying a transfrom matrix to a 2D context.
+ */
+ Drawing2D.prototype.transform = function(pmatrix) {
+ var e = pmatrix.array();
+ curContext.transform(e[0],e[3],e[1],e[4],e[2],e[5]);
+ };
+
+ /**
+ * helper function for applying a transfrom matrix to a 3D context.
+ * not currently implemented.
+ */
+ Drawing3D.prototype.transformm = function(pmatrix3d) {
+ throw("p.transform is currently not supported in 3D mode");
+ };
+
+
+ /**
+ * Pushes the current transformation matrix onto the matrix stack. Understanding pushMatrix() and popMatrix()
+ * requires understanding the concept of a matrix stack. The pushMatrix() function saves the current coordinate
+ * system to the stack and popMatrix() restores the prior coordinate system. pushMatrix() and popMatrix() are
+ * used in conjuction with the other transformation methods and may be embedded to control the scope of
+ * the transformations.
+ *
+ * @returns none
+ *
+ * @see popMatrix
+ * @see translate
+ * @see rotate
+ * @see rotateX
+ * @see rotateY
+ * @see rotateZ
+ */
+ Drawing2D.prototype.pushMatrix = function() {
+ userMatrixStack.load(modelView);
+ userReverseMatrixStack.load(modelViewInv);
+ saveContext();
+ };
+
+ Drawing3D.prototype.pushMatrix = function() {
+ userMatrixStack.load(modelView);
+ userReverseMatrixStack.load(modelViewInv);
+ };
+
+ /**
+ * Pops the current transformation matrix off the matrix stack. Understanding pushing and popping requires
+ * understanding the concept of a matrix stack. The pushMatrix() function saves the current coordinate system to
+ * the stack and popMatrix() restores the prior coordinate system. pushMatrix() and popMatrix() are used in
+ * conjuction with the other transformation methods and may be embedded to control the scope of the transformations.
+ *
+ * @returns none
+ *
+ * @see popMatrix
+ * @see pushMatrix
+ */
+ Drawing2D.prototype.popMatrix = function() {
+ modelView.set(userMatrixStack.pop());
+ modelViewInv.set(userReverseMatrixStack.pop());
+ restoreContext();
+ };
+
+ Drawing3D.prototype.popMatrix = function() {
+ modelView.set(userMatrixStack.pop());
+ modelViewInv.set(userReverseMatrixStack.pop());
+ };
+
+ /**
+ * Replaces the current matrix with the identity matrix. The equivalent function in OpenGL is glLoadIdentity().
+ *
+ * @returns none
+ *
+ * @see popMatrix
+ * @see pushMatrix
+ * @see applyMatrix
+ * @see printMatrix
+ */
+ Drawing2D.prototype.resetMatrix = function() {
+ modelView.reset();
+ modelViewInv.reset();
+ curContext.setTransform(1,0,0,1,0,0);
+ };
+
+ Drawing3D.prototype.resetMatrix = function() {
+ modelView.reset();
+ modelViewInv.reset();
+ };
+
+ /**
+ * Multiplies the current matrix by the one specified through the parameters. This is very slow because it will
+ * try to calculate the inverse of the transform, so avoid it whenever possible. The equivalent function
+ * in OpenGL is glMultMatrix().
+ *
+ * @param {int|float} n00-n15 numbers which define the 4x4 matrix to be multiplied
+ *
+ * @returns none
+ *
+ * @see popMatrix
+ * @see pushMatrix
+ * @see resetMatrix
+ * @see printMatrix
+ */
+ DrawingShared.prototype.applyMatrix = function() {
+ var a = arguments;
+ modelView.apply(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
+ modelViewInv.invApply(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
+ };
+
+ Drawing2D.prototype.applyMatrix = function() {
+ var a = arguments;
+ for (var cnt = a.length; cnt < 16; cnt++) {
+ a[cnt] = 0;
+ }
+ a[10] = a[15] = 1;
+ DrawingShared.prototype.applyMatrix.apply(this, a);
+ };
+
+ /**
+ * Rotates a shape around the x-axis the amount specified by the angle parameter. Angles should be
+ * specified in radians (values from 0 to PI*2) or converted to radians with the radians() function.
+ * Objects are always rotated around their relative position to the origin and positive numbers
+ * rotate objects in a counterclockwise direction. Transformations apply to everything that happens
+ * after and subsequent calls to the function accumulates the effect. For example, calling rotateX(PI/2)
+ * and then rotateX(PI/2) is the same as rotateX(PI). If rotateX() is called within the draw(), the
+ * transformation is reset when the loop begins again. This function requires passing P3D or OPENGL
+ * into the size() parameter as shown in the example above.
+ *
+ * @param {int|float} angleInRadians angle of rotation specified in radians
+ *
+ * @returns none
+ *
+ * @see rotateY
+ * @see rotateZ
+ * @see rotate
+ * @see translate
+ * @see scale
+ * @see popMatrix
+ * @see pushMatrix
+ */
+ p.rotateX = function(angleInRadians) {
+ modelView.rotateX(angleInRadians);
+ modelViewInv.invRotateX(angleInRadians);
+ };
+
+ /**
+ * Rotates a shape around the z-axis the amount specified by the angle parameter. Angles should be
+ * specified in radians (values from 0 to PI*2) or converted to radians with the radians() function.
+ * Objects are always rotated around their relative position to the origin and positive numbers
+ * rotate objects in a counterclockwise direction. Transformations apply to everything that happens
+ * after and subsequent calls to the function accumulates the effect. For example, calling rotateZ(PI/2)
+ * and then rotateZ(PI/2) is the same as rotateZ(PI). If rotateZ() is called within the draw(), the
+ * transformation is reset when the loop begins again. This function requires passing P3D or OPENGL
+ * into the size() parameter as shown in the example above.
+ *
+ * @param {int|float} angleInRadians angle of rotation specified in radians
+ *
+ * @returns none
+ *
+ * @see rotateX
+ * @see rotateY
+ * @see rotate
+ * @see translate
+ * @see scale
+ * @see popMatrix
+ * @see pushMatrix
+ */
+ Drawing2D.prototype.rotateZ = function() {
+ throw "rotateZ() is not supported in 2D mode. Use rotate(float) instead.";
+ };
+
+ Drawing3D.prototype.rotateZ = function(angleInRadians) {
+ modelView.rotateZ(angleInRadians);
+ modelViewInv.invRotateZ(angleInRadians);
+ };
+
+ /**
+ * Rotates a shape around the y-axis the amount specified by the angle parameter. Angles should be
+ * specified in radians (values from 0 to PI*2) or converted to radians with the radians() function.
+ * Objects are always rotated around their relative position to the origin and positive numbers
+ * rotate objects in a counterclockwise direction. Transformations apply to everything that happens
+ * after and subsequent calls to the function accumulates the effect. For example, calling rotateY(PI/2)
+ * and then rotateY(PI/2) is the same as rotateY(PI). If rotateY() is called within the draw(), the
+ * transformation is reset when the loop begins again. This function requires passing P3D or OPENGL
+ * into the size() parameter as shown in the example above.
+ *
+ * @param {int|float} angleInRadians angle of rotation specified in radians
+ *
+ * @returns none
+ *
+ * @see rotateX
+ * @see rotateZ
+ * @see rotate
+ * @see translate
+ * @see scale
+ * @see popMatrix
+ * @see pushMatrix
+ */
+ p.rotateY = function(angleInRadians) {
+ modelView.rotateY(angleInRadians);
+ modelViewInv.invRotateY(angleInRadians);
+ };
+
+ /**
+ * Rotates a shape the amount specified by the angle parameter. Angles should be specified in radians
+ * (values from 0 to TWO_PI) or converted to radians with the radians() function. Objects are always
+ * rotated around their relative position to the origin and positive numbers rotate objects in a
+ * clockwise direction. Transformations apply to everything that happens after and subsequent calls
+ * to the function accumulates the effect. For example, calling rotate(HALF_PI) and then rotate(HALF_PI)
+ * is the same as rotate(PI). All tranformations are reset when draw() begins again. Technically,
+ * rotate() multiplies the current transformation matrix by a rotation matrix. This function can be
+ * further controlled by the pushMatrix() and popMatrix().
+ *
+ * @param {int|float} angleInRadians angle of rotation specified in radians
+ *
+ * @returns none
+ *
+ * @see rotateX
+ * @see rotateY
+ * @see rotateZ
+ * @see rotate
+ * @see translate
+ * @see scale
+ * @see popMatrix
+ * @see pushMatrix
+ */
+ Drawing2D.prototype.rotate = function(angleInRadians) {
+ modelView.rotateZ(angleInRadians);
+ modelViewInv.invRotateZ(angleInRadians);
+ curContext.rotate(angleInRadians);
+ };
+
+ Drawing3D.prototype.rotate = function(angleInRadians) {
+ if (arguments.length < 4) {
+ p.rotateZ(angleInRadians);
+ } else {
+ modelView.rotate(angleInRadians, arguments[1], arguments[2], arguments[3]);
+ modelViewInv.rotate((-angleInRadians), arguments[1], arguments[2], arguments[3]);
+ }
+ };
+
+ /**
+ * Shears a shape around the x-axis the amount specified by the angle parameter.
+ * Angles should be specified in radians (values from 0 to PI*2) or converted to radians
+ * with the radians() function. Objects are always sheared around their relative position
+ * to the origin and positive numbers shear objects in a clockwise direction. Transformations
+ * apply to everything that happens after and subsequent calls to the function accumulates the
+ * effect. For example, calling shearX(PI/2) and then shearX(PI/2) is the same as shearX(PI)
+ *
+ * @param {int|float} angleInRadians angle of rotation specified in radians
+ *
+ * @returns none
+ *
+ * @see rotateX
+ * @see rotateY
+ * @see rotateZ
+ * @see rotate
+ * @see translate
+ * @see scale
+ * @see popMatrix
+ * @see pushMatrix
+ */
+
+ Drawing2D.prototype.shearX = function(angleInRadians) {
+ modelView.shearX(angleInRadians);
+ curContext.transform(1,0,angleInRadians,1,0,0);
+ };
+
+ Drawing3D.prototype.shearX = function(angleInRadians) {
+ modelView.shearX(angleInRadians);
+ };
+
+ /**
+ * Shears a shape around the y-axis the amount specified by the angle parameter.
+ * Angles should be specified in radians (values from 0 to PI*2) or converted to
+ * radians with the radians() function. Objects are always sheared around their
+ * relative position to the origin and positive numbers shear objects in a
+ * clockwise direction. Transformations apply to everything that happens after
+ * and subsequent calls to the function accumulates the effect. For example,
+ * calling shearY(PI/2) and then shearY(PI/2) is the same as shearY(PI).
+ *
+ * @param {int|float} angleInRadians angle of rotation specified in radians
+ *
+ * @returns none
+ *
+ * @see rotateX
+ * @see rotateY
+ * @see rotateZ
+ * @see rotate
+ * @see translate
+ * @see scale
+ * @see popMatrix
+ * @see pushMatrix
+ * @see shearX
+ */
+
+ Drawing2D.prototype.shearY = function(angleInRadians) {
+ modelView.shearY(angleInRadians);
+ curContext.transform(1,angleInRadians,0,1,0,0);
+ };
+
+ Drawing3D.prototype.shearY = function(angleInRadians) {
+ modelView.shearY(angleInRadians);
+ };
+
+ /**
+ * The pushStyle() function saves the current style settings and popStyle() restores the prior settings.
+ * Note that these functions are always used together. They allow you to change the style settings and later
+ * return to what you had. When a new style is started with pushStyle(), it builds on the current style information.
+ * The pushStyle() and popStyle() functions can be embedded to provide more control (see the second example
+ * above for a demonstration.)
+ * The style information controlled by the following functions are included in the style: fill(), stroke(), tint(),
+ * strokeWeight(), strokeCap(), strokeJoin(), imageMode(), rectMode(), ellipseMode(), shapeMode(), colorMode(),
+ * textAlign(), textFont(), textMode(), textSize(), textLeading(), emissive(), specular(), shininess(), ambient()
+ *
+ * @returns none
+ *
+ * @see popStyle
+ */
+ p.pushStyle = function() {
+ // Save the canvas state.
+ saveContext();
+
+ p.pushMatrix();
+
+ var newState = {
+ 'doFill': doFill,
+ 'currentFillColor': currentFillColor,
+ 'doStroke': doStroke,
+ 'currentStrokeColor': currentStrokeColor,
+ 'curTint': curTint,
+ 'curRectMode': curRectMode,
+ 'curColorMode': curColorMode,
+ 'colorModeX': colorModeX,
+ 'colorModeZ': colorModeZ,
+ 'colorModeY': colorModeY,
+ 'colorModeA': colorModeA,
+ 'curTextFont': curTextFont,
+ 'horizontalTextAlignment': horizontalTextAlignment,
+ 'verticalTextAlignment': verticalTextAlignment,
+ 'textMode': textMode,
+ 'curFontName': curFontName,
+ 'curTextSize': curTextSize,
+ 'curTextAscent': curTextAscent,
+ 'curTextDescent': curTextDescent,
+ 'curTextLeading': curTextLeading
+ };
+
+ styleArray.push(newState);
+ };
+
+ /**
+ * The pushStyle() function saves the current style settings and popStyle() restores the prior settings; these
+ * functions are always used together. They allow you to change the style settings and later return to what you had.
+ * When a new style is started with pushStyle(), it builds on the current style information. The pushStyle() and
+ * popStyle() functions can be embedded to provide more control (see the second example above for a demonstration.)
+ *
+ * @returns none
+ *
+ * @see pushStyle
+ */
+ p.popStyle = function() {
+ var oldState = styleArray.pop();
+
+ if (oldState) {
+ restoreContext();
+
+ p.popMatrix();
+
+ doFill = oldState.doFill;
+ currentFillColor = oldState.currentFillColor;
+ doStroke = oldState.doStroke;
+ currentStrokeColor = oldState.currentStrokeColor;
+ curTint = oldState.curTint;
+ curRectMode = oldState.curRectMode;
+ curColorMode = oldState.curColorMode;
+ colorModeX = oldState.colorModeX;
+ colorModeZ = oldState.colorModeZ;
+ colorModeY = oldState.colorModeY;
+ colorModeA = oldState.colorModeA;
+ curTextFont = oldState.curTextFont;
+ curFontName = oldState.curFontName;
+ curTextSize = oldState.curTextSize;
+ horizontalTextAlignment = oldState.horizontalTextAlignment;
+ verticalTextAlignment = oldState.verticalTextAlignment;
+ textMode = oldState.textMode;
+ curTextAscent = oldState.curTextAscent;
+ curTextDescent = oldState.curTextDescent;
+ curTextLeading = oldState.curTextLeading;
+ } else {
+ throw "Too many popStyle() without enough pushStyle()";
+ }
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Time based functions
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Processing communicates with the clock on your computer.
+ * The year() function returns the current year as an integer (2003, 2004, 2005, etc).
+ *
+ * @returns {float} The current year.
+ *
+ * @see millis
+ * @see second
+ * @see minute
+ * @see hour
+ * @see day
+ * @see month
+ */
+ p.year = function() {
+ return new Date().getFullYear();
+ };
+ /**
+ * Processing communicates with the clock on your computer.
+ * The month() function returns the current month as a value from 1 - 12.
+ *
+ * @returns {float} The current month.
+ *
+ * @see millis
+ * @see second
+ * @see minute
+ * @see hour
+ * @see day
+ * @see year
+ */
+ p.month = function() {
+ return new Date().getMonth() + 1;
+ };
+ /**
+ * Processing communicates with the clock on your computer.
+ * The day() function returns the current day as a value from 1 - 31.
+ *
+ * @returns {float} The current day.
+ *
+ * @see millis
+ * @see second
+ * @see minute
+ * @see hour
+ * @see month
+ * @see year
+ */
+ p.day = function() {
+ return new Date().getDate();
+ };
+ /**
+ * Processing communicates with the clock on your computer.
+ * The hour() function returns the current hour as a value from 0 - 23.
+ *
+ * @returns {float} The current hour.
+ *
+ * @see millis
+ * @see second
+ * @see minute
+ * @see month
+ * @see day
+ * @see year
+ */
+ p.hour = function() {
+ return new Date().getHours();
+ };
+ /**
+ * Processing communicates with the clock on your computer.
+ * The minute() function returns the current minute as a value from 0 - 59.
+ *
+ * @returns {float} The current minute.
+ *
+ * @see millis
+ * @see second
+ * @see month
+ * @see hour
+ * @see day
+ * @see year
+ */
+ p.minute = function() {
+ return new Date().getMinutes();
+ };
+ /**
+ * Processing communicates with the clock on your computer.
+ * The second() function returns the current second as a value from 0 - 59.
+ *
+ * @returns {float} The current minute.
+ *
+ * @see millis
+ * @see month
+ * @see minute
+ * @see hour
+ * @see day
+ * @see year
+ */
+ p.second = function() {
+ return new Date().getSeconds();
+ };
+ /**
+ * Returns the number of milliseconds (thousandths of a second) since starting a sketch.
+ * This information is often used for timing animation sequences.
+ *
+ * @returns {long} The number of milliseconds since starting the sketch.
+ *
+ * @see month
+ * @see second
+ * @see minute
+ * @see hour
+ * @see day
+ * @see year
+ */
+ p.millis = function() {
+ return Date.now() - start;
+ };
+
+ /**
+ * Executes the code within draw() one time. This functions allows the program to update
+ * the display window only when necessary, for example when an event registered by
+ * mousePressed() or keyPressed() occurs.
+ * In structuring a program, it only makes sense to call redraw() within events such as
+ * mousePressed(). This is because redraw() does not run draw() immediately (it only sets
+ * a flag that indicates an update is needed).
+ * Calling redraw() within draw() has no effect because draw() is continuously called anyway.
+ *
+ * @returns none
+ *
+ * @see noLoop
+ * @see loop
+ */
+ function redrawHelper() {
+ var sec = (Date.now() - timeSinceLastFPS) / 1000;
+ framesSinceLastFPS++;
+ var fps = framesSinceLastFPS / sec;
+
+ // recalculate FPS every half second for better accuracy.
+ if (sec > 0.5) {
+ timeSinceLastFPS = Date.now();
+ framesSinceLastFPS = 0;
+ p.__frameRate = fps;
+ }
+
+ p.frameCount++;
+ }
+
+ Drawing2D.prototype.redraw = function() {
+ redrawHelper();
+
+ curContext.lineWidth = lineWidth;
+ var pmouseXLastEvent = p.pmouseX,
+ pmouseYLastEvent = p.pmouseY;
+ p.pmouseX = pmouseXLastFrame;
+ p.pmouseY = pmouseYLastFrame;
+
+ saveContext();
+ p.draw();
+ restoreContext();
+
+ pmouseXLastFrame = p.mouseX;
+ pmouseYLastFrame = p.mouseY;
+ p.pmouseX = pmouseXLastEvent;
+ p.pmouseY = pmouseYLastEvent;
+ };
+
+ Drawing3D.prototype.redraw = function() {
+ redrawHelper();
+
+ var pmouseXLastEvent = p.pmouseX,
+ pmouseYLastEvent = p.pmouseY;
+ p.pmouseX = pmouseXLastFrame;
+ p.pmouseY = pmouseYLastFrame;
+ // even if the color buffer isn't cleared with background(),
+ // the depth buffer needs to be cleared regardless.
+ curContext.clear(curContext.DEPTH_BUFFER_BIT);
+ curContextCache = { attributes: {}, locations: {} };
+ // Delete all the lighting states and the materials the
+ // user set in the last draw() call.
+ p.noLights();
+ p.lightFalloff(1, 0, 0);
+ p.shininess(1);
+ p.ambient(255, 255, 255);
+ p.specular(0, 0, 0);
+ p.emissive(0, 0, 0);
+ p.camera();
+ p.draw();
+
+ pmouseXLastFrame = p.mouseX;
+ pmouseYLastFrame = p.mouseY;
+ p.pmouseX = pmouseXLastEvent;
+ p.pmouseY = pmouseYLastEvent;
+ };
+
+ /**
+ * Stops Processing from continuously executing the code within draw(). If loop() is
+ * called, the code in draw() begin to run continuously again. If using noLoop() in
+ * setup(), it should be the last line inside the block.
+ * When noLoop() is used, it's not possible to manipulate or access the screen inside event
+ * handling functions such as mousePressed() or keyPressed(). Instead, use those functions
+ * to call redraw() or loop(), which will run draw(), which can update the screen properly.
+ * This means that when noLoop() has been called, no drawing can happen, and functions like
+ * saveFrame() or loadPixels() may not be used.
+ * Note that if the sketch is resized, redraw() will be called to update the sketch, even
+ * after noLoop() has been specified. Otherwise, the sketch would enter an odd state until
+ * loop() was called.
+ *
+ * @returns none
+ *
+ * @see redraw
+ * @see draw
+ * @see loop
+ */
+ p.noLoop = function() {
+ doLoop = false;
+ loopStarted = false;
+ clearInterval(looping);
+ curSketch.onPause();
+ };
+
+ /**
+ * Causes Processing to continuously execute the code within draw(). If noLoop() is called,
+ * the code in draw() stops executing.
+ *
+ * @returns none
+ *
+ * @see noLoop
+ */
+ p.loop = function() {
+ if (loopStarted) {
+ return;
+ }
+
+ timeSinceLastFPS = Date.now();
+ framesSinceLastFPS = 0;
+
+ looping = window.setInterval(function() {
+ try {
+ curSketch.onFrameStart();
+ p.redraw();
+ curSketch.onFrameEnd();
+ } catch(e_loop) {
+ window.clearInterval(looping);
+ throw e_loop;
+ }
+ }, curMsPerFrame);
+ doLoop = true;
+ loopStarted = true;
+ curSketch.onLoop();
+ };
+
+ /**
+ * Specifies the number of frames to be displayed every second. If the processor is not
+ * fast enough to maintain the specified rate, it will not be achieved. For example, the
+ * function call frameRate(30) will attempt to refresh 30 times a second. It is recommended
+ * to set the frame rate within setup(). The default rate is 60 frames per second.
+ *
+ * @param {int} aRate number of frames per second.
+ *
+ * @returns none
+ *
+ * @see delay
+ */
+ p.frameRate = function(aRate) {
+ curFrameRate = aRate;
+ curMsPerFrame = 1000 / curFrameRate;
+
+ // clear and reset interval
+ if (doLoop) {
+ p.noLoop();
+ p.loop();
+ }
+ };
+
+ /**
+ * Quits/stops/exits the program.
+ * Rather than terminating immediately, exit() will cause the sketch to exit after draw()
+ * has completed (or after setup() completes if called during the setup() method).
+ *
+ * @returns none
+ */
+ p.exit = function() {
+ // cleanup
+ window.clearInterval(looping);
+ removeInstance(p.externals.canvas.id);
+ delete(curElement.onmousedown);
+
+ // Step through the libraries to detach them
+ for (var lib in Processing.lib) {
+ if (Processing.lib.hasOwnProperty(lib)) {
+ if (Processing.lib[lib].hasOwnProperty("detach")) {
+ Processing.lib[lib].detach(p);
+ }
+ }
+ }
+
+ // clean up all event handling
+ var i = eventHandlers.length;
+ while (i--) {
+ detachEventHandler(eventHandlers[i]);
+ }
+ curSketch.onExit();
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // MISC functions
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Sets the cursor to a predefined symbol, an image, or turns it on if already hidden.
+ * If you are trying to set an image as the cursor, it is recommended to make the size
+ * 16x16 or 32x32 pixels. It is not possible to load an image as the cursor if you are
+ * exporting your program for the Web. The values for parameters x and y must be less
+ * than the dimensions of the image.
+ *
+ * @param {MODE} MODE either ARROW, CROSS, HAND, MOVE, TEXT, WAIT
+ * @param {PImage} image any variable of type PImage
+ * @param {int} x the horizonal active spot of the cursor
+ * @param {int} y the vertical active spot of the cursor
+ *
+ * @returns none
+ *
+ * @see noCursor
+ */
+ p.cursor = function() {
+ if (arguments.length > 1 || (arguments.length === 1 && arguments[0] instanceof p.PImage)) {
+ var image = arguments[0],
+ x, y;
+ if (arguments.length >= 3) {
+ x = arguments[1];
+ y = arguments[2];
+ if (x < 0 || y < 0 || y >= image.height || x >= image.width) {
+ throw "x and y must be non-negative and less than the dimensions of the image";
+ }
+ } else {
+ x = image.width >>> 1;
+ y = image.height >>> 1;
+ }
+
+ // see https://developer.mozilla.org/en/Using_URL_values_for_the_cursor_property
+ var imageDataURL = image.toDataURL();
+ var style = "url(\"" + imageDataURL + "\") " + x + " " + y + ", default";
+ curCursor = curElement.style.cursor = style;
+ } else if (arguments.length === 1) {
+ var mode = arguments[0];
+ curCursor = curElement.style.cursor = mode;
+ } else {
+ curCursor = curElement.style.cursor = oldCursor;
+ }
+ };
+
+ /**
+ * Hides the cursor from view.
+ *
+ * @returns none
+ *
+ * @see cursor
+ */
+ p.noCursor = function() {
+ curCursor = curElement.style.cursor = PConstants.NOCURSOR;
+ };
+
+ /**
+ * Links to a webpage either in the same window or in a new window. The complete URL
+ * must be specified.
+ *
+ * @param {String} href complete url as a String in quotes
+ * @param {String} target name of the window to load the URL as a string in quotes
+ *
+ * @returns none
+ */
+ p.link = function(href, target) {
+ if (target !== undef) {
+ window.open(href, target);
+ } else {
+ window.location = href;
+ }
+ };
+
+ // PGraphics methods
+ // These functions exist only for compatibility with P5
+ p.beginDraw = noop;
+ p.endDraw = noop;
+
+ /**
+ * This function takes content from a canvas and turns it into an ImageData object to be used with a PImage
+ *
+ * @returns {ImageData} ImageData object to attach to a PImage (1D array of pixel data)
+ *
+ * @see PImage
+ */
+ Drawing2D.prototype.toImageData = function(x, y, w, h) {
+ x = x !== undef ? x : 0;
+ y = y !== undef ? y : 0;
+ w = w !== undef ? w : p.width;
+ h = h !== undef ? h : p.height;
+ return curContext.getImageData(x, y, w, h);
+ };
+
+ Drawing3D.prototype.toImageData = function(x, y, w, h) {
+ x = x !== undef ? x : 0;
+ y = y !== undef ? y : 0;
+ w = w !== undef ? w : p.width;
+ h = h !== undef ? h : p.height;
+ var c = document.createElement("canvas"),
+ ctx = c.getContext("2d"),
+ obj = ctx.createImageData(w, h),
+ uBuff = new Uint8Array(w * h * 4);
+ curContext.readPixels(x, y, w, h, curContext.RGBA, curContext.UNSIGNED_BYTE, uBuff);
+ for (var i=0, ul=uBuff.length, obj_data=obj.data; i < ul; i++) {
+ obj_data[i] = uBuff[(h - 1 - Math.floor(i / 4 / w)) * w * 4 + (i % (w * 4))];
+ }
+ return obj;
+ };
+
+ /**
+ * Displays message in the browser's status area. This is the text area in the lower
+ * left corner of the browser. The status() function will only work when the
+ * Processing program is running in a web browser.
+ *
+ * @param {String} text any valid String
+ *
+ * @returns none
+ */
+ p.status = function(text) {
+ window.status = text;
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Binary Functions
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Converts a byte, char, int, or color to a String containing the equivalent binary
+ * notation. For example color(0, 102, 153, 255) will convert to the String
+ * "11111111000000000110011010011001". This function can help make your geeky debugging
+ * sessions much happier.
+ *
+ * @param {byte|char|int|color} num byte, char, int, color: value to convert
+ * @param {int} numBits number of digits to return
+ *
+ * @returns {String}
+ *
+ * @see unhex
+ * @see hex
+ * @see unbinary
+ */
+ p.binary = function(num, numBits) {
+ var bit;
+ if (numBits > 0) {
+ bit = numBits;
+ } else if(num instanceof Char) {
+ bit = 16;
+ num |= 0; // making it int
+ } else {
+ // autodetect, skipping zeros
+ bit = 32;
+ while (bit > 1 && !((num >>> (bit - 1)) & 1)) {
+ bit--;
+ }
+ }
+ var result = "";
+ while (bit > 0) {
+ result += ((num >>> (--bit)) & 1) ? "1" : "0";
+ }
+ return result;
+ };
+
+ /**
+ * Converts a String representation of a binary number to its equivalent integer value.
+ * For example, unbinary("00001000") will return 8.
+ *
+ * @param {String} binaryString String
+ *
+ * @returns {Int}
+ *
+ * @see hex
+ * @see binary
+ * @see unbinary
+ */
+ p.unbinary = function(binaryString) {
+ var i = binaryString.length - 1, mask = 1, result = 0;
+ while (i >= 0) {
+ var ch = binaryString[i--];
+ if (ch !== '0' && ch !== '1') {
+ throw "the value passed into unbinary was not an 8 bit binary number";
+ }
+ if (ch === '1') {
+ result += mask;
+ }
+ mask <<= 1;
+ }
+ return result;
+ };
+
+ var decimalToHex = function(d, padding) {
+ //if there is no padding value added, default padding to 8 else go into while statement.
+ padding = (padding === undef || padding === null) ? padding = 8 : padding;
+ if (d < 0) {
+ d = 0xFFFFFFFF + d + 1;
+ }
+ var hex = Number(d).toString(16).toUpperCase();
+ while (hex.length < padding) {
+ hex = "0" + hex;
+ }
+ if (hex.length >= padding) {
+ hex = hex.substring(hex.length - padding, hex.length);
+ }
+ return hex;
+ };
+
+ // note: since we cannot keep track of byte, int types by default the returned string is 8 chars long
+ // if no 2nd argument is passed. closest compromise we can use to match java implementation Feb 5 2010
+ // also the char parser has issues with chars that are not digits or letters IE: !@#$%^&*
+ /**
+ * Converts a byte, char, int, or color to a String containing the equivalent hexadecimal notation.
+ * For example color(0, 102, 153, 255) will convert to the String "FF006699". This function can help
+ * make your geeky debugging sessions much happier.
+ *
+ * @param {byte|char|int|Color} value the value to turn into a hex string
+ * @param {int} digits the number of digits to return
+ *
+ * @returns {String}
+ *
+ * @see unhex
+ * @see binary
+ * @see unbinary
+ */
+ p.hex = function(value, len) {
+ if (arguments.length === 1) {
+ if (value instanceof Char) {
+ len = 4;
+ } else { // int or byte, indistinguishable at the moment, default to 8
+ len = 8;
+ }
+ }
+ return decimalToHex(value, len);
+ };
+
+ function unhexScalar(hex) {
+ var value = parseInt("0x" + hex, 16);
+
+ // correct for int overflow java expectation
+ if (value > 2147483647) {
+ value -= 4294967296;
+ }
+ return value;
+ }
+
+ /**
+ * Converts a String representation of a hexadecimal number to its equivalent integer value.
+ *
+ * @param {String} hex the hex string to convert to an int
+ *
+ * @returns {int}
+ *
+ * @see hex
+ * @see binary
+ * @see unbinary
+ */
+ p.unhex = function(hex) {
+ if (hex instanceof Array) {
+ var arr = [];
+ for (var i = 0; i < hex.length; i++) {
+ arr.push(unhexScalar(hex[i]));
+ }
+ return arr;
+ }
+ return unhexScalar(hex);
+ };
+
+ // Load a file or URL into strings
+ /**
+ * Reads the contents of a file or url and creates a String array of its individual lines.
+ * The filename parameter can also be a URL to a file found online. If the file is not available or an error occurs,
+ * null will be returned and an error message will be printed to the console. The error message does not halt
+ * the program.
+ *
+ * @param {String} filename name of the file or url to load
+ *
+ * @returns {String[]}
+ *
+ * @see loadBytes
+ * @see saveStrings
+ * @see saveBytes
+ */
+ p.loadStrings = function(filename) {
+ if (localStorage[filename]) {
+ return localStorage[filename].split("\n");
+ }
+
+ var filecontent = ajax(filename);
+ if(typeof filecontent !== "string" || filecontent === "") {
+ return [];
+ }
+
+ // deal with the fact that Windows uses \r\n, Unix uses \n,
+ // Mac uses \r, and we actually expect \n
+ filecontent = filecontent.replace(/(\r\n?)/g,"\n").replace(/\n$/,"");
+
+ return filecontent.split("\n");
+ };
+
+ // Writes an array of strings to a file, one line per string
+ /**
+ * Writes an array of strings to a file, one line per string. This file is saved to the localStorage.
+ *
+ * @param {String} filename name of the file to save to localStorage
+ * @param {String[]} strings string array to be written
+ *
+ * @see loadBytes
+ * @see loadStrings
+ * @see saveBytes
+ */
+ p.saveStrings = function(filename, strings) {
+ localStorage[filename] = strings.join('\n');
+ };
+
+ /**
+ * Reads the contents of a file or url and places it in a byte array. If a file is specified, it must be located in the localStorage.
+ * The filename parameter can also be a URL to a file found online.
+ *
+ * @param {String} filename name of a file in the localStorage or a URL.
+ *
+ * @returns {byte[]}
+ *
+ * @see loadStrings
+ * @see saveStrings
+ * @see saveBytes
+ */
+ p.loadBytes = function(url) {
+ var string = ajax(url);
+ var ret = [];
+
+ for (var i = 0; i < string.length; i++) {
+ ret.push(string.charCodeAt(i));
+ }
+
+ return ret;
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // String Functions
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * The matchAll() function is identical to match(), except that it returns an array of all matches in
+ * the specified String, rather than just the first.
+ *
+ * @param {String} aString the String to search inside
+ * @param {String} aRegExp the regexp to be used for matching
+ *
+ * @return {String[]} returns an array of matches
+ *
+ * @see #match
+ */
+ p.matchAll = function(aString, aRegExp) {
+ var results = [],
+ latest;
+ var regexp = new RegExp(aRegExp, "g");
+ while ((latest = regexp.exec(aString)) !== null) {
+ results.push(latest);
+ if (latest[0].length === 0) {
+ ++regexp.lastIndex;
+ }
+ }
+ return results.length > 0 ? results : null;
+ };
+ /**
+ * The match() function matches a string with a regular expression, and returns the match as an
+ * array. The first index is the matching expression, and array elements
+ * [1] and higher represent each of the groups (sequences found in parens).
+ *
+ * @param {String} str the String to be searched
+ * @param {String} regexp the regexp to be used for matching
+ *
+ * @return {String[]} an array of matching strings
+ */
+ p.match = function(str, regexp) {
+ return str.match(regexp);
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Other java specific functions
+ ////////////////////////////////////////////////////////////////////////////
+
+
+ var logBuffer = [];
+
+ /**
+ * The println() function writes to the console area of the Processing environment.
+ * Each call to this function creates a new line of output. Individual elements can be separated with quotes ("") and joined with the string concatenation operator (+).
+ *
+ * @param {String} message the string to write to the console
+ *
+ * @see #join
+ * @see #print
+ */
+ p.println = function() {
+ Processing.logger.println.apply(Processing.logger, arguments);
+ };
+ /**
+ * The print() function writes to the console area of the Processing environment.
+ *
+ * @param {String} message the string to write to the console
+ *
+ * @see #join
+ */
+ p.print = function() {
+ Processing.logger.print.apply(Processing.logger, arguments);
+ };
+
+ // Alphanumeric chars arguments automatically converted to numbers when
+ // passed in, and will come out as numbers.
+ p.str = function(val) {
+ if (val instanceof Array) {
+ var arr = [];
+ for (var i = 0; i < val.length; i++) {
+ arr.push(val[i].toString() + "");
+ }
+ return arr;
+ }
+ return (val.toString() + "");
+ };
+
+
+ // Conversion
+ function booleanScalar(val) {
+ if (typeof val === 'number') {
+ return val !== 0;
+ }
+ if (typeof val === 'boolean') {
+ return val;
+ }
+ if (typeof val === 'string') {
+ return val.toLowerCase() === 'true';
+ }
+ if (val instanceof Char) {
+ // 1, T or t
+ return val.code === 49 || val.code === 84 || val.code === 116;
+ }
+ }
+
+ /**
+ * Converts the passed parameter to the function to its boolean value.
+ * It will return an array of booleans if an array is passed in.
+ *
+ * @param {int, byte, string} val the parameter to be converted to boolean
+ * @param {int[], byte[], string[]} val the array to be converted to boolean[]
+ *
+ * @return {boolean|boolean[]} returns a boolean or an array of booleans
+ */
+ p.parseBoolean = function (val) {
+ if (val instanceof Array) {
+ var ret = [];
+ for (var i = 0; i < val.length; i++) {
+ ret.push(booleanScalar(val[i]));
+ }
+ return ret;
+ }
+ return booleanScalar(val);
+ };
+
+ /**
+ * Converts the passed parameter to the function to its byte value.
+ * A byte is a number between -128 and 127.
+ * It will return an array of bytes if an array is passed in.
+ *
+ * @param {int, char} what the parameter to be conveted to byte
+ * @param {int[], char[]} what the array to be converted to byte[]
+ *
+ * @return {byte|byte[]} returns a byte or an array of bytes
+ */
+ p.parseByte = function(what) {
+ if (what instanceof Array) {
+ var bytes = [];
+ for (var i = 0; i < what.length; i++) {
+ bytes.push((0 - (what[i] & 0x80)) | (what[i] & 0x7F));
+ }
+ return bytes;
+ }
+ return (0 - (what & 0x80)) | (what & 0x7F);
+ };
+
+ /**
+ * Converts the passed parameter to the function to its char value.
+ * It will return an array of chars if an array is passed in.
+ *
+ * @param {int, byte} key the parameter to be conveted to char
+ * @param {int[], byte[]} key the array to be converted to char[]
+ *
+ * @return {char|char[]} returns a char or an array of chars
+ */
+ p.parseChar = function(key) {
+ if (typeof key === "number") {
+ return new Char(String.fromCharCode(key & 0xFFFF));
+ }
+ if (key instanceof Array) {
+ var ret = [];
+ for (var i = 0; i < key.length; i++) {
+ ret.push(new Char(String.fromCharCode(key[i] & 0xFFFF)));
+ }
+ return ret;
+ }
+ throw "char() may receive only one argument of type int, byte, int[], or byte[].";
+ };
+
+ // Processing doc claims good argument types are: int, char, byte, boolean,
+ // String, int[], char[], byte[], boolean[], String[].
+ // floats should not work. However, floats with only zeroes right of the
+ // decimal will work because JS converts those to int.
+ function floatScalar(val) {
+ if (typeof val === 'number') {
+ return val;
+ }
+ if (typeof val === 'boolean') {
+ return val ? 1 : 0;
+ }
+ if (typeof val === 'string') {
+ return parseFloat(val);
+ }
+ if (val instanceof Char) {
+ return val.code;
+ }
+ }
+
+ /**
+ * Converts the passed parameter to the function to its float value.
+ * It will return an array of floats if an array is passed in.
+ *
+ * @param {int, char, boolean, string} val the parameter to be conveted to float
+ * @param {int[], char[], boolean[], string[]} val the array to be converted to float[]
+ *
+ * @return {float|float[]} returns a float or an array of floats
+ */
+ p.parseFloat = function(val) {
+ if (val instanceof Array) {
+ var ret = [];
+ for (var i = 0; i < val.length; i++) {
+ ret.push(floatScalar(val[i]));
+ }
+ return ret;
+ }
+ return floatScalar(val);
+ };
+
+ function intScalar(val, radix) {
+ if (typeof val === 'number') {
+ return val & 0xFFFFFFFF;
+ }
+ if (typeof val === 'boolean') {
+ return val ? 1 : 0;
+ }
+ if (typeof val === 'string') {
+ var number = parseInt(val, radix || 10); // Default to decimal radix.
+ return number & 0xFFFFFFFF;
+ }
+ if (val instanceof Char) {
+ return val.code;
+ }
+ }
+
+ /**
+ * Converts the passed parameter to the function to its int value.
+ * It will return an array of ints if an array is passed in.
+ *
+ * @param {string, char, boolean, float} val the parameter to be conveted to int
+ * @param {string[], char[], boolean[], float[]} val the array to be converted to int[]
+ * @param {int} radix optional the radix of the number (for js compatibility)
+ *
+ * @return {int|int[]} returns a int or an array of ints
+ */
+ p.parseInt = function(val, radix) {
+ if (val instanceof Array) {
+ var ret = [];
+ for (var i = 0; i < val.length; i++) {
+ if (typeof val[i] === 'string' && !/^\s*[+\-]?\d+\s*$/.test(val[i])) {
+ ret.push(0);
+ } else {
+ ret.push(intScalar(val[i], radix));
+ }
+ }
+ return ret;
+ }
+ return intScalar(val, radix);
+ };
+
+ p.__int_cast = function(val) {
+ return 0|val;
+ };
+
+ p.__instanceof = function(obj, type) {
+ if (typeof type !== "function") {
+ throw "Function is expected as type argument for instanceof operator";
+ }
+
+ if (typeof obj === "string") {
+ // special case for strings
+ return type === Object || type === String;
+ }
+
+ if (obj instanceof type) {
+ // fast check if obj is already of type instance
+ return true;
+ }
+
+ if (typeof obj !== "object" || obj === null) {
+ return false; // not an object or null
+ }
+
+ var objType = obj.constructor;
+ if (type.$isInterface) {
+ // expecting the interface
+ // queueing interfaces from type and its base classes
+ var interfaces = [];
+ while (objType) {
+ if (objType.$interfaces) {
+ interfaces = interfaces.concat(objType.$interfaces);
+ }
+ objType = objType.$base;
+ }
+ while (interfaces.length > 0) {
+ var i = interfaces.shift();
+ if (i === type) {
+ return true;
+ }
+ // wide search in base interfaces
+ if (i.$interfaces) {
+ interfaces = interfaces.concat(i.$interfaces);
+ }
+ }
+ return false;
+ }
+
+ while (objType.hasOwnProperty("$base")) {
+ objType = objType.$base;
+ if (objType === type) {
+ return true; // object was found
+ }
+ }
+
+ return false;
+ };
+
+ /**
+ * Defines the dimension of the display window in units of pixels. The size() function must
+ * be the first line in setup(). If size() is not called, the default size of the window is
+ * 100x100 pixels. The system variables width and height are set by the parameters passed to
+ * the size() function.
+ *
+ * @param {int} aWidth width of the display window in units of pixels
+ * @param {int} aHeight height of the display window in units of pixels
+ * @param {MODE} aMode Either P2D, P3D, JAVA2D, or OPENGL
+ *
+ * @see createGraphics
+ * @see screen
+ */
+ DrawingShared.prototype.size = function(aWidth, aHeight, aMode) {
+ if (doStroke) {
+ p.stroke(0);
+ }
+
+ if (doFill) {
+ p.fill(255);
+ }
+
+ // The default 2d context has already been created in the p.init() stage if
+ // a 3d context was not specified. This is so that a 2d context will be
+ // available if size() was not called.
+ var savedProperties = {
+ fillStyle: curContext.fillStyle,
+ strokeStyle: curContext.strokeStyle,
+ lineCap: curContext.lineCap,
+ lineJoin: curContext.lineJoin
+ };
+ // remove the style width and height properties to ensure that the canvas gets set to
+ // aWidth and aHeight coming in
+ if (curElement.style.length > 0 ) {
+ curElement.style.removeProperty("width");
+ curElement.style.removeProperty("height");
+ }
+
+ curElement.width = p.width = aWidth || 100;
+ curElement.height = p.height = aHeight || 100;
+
+ for (var prop in savedProperties) {
+ if (savedProperties.hasOwnProperty(prop)) {
+ curContext[prop] = savedProperties[prop];
+ }
+ }
+
+ // make sure to set the default font the first time round.
+ p.textFont(curTextFont);
+
+ // Set the background to whatever it was called last as if background() was called before size()
+ // If background() hasn't been called before, set background() to a light gray
+ p.background();
+
+ // set 5% for pixels to cache (or 1000)
+ maxPixelsCached = Math.max(1000, aWidth * aHeight * 0.05);
+
+ // Externalize the context
+ p.externals.context = curContext;
+
+ for (var i = 0; i < PConstants.SINCOS_LENGTH; i++) {
+ sinLUT[i] = p.sin(i * (PConstants.PI / 180) * 0.5);
+ cosLUT[i] = p.cos(i * (PConstants.PI / 180) * 0.5);
+ }
+ };
+
+ Drawing2D.prototype.size = function(aWidth, aHeight, aMode) {
+ if (curContext === undef) {
+ // size() was called without p.init() default context, i.e. p.createGraphics()
+ curContext = curElement.getContext("2d");
+ userMatrixStack = new PMatrixStack();
+ userReverseMatrixStack = new PMatrixStack();
+ modelView = new PMatrix2D();
+ modelViewInv = new PMatrix2D();
+ }
+
+ DrawingShared.prototype.size.apply(this, arguments);
+ };
+
+ Drawing3D.prototype.size = (function() {
+ var size3DCalled = false;
+
+ return function size(aWidth, aHeight, aMode) {
+ if (size3DCalled) {
+ throw "Multiple calls to size() for 3D renders are not allowed.";
+ }
+ size3DCalled = true;
+
+ function getGLContext(canvas) {
+ var ctxNames = ['experimental-webgl', 'webgl', 'webkit-3d'],
+ gl;
+
+ for (var i=0, l=ctxNames.length; i<l; i++) {
+ gl = canvas.getContext(ctxNames[i], {antialias: false, preserveDrawingBuffer: true});
+ if (gl) {
+ break;
+ }
+ }
+
+ return gl;
+ }
+
+ // Get the 3D rendering context.
+ try {
+ // If the HTML <canvas> dimensions differ from the
+ // dimensions specified in the size() call in the sketch, for
+ // 3D sketches, browsers will either not render or render the
+ // scene incorrectly. To fix this, we need to adjust the
+ // width and height attributes of the canvas.
+ curElement.width = p.width = aWidth || 100;
+ curElement.height = p.height = aHeight || 100;
+ curContext = getGLContext(curElement);
+ canTex = curContext.createTexture();
+ textTex = curContext.createTexture();
+ } catch(e_size) {
+ Processing.debug(e_size);
+ }
+
+ if (!curContext) {
+ throw "WebGL context is not supported on this browser.";
+ }
+
+ // Set defaults
+ curContext.viewport(0, 0, curElement.width, curElement.height);
+ curContext.enable(curContext.DEPTH_TEST);
+ curContext.enable(curContext.BLEND);
+ curContext.blendFunc(curContext.SRC_ALPHA, curContext.ONE_MINUS_SRC_ALPHA);
+
+ // Create the program objects to render 2D (points, lines) and
+ // 3D (spheres, boxes) shapes. Because 2D shapes are not lit,
+ // lighting calculations are ommitted from this program object.
+ programObject2D = createProgramObject(curContext, vertexShaderSrc2D, fragmentShaderSrc2D);
+
+ programObjectUnlitShape = createProgramObject(curContext, vertexShaderSrcUnlitShape, fragmentShaderSrcUnlitShape);
+
+ // Set the default point and line width for the 2D and unlit shapes.
+ p.strokeWeight(1);
+
+ // Now that the programs have been compiled, we can set the default
+ // states for the lights.
+ programObject3D = createProgramObject(curContext, vertexShaderSrc3D, fragmentShaderSrc3D);
+ curContext.useProgram(programObject3D);
+
+ // Assume we aren't using textures by default.
+ uniformi("usingTexture3d", programObject3D, "usingTexture", usingTexture);
+
+ // Set some defaults.
+ p.lightFalloff(1, 0, 0);
+ p.shininess(1);
+ p.ambient(255, 255, 255);
+ p.specular(0, 0, 0);
+ p.emissive(0, 0, 0);
+
+ // Create buffers for 3D primitives
+ boxBuffer = curContext.createBuffer();
+ curContext.bindBuffer(curContext.ARRAY_BUFFER, boxBuffer);
+ curContext.bufferData(curContext.ARRAY_BUFFER, boxVerts, curContext.STATIC_DRAW);
+
+ boxNormBuffer = curContext.createBuffer();
+ curContext.bindBuffer(curContext.ARRAY_BUFFER, boxNormBuffer);
+ curContext.bufferData(curContext.ARRAY_BUFFER, boxNorms, curContext.STATIC_DRAW);
+
+ boxOutlineBuffer = curContext.createBuffer();
+ curContext.bindBuffer(curContext.ARRAY_BUFFER, boxOutlineBuffer);
+ curContext.bufferData(curContext.ARRAY_BUFFER, boxOutlineVerts, curContext.STATIC_DRAW);
+
+ // used to draw the rectangle and the outline
+ rectBuffer = curContext.createBuffer();
+ curContext.bindBuffer(curContext.ARRAY_BUFFER, rectBuffer);
+ curContext.bufferData(curContext.ARRAY_BUFFER, rectVerts, curContext.STATIC_DRAW);
+
+ rectNormBuffer = curContext.createBuffer();
+ curContext.bindBuffer(curContext.ARRAY_BUFFER, rectNormBuffer);
+ curContext.bufferData(curContext.ARRAY_BUFFER, rectNorms, curContext.STATIC_DRAW);
+
+ // The sphere vertices are specified dynamically since the user
+ // can change the level of detail. Everytime the user does that
+ // using sphereDetail(), the new vertices are calculated.
+ sphereBuffer = curContext.createBuffer();
+
+ lineBuffer = curContext.createBuffer();
+
+ // Shape buffers
+ fillBuffer = curContext.createBuffer();
+ fillColorBuffer = curContext.createBuffer();
+ strokeColorBuffer = curContext.createBuffer();
+ shapeTexVBO = curContext.createBuffer();
+
+ pointBuffer = curContext.createBuffer();
+ curContext.bindBuffer(curContext.ARRAY_BUFFER, pointBuffer);
+ curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([0, 0, 0]), curContext.STATIC_DRAW);
+
+ textBuffer = curContext.createBuffer();
+ curContext.bindBuffer(curContext.ARRAY_BUFFER, textBuffer );
+ curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([1,1,0,-1,1,0,-1,-1,0,1,-1,0]), curContext.STATIC_DRAW);
+
+ textureBuffer = curContext.createBuffer();
+ curContext.bindBuffer(curContext.ARRAY_BUFFER, textureBuffer);
+ curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array([0,0,1,0,1,1,0,1]), curContext.STATIC_DRAW);
+
+ indexBuffer = curContext.createBuffer();
+ curContext.bindBuffer(curContext.ELEMENT_ARRAY_BUFFER, indexBuffer);
+ curContext.bufferData(curContext.ELEMENT_ARRAY_BUFFER, new Uint16Array([0,1,2,2,3,0]), curContext.STATIC_DRAW);
+
+ cam = new PMatrix3D();
+ cameraInv = new PMatrix3D();
+ modelView = new PMatrix3D();
+ modelViewInv = new PMatrix3D();
+ projection = new PMatrix3D();
+ p.camera();
+ p.perspective();
+
+ userMatrixStack = new PMatrixStack();
+ userReverseMatrixStack = new PMatrixStack();
+ // used by both curve and bezier, so just init here
+ curveBasisMatrix = new PMatrix3D();
+ curveToBezierMatrix = new PMatrix3D();
+ curveDrawMatrix = new PMatrix3D();
+ bezierDrawMatrix = new PMatrix3D();
+ bezierBasisInverse = new PMatrix3D();
+ bezierBasisMatrix = new PMatrix3D();
+ bezierBasisMatrix.set(-1, 3, -3, 1, 3, -6, 3, 0, -3, 3, 0, 0, 1, 0, 0, 0);
+
+ DrawingShared.prototype.size.apply(this, arguments);
+ };
+ }());
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Lights
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Adds an ambient light. Ambient light doesn't come from a specific direction,
+ * the rays have light have bounced around so much that objects are evenly lit
+ * from all sides. Ambient lights are almost always used in combination with
+ * other types of lights. Lights need to be included in the <b>draw()</b> to
+ * remain persistent in a looping program. Placing them in the <b>setup()</b>
+ * of a looping program will cause them to only have an effect the first time
+ * through the loop. The effect of the parameters is determined by the current
+ * color mode.
+ *
+ * @param {int | float} r red or hue value
+ * @param {int | float} g green or hue value
+ * @param {int | float} b blue or hue value
+ *
+ * @param {int | float} x x position of light (used for falloff)
+ * @param {int | float} y y position of light (used for falloff)
+ * @param {int | float} z z position of light (used for falloff)
+ *
+ * @returns none
+ *
+ * @see lights
+ * @see directionalLight
+ * @see pointLight
+ * @see spotLight
+ */
+ Drawing2D.prototype.ambientLight = DrawingShared.prototype.a3DOnlyFunction;
+
+ Drawing3D.prototype.ambientLight = function(r, g, b, x, y, z) {
+ if (lightCount === PConstants.MAX_LIGHTS) {
+ throw "can only create " + PConstants.MAX_LIGHTS + " lights";
+ }
+
+ var pos = new PVector(x, y, z);
+ var view = new PMatrix3D();
+ view.scale(1, -1, 1);
+ view.apply(modelView.array());
+ view.mult(pos, pos);
+
+ // Instead of calling p.color, we do the calculations ourselves to
+ // reduce property lookups.
+ var col = color$4(r, g, b, 0);
+ var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
+ ((col & PConstants.GREEN_MASK) >>> 8) / 255,
+ (col & PConstants.BLUE_MASK) / 255 ];
+
+ curContext.useProgram(programObject3D);
+ uniformf("uLights.color.3d." + lightCount, programObject3D, "uLights" + lightCount + ".color", normalizedCol);
+ uniformf("uLights.position.3d." + lightCount, programObject3D, "uLights" + lightCount + ".position", pos.array());
+ uniformi("uLights.type.3d." + lightCount, programObject3D, "uLights" + lightCount + ".type", 0);
+ uniformi("uLightCount3d", programObject3D, "uLightCount", ++lightCount);
+ };
+
+ /**
+ * Adds a directional light. Directional light comes from one direction and
+ * is stronger when hitting a surface squarely and weaker if it hits at a
+ * gentle angle. After hitting a surface, a directional lights scatters in
+ * all directions. Lights need to be included in the <b>draw()</b> to remain
+ * persistent in a looping program. Placing them in the <b>setup()</b> of a
+ * looping program will cause them to only have an effect the first time
+ * through the loop. The affect of the <br>r</b>, <br>g</b>, and <br>b</b>
+ * parameters is determined by the current color mode. The <b>nx</b>,
+ * <b>ny</b>, and <b>nz</b> parameters specify the direction the light is
+ * facing. For example, setting <b>ny</b> to -1 will cause the geometry to be
+ * lit from below (the light is facing directly upward).
+ *
+ * @param {int | float} r red or hue value
+ * @param {int | float} g green or hue value
+ * @param {int | float} b blue or hue value
+ *
+ * @param {int | float} nx direction along the x axis
+ * @param {int | float} ny direction along the y axis
+ * @param {int | float} nz direction along the z axis
+ *
+ * @returns none
+ *
+ * @see lights
+ * @see ambientLight
+ * @see pointLight
+ * @see spotLight
+ */
+ Drawing2D.prototype.directionalLight = DrawingShared.prototype.a3DOnlyFunction;
+
+ Drawing3D.prototype.directionalLight = function(r, g, b, nx, ny, nz) {
+ if (lightCount === PConstants.MAX_LIGHTS) {
+ throw "can only create " + PConstants.MAX_LIGHTS + " lights";
+ }
+
+ curContext.useProgram(programObject3D);
+
+ var mvm = new PMatrix3D();
+ mvm.scale(1, -1, 1);
+ mvm.apply(modelView.array());
+ mvm = mvm.array();
+
+ // We need to multiply the direction by the model view matrix, but
+ // the mult function checks the w component of the vector, if it isn't
+ // present, it uses 1, so we manually multiply.
+ var dir = [
+ mvm[0] * nx + mvm[4] * ny + mvm[8] * nz,
+ mvm[1] * nx + mvm[5] * ny + mvm[9] * nz,
+ mvm[2] * nx + mvm[6] * ny + mvm[10] * nz
+ ];
+
+ // Instead of calling p.color, we do the calculations ourselves to
+ // reduce property lookups.
+ var col = color$4(r, g, b, 0);
+ var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
+ ((col & PConstants.GREEN_MASK) >>> 8) / 255,
+ (col & PConstants.BLUE_MASK) / 255 ];
+
+ uniformf("uLights.color.3d." + lightCount, programObject3D, "uLights" + lightCount + ".color", normalizedCol);
+ uniformf("uLights.position.3d." + lightCount, programObject3D, "uLights" + lightCount + ".position", dir);
+ uniformi("uLights.type.3d." + lightCount, programObject3D, "uLights" + lightCount + ".type", 1);
+ uniformi("uLightCount3d", programObject3D, "uLightCount", ++lightCount);
+ };
+
+ /**
+ * Sets the falloff rates for point lights, spot lights, and ambient lights.
+ * The parameters are used to determine the falloff with the following equation:
+ *
+ * d = distance from light position to vertex position
+ * falloff = 1 / (CONSTANT + d * LINEAR + (d*d) * QUADRATIC)
+ *
+ * Like <b>fill()</b>, it affects only the elements which are created after it in the
+ * code. The default value if <b>LightFalloff(1.0, 0.0, 0.0)</b>. Thinking about an
+ * ambient light with a falloff can be tricky. It is used, for example, if you
+ * wanted a region of your scene to be lit ambiently one color and another region
+ * to be lit ambiently by another color, you would use an ambient light with location
+ * and falloff. You can think of it as a point light that doesn't care which direction
+ * a surface is facing.
+ *
+ * @param {int | float} constant constant value for determining falloff
+ * @param {int | float} linear linear value for determining falloff
+ * @param {int | float} quadratic quadratic value for determining falloff
+ *
+ * @returns none
+ *
+ * @see lights
+ * @see ambientLight
+ * @see pointLight
+ * @see spotLight
+ * @see lightSpecular
+ */
+ Drawing2D.prototype.lightFalloff = DrawingShared.prototype.a3DOnlyFunction;
+
+ Drawing3D.prototype.lightFalloff = function(constant, linear, quadratic) {
+ curContext.useProgram(programObject3D);
+ uniformf("uFalloff3d", programObject3D, "uFalloff", [constant, linear, quadratic]);
+ };
+
+ /**
+ * Sets the specular color for lights. Like <b>fill()</b>, it affects only the
+ * elements which are created after it in the code. Specular refers to light
+ * which bounces off a surface in a perferred direction (rather than bouncing
+ * in all directions like a diffuse light) and is used for creating highlights.
+ * The specular quality of a light interacts with the specular material qualities
+ * set through the <b>specular()</b> and <b>shininess()</b> functions.
+ *
+ * @param {int | float} r red or hue value
+ * @param {int | float} g green or hue value
+ * @param {int | float} b blue or hue value
+ *
+ * @returns none
+ *
+ * @see lights
+ * @see ambientLight
+ * @see pointLight
+ * @see spotLight
+ */
+ Drawing2D.prototype.lightSpecular = DrawingShared.prototype.a3DOnlyFunction;
+
+ Drawing3D.prototype.lightSpecular = function(r, g, b) {
+
+ // Instead of calling p.color, we do the calculations ourselves to
+ // reduce property lookups.
+ var col = color$4(r, g, b, 0);
+ var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
+ ((col & PConstants.GREEN_MASK) >>> 8) / 255,
+ (col & PConstants.BLUE_MASK) / 255 ];
+
+ curContext.useProgram(programObject3D);
+ uniformf("uSpecular3d", programObject3D, "uSpecular", normalizedCol);
+ };
+
+ /**
+ * Sets the default ambient light, directional light, falloff, and specular
+ * values. The defaults are ambientLight(128, 128, 128) and
+ * directionalLight(128, 128, 128, 0, 0, -1), lightFalloff(1, 0, 0), and
+ * lightSpecular(0, 0, 0). Lights need to be included in the draw() to remain
+ * persistent in a looping program. Placing them in the setup() of a looping
+ * program will cause them to only have an effect the first time through the
+ * loop.
+ *
+ * @returns none
+ *
+ * @see ambientLight
+ * @see directionalLight
+ * @see pointLight
+ * @see spotLight
+ * @see noLights
+ *
+ */
+ p.lights = function() {
+ p.ambientLight(128, 128, 128);
+ p.directionalLight(128, 128, 128, 0, 0, -1);
+ p.lightFalloff(1, 0, 0);
+ p.lightSpecular(0, 0, 0);
+ };
+
+ /**
+ * Adds a point light. Lights need to be included in the <b>draw()</b> to remain
+ * persistent in a looping program. Placing them in the <b>setup()</b> of a
+ * looping program will cause them to only have an effect the first time through
+ * the loop. The affect of the <b>r</b>, <b>g</b>, and <b>b</b> parameters
+ * is determined by the current color mode. The <b>x</b>, <b>y</b>, and <b>z</b>
+ * parameters set the position of the light.
+ *
+ * @param {int | float} r red or hue value
+ * @param {int | float} g green or hue value
+ * @param {int | float} b blue or hue value
+ * @param {int | float} x x coordinate of the light
+ * @param {int | float} y y coordinate of the light
+ * @param {int | float} z z coordinate of the light
+ *
+ * @returns none
+ *
+ * @see lights
+ * @see directionalLight
+ * @see ambientLight
+ * @see spotLight
+ */
+ Drawing2D.prototype.pointLight = DrawingShared.prototype.a3DOnlyFunction;
+
+ Drawing3D.prototype.pointLight = function(r, g, b, x, y, z) {
+ if (lightCount === PConstants.MAX_LIGHTS) {
+ throw "can only create " + PConstants.MAX_LIGHTS + " lights";
+ }
+
+ // Place the point in view space once instead of once per vertex
+ // in the shader.
+ var pos = new PVector(x, y, z);
+ var view = new PMatrix3D();
+ view.scale(1, -1, 1);
+ view.apply(modelView.array());
+ view.mult(pos, pos);
+
+ // Instead of calling p.color, we do the calculations ourselves to
+ // reduce property lookups.
+ var col = color$4(r, g, b, 0);
+ var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
+ ((col & PConstants.GREEN_MASK) >>> 8) / 255,
+ (col & PConstants.BLUE_MASK) / 255 ];
+
+ curContext.useProgram(programObject3D);
+ uniformf("uLights.color.3d." + lightCount, programObject3D, "uLights" + lightCount + ".color", normalizedCol);
+ uniformf("uLights.position.3d." + lightCount, programObject3D, "uLights" + lightCount + ".position", pos.array());
+ uniformi("uLights.type.3d." + lightCount, programObject3D, "uLights" + lightCount + ".type", 2);
+ uniformi("uLightCount3d", programObject3D, "uLightCount", ++lightCount);
+ };
+
+ /**
+ * Disable all lighting. Lighting is turned off by default and enabled with
+ * the lights() method. This function can be used to disable lighting so
+ * that 2D geometry (which does not require lighting) can be drawn after a
+ * set of lighted 3D geometry.
+ *
+ * @returns none
+ *
+ * @see lights
+ */
+ Drawing2D.prototype.noLights = DrawingShared.prototype.a3DOnlyFunction;
+
+ Drawing3D.prototype.noLights = function() {
+ lightCount = 0;
+ curContext.useProgram(programObject3D);
+ uniformi("uLightCount3d", programObject3D, "uLightCount", lightCount);
+ };
+
+ /**
+ * Adds a spot light. Lights need to be included in the <b>draw()</b> to
+ * remain persistent in a looping program. Placing them in the <b>setup()</b>
+ * of a looping program will cause them to only have an effect the first time
+ * through the loop. The affect of the <b>r</b>, <b>g</b>, and <b>b</b> parameters
+ * is determined by the current color mode. The <b>x</b>, <b>y</b>, and <b>z</b>
+ * parameters specify the position of the light and <b>nx</b>, <b>ny</b>, <b>nz</b>
+ * specify the direction or light. The angle parameter affects <b>angle</b> of the
+ * spotlight cone.
+ *
+ * @param {int | float} r red or hue value
+ * @param {int | float} g green or hue value
+ * @param {int | float} b blue or hue value
+ * @param {int | float} x coordinate of the light
+ * @param {int | float} y coordinate of the light
+ * @param {int | float} z coordinate of the light
+ * @param {int | float} nx direction along the x axis
+ * @param {int | float} ny direction along the y axis
+ * @param {int | float} nz direction along the z axis
+ * @param {float} angle angle of the spotlight cone
+ * @param {float} concentration exponent determining the center bias of the cone
+ *
+ * @returns none
+ *
+ * @see lights
+ * @see directionalLight
+ * @see ambientLight
+ * @see pointLight
+ */
+ Drawing2D.prototype.spotLight = DrawingShared.prototype.a3DOnlyFunction;
+
+ Drawing3D.prototype.spotLight = function(r, g, b, x, y, z, nx, ny, nz, angle, concentration) {
+ if (lightCount === PConstants.MAX_LIGHTS) {
+ throw "can only create " + PConstants.MAX_LIGHTS + " lights";
+ }
+
+ curContext.useProgram(programObject3D);
+
+ // multiply the position and direction by the model view matrix
+ // once per object rather than once per vertex.
+ var pos = new PVector(x, y, z);
+ var mvm = new PMatrix3D();
+ mvm.scale(1, -1, 1);
+ mvm.apply(modelView.array());
+ mvm.mult(pos, pos);
+
+ // Convert to array since we need to directly access the elements.
+ mvm = mvm.array();
+
+ // We need to multiply the direction by the model view matrix, but
+ // the mult function checks the w component of the vector, if it isn't
+ // present, it uses 1, so we use a very small value as a work around.
+ var dir = [
+ mvm[0] * nx + mvm[4] * ny + mvm[8] * nz,
+ mvm[1] * nx + mvm[5] * ny + mvm[9] * nz,
+ mvm[2] * nx + mvm[6] * ny + mvm[10] * nz
+ ];
+
+ // Instead of calling p.color, we do the calculations ourselves to
+ // reduce property lookups.
+ var col = color$4(r, g, b, 0);
+ var normalizedCol = [ ((col & PConstants.RED_MASK) >>> 16) / 255,
+ ((col & PConstants.GREEN_MASK) >>> 8) / 255,
+ (col & PConstants.BLUE_MASK) / 255 ];
+
+ uniformf("uLights.color.3d." + lightCount, programObject3D, "uLights" + lightCount + ".color", normalizedCol);
+ uniformf("uLights.position.3d." + lightCount, programObject3D, "uLights" + lightCount + ".position", pos.array());
+ uniformf("uLights.direction.3d." + lightCount, programObject3D, "uLights" + lightCount + ".direction", dir);
+ uniformf("uLights.concentration.3d." + lightCount, programObject3D, "uLights" + lightCount + ".concentration", concentration);
+ uniformf("uLights.angle.3d." + lightCount, programObject3D, "uLights" + lightCount + ".angle", angle);
+ uniformi("uLights.type.3d." + lightCount, programObject3D, "uLights" + lightCount + ".type", 3);
+ uniformi("uLightCount3d", programObject3D, "uLightCount", ++lightCount);
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Camera functions
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * The <b>beginCamera()</b> and <b>endCamera()</b> functions enable advanced customization of the camera space.
+ * The functions are useful if you want to more control over camera movement, however for most users, the <b>camera()</b>
+ * function will be sufficient.<br /><br />The camera functions will replace any transformations (such as <b>rotate()</b>
+ * or <b>translate()</b>) that occur before them in <b>draw()</b>, but they will not automatically replace the camera
+ * transform itself. For this reason, camera functions should be placed at the beginning of <b>draw()</b> (so that
+ * transformations happen afterwards), and the <b>camera()</b> function can be used after <b>beginCamera()</b> if
+ * you want to reset the camera before applying transformations.<br /><br />This function sets the matrix mode to the
+ * camera matrix so calls such as <b>translate()</b>, <b>rotate()</b>, applyMatrix() and resetMatrix() affect the camera.
+ * <b>beginCamera()</b> should always be used with a following <b>endCamera()</b> and pairs of <b>beginCamera()</b> and
+ * <b>endCamera()</b> cannot be nested.
+ *
+ * @see camera
+ * @see endCamera
+ * @see applyMatrix
+ * @see resetMatrix
+ * @see translate
+ * @see rotate
+ * @see scale
+ */
+ Drawing2D.prototype.beginCamera = function() {
+ throw ("beginCamera() is not available in 2D mode");
+ };
+
+ Drawing3D.prototype.beginCamera = function() {
+ if (manipulatingCamera) {
+ throw ("You cannot call beginCamera() again before calling endCamera()");
+ }
+ manipulatingCamera = true;
+ modelView = cameraInv;
+ modelViewInv = cam;
+ };
+
+ /**
+ * The <b>beginCamera()</b> and <b>endCamera()</b> functions enable advanced customization of the camera space.
+ * Please see the reference for <b>beginCamera()</b> for a description of how the functions are used.
+ *
+ * @see beginCamera
+ */
+ Drawing2D.prototype.endCamera = function() {
+ throw ("endCamera() is not available in 2D mode");
+ };
+
+ Drawing3D.prototype.endCamera = function() {
+ if (!manipulatingCamera) {
+ throw ("You cannot call endCamera() before calling beginCamera()");
+ }
+ modelView.set(cam);
+ modelViewInv.set(cameraInv);
+ manipulatingCamera = false;
+ };
+
+ /**
+ * Sets the position of the camera through setting the eye position, the center of the scene, and which axis is facing
+ * upward. Moving the eye position and the direction it is pointing (the center of the scene) allows the images to be
+ * seen from different angles. The version without any parameters sets the camera to the default position, pointing to
+ * the center of the display window with the Y axis as up. The default values are camera(width/2.0, height/2.0,
+ * (height/2.0) / tan(PI*60.0 / 360.0), width/2.0, height/2.0, 0, 0, 1, 0). This function is similar to gluLookAt()
+ * in OpenGL, but it first clears the current camera settings.
+ *
+ * @param {float} eyeX x-coordinate for the eye
+ * @param {float} eyeY y-coordinate for the eye
+ * @param {float} eyeZ z-coordinate for the eye
+ * @param {float} centerX x-coordinate for the center of the scene
+ * @param {float} centerY y-coordinate for the center of the scene
+ * @param {float} centerZ z-coordinate for the center of the scene
+ * @param {float} upX usually 0.0, 1.0, -1.0
+ * @param {float} upY usually 0.0, 1.0, -1.0
+ * @param {float} upZ usually 0.0, 1.0, -1.0
+ *
+ * @see beginCamera
+ * @see endCamera
+ * @see frustum
+ */
+ p.camera = function(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) {
+ if (eyeX === undef) {
+ // Workaround if createGraphics is used.
+ cameraX = p.width / 2;
+ cameraY = p.height / 2;
+ cameraZ = cameraY / Math.tan(cameraFOV / 2);
+ eyeX = cameraX;
+ eyeY = cameraY;
+ eyeZ = cameraZ;
+ centerX = cameraX;
+ centerY = cameraY;
+ centerZ = 0;
+ upX = 0;
+ upY = 1;
+ upZ = 0;
+ }
+
+ var z = new PVector(eyeX - centerX, eyeY - centerY, eyeZ - centerZ);
+ var y = new PVector(upX, upY, upZ);
+ z.normalize();
+ var x = PVector.cross(y, z);
+ y = PVector.cross(z, x);
+ x.normalize();
+ y.normalize();
+
+ var xX = x.x,
+ xY = x.y,
+ xZ = x.z;
+
+ var yX = y.x,
+ yY = y.y,
+ yZ = y.z;
+
+ var zX = z.x,
+ zY = z.y,
+ zZ = z.z;
+
+ cam.set(xX, xY, xZ, 0, yX, yY, yZ, 0, zX, zY, zZ, 0, 0, 0, 0, 1);
+
+ cam.translate(-eyeX, -eyeY, -eyeZ);
+
+ cameraInv.reset();
+ cameraInv.invApply(xX, xY, xZ, 0, yX, yY, yZ, 0, zX, zY, zZ, 0, 0, 0, 0, 1);
+
+ cameraInv.translate(eyeX, eyeY, eyeZ);
+
+ modelView.set(cam);
+ modelViewInv.set(cameraInv);
+ };
+
+ /**
+ * Sets a perspective projection applying foreshortening, making distant objects appear smaller than closer ones. The
+ * parameters define a viewing volume with the shape of truncated pyramid. Objects near to the front of the volume appear
+ * their actual size, while farther objects appear smaller. This projection simulates the perspective of the world more
+ * accurately than orthographic projection. The version of perspective without parameters sets the default perspective and
+ * the version with four parameters allows the programmer to set the area precisely. The default values are:
+ * perspective(PI/3.0, width/height, cameraZ/10.0, cameraZ*10.0) where cameraZ is ((height/2.0) / tan(PI*60.0/360.0));
+ *
+ * @param {float} fov field-of-view angle (in radians) for vertical direction
+ * @param {float} aspect ratio of width to height
+ * @param {float} zNear z-position of nearest clipping plane
+ * @param {float} zFar z-positions of farthest clipping plane
+ */
+ p.perspective = function(fov, aspect, near, far) {
+ if (arguments.length === 0) {
+ //in case canvas is resized
+ cameraY = curElement.height / 2;
+ cameraZ = cameraY / Math.tan(cameraFOV / 2);
+ cameraNear = cameraZ / 10;
+ cameraFar = cameraZ * 10;
+ cameraAspect = p.width / p.height;
+ fov = cameraFOV;
+ aspect = cameraAspect;
+ near = cameraNear;
+ far = cameraFar;
+ }
+
+ var yMax, yMin, xMax, xMin;
+ yMax = near * Math.tan(fov / 2);
+ yMin = -yMax;
+ xMax = yMax * aspect;
+ xMin = yMin * aspect;
+ p.frustum(xMin, xMax, yMin, yMax, near, far);
+ };
+
+ /**
+ * Sets a perspective matrix defined through the parameters. Works like glFrustum, except it wipes out the current
+ * perspective matrix rather than muliplying itself with it.
+ *
+ * @param {float} left left coordinate of the clipping plane
+ * @param {float} right right coordinate of the clipping plane
+ * @param {float} bottom bottom coordinate of the clipping plane
+ * @param {float} top top coordinate of the clipping plane
+ * @param {float} near near coordinate of the clipping plane
+ * @param {float} far far coordinate of the clipping plane
+ *
+ * @see beginCamera
+ * @see camera
+ * @see endCamera
+ * @see perspective
+ */
+ Drawing2D.prototype.frustum = function() {
+ throw("Processing.js: frustum() is not supported in 2D mode");
+ };
+
+ Drawing3D.prototype.frustum = function(left, right, bottom, top, near, far) {
+ frustumMode = true;
+ projection = new PMatrix3D();
+ projection.set((2 * near) / (right - left), 0, (right + left) / (right - left),
+ 0, 0, (2 * near) / (top - bottom), (top + bottom) / (top - bottom),
+ 0, 0, 0, -(far + near) / (far - near), -(2 * far * near) / (far - near),
+ 0, 0, -1, 0);
+ var proj = new PMatrix3D();
+ proj.set(projection);
+ proj.transpose();
+ curContext.useProgram(programObject2D);
+ uniformMatrix("projection2d", programObject2D, "uProjection", false, proj.array());
+ curContext.useProgram(programObject3D);
+ uniformMatrix("projection3d", programObject3D, "uProjection", false, proj.array());
+ curContext.useProgram(programObjectUnlitShape);
+ uniformMatrix("uProjectionUS", programObjectUnlitShape, "uProjection", false, proj.array());
+ };
+
+ /**
+ * Sets an orthographic projection and defines a parallel clipping volume. All objects with the same dimension appear
+ * the same size, regardless of whether they are near or far from the camera. The parameters to this function specify
+ * the clipping volume where left and right are the minimum and maximum x values, top and bottom are the minimum and
+ * maximum y values, and near and far are the minimum and maximum z values. If no parameters are given, the default
+ * is used: ortho(0, width, 0, height, -10, 10).
+ *
+ * @param {float} left left plane of the clipping volume
+ * @param {float} right right plane of the clipping volume
+ * @param {float} bottom bottom plane of the clipping volume
+ * @param {float} top top plane of the clipping volume
+ * @param {float} near maximum distance from the origin to the viewer
+ * @param {float} far maximum distance from the origin away from the viewer
+ */
+ p.ortho = function(left, right, bottom, top, near, far) {
+ if (arguments.length === 0) {
+ left = 0;
+ right = p.width;
+ bottom = 0;
+ top = p.height;
+ near = -10;
+ far = 10;
+ }
+
+ var x = 2 / (right - left);
+ var y = 2 / (top - bottom);
+ var z = -2 / (far - near);
+
+ var tx = -(right + left) / (right - left);
+ var ty = -(top + bottom) / (top - bottom);
+ var tz = -(far + near) / (far - near);
+
+ projection = new PMatrix3D();
+ projection.set(x, 0, 0, tx, 0, y, 0, ty, 0, 0, z, tz, 0, 0, 0, 1);
+
+ var proj = new PMatrix3D();
+ proj.set(projection);
+ proj.transpose();
+ curContext.useProgram(programObject2D);
+ uniformMatrix("projection2d", programObject2D, "uProjection", false, proj.array());
+ curContext.useProgram(programObject3D);
+ uniformMatrix("projection3d", programObject3D, "uProjection", false, proj.array());
+ curContext.useProgram(programObjectUnlitShape);
+ uniformMatrix("uProjectionUS", programObjectUnlitShape, "uProjection", false, proj.array());
+ frustumMode = false;
+ };
+ /**
+ * The printProjection() prints the current projection matrix to the text window.
+ */
+ p.printProjection = function() {
+ projection.print();
+ };
+ /**
+ * The printCamera() function prints the current camera matrix.
+ */
+ p.printCamera = function() {
+ cam.print();
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Shapes
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * The box() function renders a box. A box is an extruded rectangle. A box with equal dimension on all sides is a cube.
+ * Calling this function with only one parameter will create a cube.
+ *
+ * @param {int|float} w dimension of the box in the x-dimension
+ * @param {int|float} h dimension of the box in the y-dimension
+ * @param {int|float} d dimension of the box in the z-dimension
+ */
+ Drawing2D.prototype.box = DrawingShared.prototype.a3DOnlyFunction;
+
+ Drawing3D.prototype.box = function(w, h, d) {
+ // user can uniformly scale the box by
+ // passing in only one argument.
+ if (!h || !d) {
+ h = d = w;
+ }
+
+ // Modeling transformation
+ var model = new PMatrix3D();
+ model.scale(w, h, d);
+
+ // Viewing transformation needs to have Y flipped
+ // becuase that's what Processing does.
+ var view = new PMatrix3D();
+ view.scale(1, -1, 1);
+ view.apply(modelView.array());
+ view.transpose();
+
+ if (doFill) {
+ curContext.useProgram(programObject3D);
+ uniformMatrix("model3d", programObject3D, "uModel", false, model.array());
+ uniformMatrix("view3d", programObject3D, "uView", false, view.array());
+ // Fix stitching problems. (lines get occluded by triangles
+ // since they share the same depth values). This is not entirely
+ // working, but it's a start for drawing the outline. So
+ // developers can start playing around with styles.
+ curContext.enable(curContext.POLYGON_OFFSET_FILL);
+ curContext.polygonOffset(1, 1);
+ uniformf("color3d", programObject3D, "uColor", fillStyle);
+
+ // Calculating the normal matrix can be expensive, so only
+ // do it if it's necessary.
+ if(lightCount > 0){
+ // Create the normal transformation matrix.
+ var v = new PMatrix3D();
+ v.set(view);
+
+ var m = new PMatrix3D();
+ m.set(model);
+
+ v.mult(m);
+
+ var normalMatrix = new PMatrix3D();
+ normalMatrix.set(v);
+ normalMatrix.invert();
+ normalMatrix.transpose();
+
+ uniformMatrix("uNormalTransform3d", programObject3D, "uNormalTransform", false, normalMatrix.array());
+ vertexAttribPointer("aNormal3d", programObject3D, "aNormal", 3, boxNormBuffer);
+ }
+ else{
+ disableVertexAttribPointer("aNormal3d", programObject3D, "aNormal");
+ }
+
+ vertexAttribPointer("aVertex3d", programObject3D, "aVertex", 3, boxBuffer);
+
+ // Turn off per vertex colors.
+ disableVertexAttribPointer("aColor3d", programObject3D, "aColor");
+ disableVertexAttribPointer("aTexture3d", programObject3D, "aTexture");
+
+ curContext.drawArrays(curContext.TRIANGLES, 0, boxVerts.length / 3);
+ curContext.disable(curContext.POLYGON_OFFSET_FILL);
+ }
+
+ // Draw the box outline.
+ if (lineWidth > 0 && doStroke) {
+ curContext.useProgram(programObject2D);
+ uniformMatrix("uModel2d", programObject2D, "uModel", false, model.array());
+ uniformMatrix("uView2d", programObject2D, "uView", false, view.array());
+ uniformf("uColor2d", programObject2D, "uColor", strokeStyle);
+ uniformi("uIsDrawingText2d", programObject2D, "uIsDrawingText", false);
+ vertexAttribPointer("vertex2d", programObject2D, "aVertex", 3, boxOutlineBuffer);
+ disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
+ curContext.drawArrays(curContext.LINES, 0, boxOutlineVerts.length / 3);
+ }
+ };
+
+ /**
+ * The initSphere() function is a helper function used by <b>sphereDetail()</b>
+ * This function creates and stores sphere vertices every time the user changes sphere detail.
+ *
+ * @see #sphereDetail
+ */
+ var initSphere = function() {
+ var i;
+ sphereVerts = [];
+
+ for (i = 0; i < sphereDetailU; i++) {
+ sphereVerts.push(0);
+ sphereVerts.push(-1);
+ sphereVerts.push(0);
+ sphereVerts.push(sphereX[i]);
+ sphereVerts.push(sphereY[i]);
+ sphereVerts.push(sphereZ[i]);
+ }
+ sphereVerts.push(0);
+ sphereVerts.push(-1);
+ sphereVerts.push(0);
+ sphereVerts.push(sphereX[0]);
+ sphereVerts.push(sphereY[0]);
+ sphereVerts.push(sphereZ[0]);
+
+ var v1, v11, v2;
+
+ // middle rings
+ var voff = 0;
+ for (i = 2; i < sphereDetailV; i++) {
+ v1 = v11 = voff;
+ voff += sphereDetailU;
+ v2 = voff;
+ for (var j = 0; j < sphereDetailU; j++) {
+ sphereVerts.push(sphereX[v1]);
+ sphereVerts.push(sphereY[v1]);
+ sphereVerts.push(sphereZ[v1++]);
+ sphereVerts.push(sphereX[v2]);
+ sphereVerts.push(sphereY[v2]);
+ sphereVerts.push(sphereZ[v2++]);
+ }
+
+ // close each ring
+ v1 = v11;
+ v2 = voff;
+
+ sphereVerts.push(sphereX[v1]);
+ sphereVerts.push(sphereY[v1]);
+ sphereVerts.push(sphereZ[v1]);
+ sphereVerts.push(sphereX[v2]);
+ sphereVerts.push(sphereY[v2]);
+ sphereVerts.push(sphereZ[v2]);
+ }
+
+ // add the northern cap
+ for (i = 0; i < sphereDetailU; i++) {
+ v2 = voff + i;
+
+ sphereVerts.push(sphereX[v2]);
+ sphereVerts.push(sphereY[v2]);
+ sphereVerts.push(sphereZ[v2]);
+ sphereVerts.push(0);
+ sphereVerts.push(1);
+ sphereVerts.push(0);
+ }
+
+ sphereVerts.push(sphereX[voff]);
+ sphereVerts.push(sphereY[voff]);
+ sphereVerts.push(sphereZ[voff]);
+ sphereVerts.push(0);
+ sphereVerts.push(1);
+ sphereVerts.push(0);
+
+ //set the buffer data
+ curContext.bindBuffer(curContext.ARRAY_BUFFER, sphereBuffer);
+ curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(sphereVerts), curContext.STATIC_DRAW);
+ };
+
+ /**
+ * The sphereDetail() function controls the detail used to render a sphere by adjusting the number of
+ * vertices of the sphere mesh. The default resolution is 30, which creates
+ * a fairly detailed sphere definition with vertices every 360/30 = 12
+ * degrees. If you're going to render a great number of spheres per frame,
+ * it is advised to reduce the level of detail using this function.
+ * The setting stays active until <b>sphereDetail()</b> is called again with
+ * a new parameter and so should <i>not</i> be called prior to every
+ * <b>sphere()</b> statement, unless you wish to render spheres with
+ * different settings, e.g. using less detail for smaller spheres or ones
+ * further away from the camera. To control the detail of the horizontal
+ * and vertical resolution independently, use the version of the functions
+ * with two parameters. Calling this function with one parameter sets the number of segments
+ *(minimum of 3) used per full circle revolution. This is equivalent to calling the function with
+ * two identical values.
+ *
+ * @param {int} ures number of segments used horizontally (longitudinally) per full circle revolution
+ * @param {int} vres number of segments used vertically (latitudinally) from top to bottom
+ *
+ * @see #sphere()
+ */
+ p.sphereDetail = function(ures, vres) {
+ var i;
+
+ if (arguments.length === 1) {
+ ures = vres = arguments[0];
+ }
+
+ if (ures < 3) {
+ ures = 3;
+ } // force a minimum res
+ if (vres < 2) {
+ vres = 2;
+ } // force a minimum res
+ // if it hasn't changed do nothing
+ if ((ures === sphereDetailU) && (vres === sphereDetailV)) {
+ return;
+ }
+
+ var delta = PConstants.SINCOS_LENGTH / ures;
+ var cx = new Float32Array(ures);
+ var cz = new Float32Array(ures);
+ // calc unit circle in XZ plane
+ for (i = 0; i < ures; i++) {
+ cx[i] = cosLUT[((i * delta) % PConstants.SINCOS_LENGTH) | 0];
+ cz[i] = sinLUT[((i * delta) % PConstants.SINCOS_LENGTH) | 0];
+ }
+
+ // computing vertexlist
+ // vertexlist starts at south pole
+ var vertCount = ures * (vres - 1) + 2;
+ var currVert = 0;
+
+ // re-init arrays to store vertices
+ sphereX = new Float32Array(vertCount);
+ sphereY = new Float32Array(vertCount);
+ sphereZ = new Float32Array(vertCount);
+
+ var angle_step = (PConstants.SINCOS_LENGTH * 0.5) / vres;
+ var angle = angle_step;
+
+ // step along Y axis
+ for (i = 1; i < vres; i++) {
+ var curradius = sinLUT[(angle % PConstants.SINCOS_LENGTH) | 0];
+ var currY = -cosLUT[(angle % PConstants.SINCOS_LENGTH) | 0];
+ for (var j = 0; j < ures; j++) {
+ sphereX[currVert] = cx[j] * curradius;
+ sphereY[currVert] = currY;
+ sphereZ[currVert++] = cz[j] * curradius;
+ }
+ angle += angle_step;
+ }
+ sphereDetailU = ures;
+ sphereDetailV = vres;
+
+ // make the sphere verts and norms
+ initSphere();
+ };
+
+ /**
+ * The sphere() function draws a sphere with radius r centered at coordinate 0, 0, 0.
+ * A sphere is a hollow ball made from tessellated triangles.
+ *
+ * @param {int|float} r the radius of the sphere
+ */
+ Drawing2D.prototype.sphere = DrawingShared.prototype.a3DOnlyFunction;
+
+ Drawing3D.prototype.sphere = function() {
+ var sRad = arguments[0];
+
+ if ((sphereDetailU < 3) || (sphereDetailV < 2)) {
+ p.sphereDetail(30);
+ }
+
+ // Modeling transformation.
+ var model = new PMatrix3D();
+ model.scale(sRad, sRad, sRad);
+
+ // viewing transformation needs to have Y flipped
+ // becuase that's what Processing does.
+ var view = new PMatrix3D();
+ view.scale(1, -1, 1);
+ view.apply(modelView.array());
+ view.transpose();
+
+ if (doFill) {
+ // Calculating the normal matrix can be expensive, so only
+ // do it if it's necessary.
+ if(lightCount > 0){
+ // Create a normal transformation matrix.
+ var v = new PMatrix3D();
+ v.set(view);
+
+ var m = new PMatrix3D();
+ m.set(model);
+
+ v.mult(m);
+
+ var normalMatrix = new PMatrix3D();
+ normalMatrix.set(v);
+ normalMatrix.invert();
+ normalMatrix.transpose();
+
+ uniformMatrix("uNormalTransform3d", programObject3D, "uNormalTransform", false, normalMatrix.array());
+ vertexAttribPointer("aNormal3d", programObject3D, "aNormal", 3, sphereBuffer);
+ }
+ else{
+ disableVertexAttribPointer("aNormal3d", programObject3D, "aNormal");
+ }
+
+ curContext.useProgram(programObject3D);
+ disableVertexAttribPointer("aTexture3d", programObject3D, "aTexture");
+
+ uniformMatrix("uModel3d", programObject3D, "uModel", false, model.array());
+ uniformMatrix("uView3d", programObject3D, "uView", false, view.array());
+ vertexAttribPointer("aVertex3d", programObject3D, "aVertex", 3, sphereBuffer);
+
+ // Turn off per vertex colors.
+ disableVertexAttribPointer("aColor3d", programObject3D, "aColor");
+
+ // fix stitching problems. (lines get occluded by triangles
+ // since they share the same depth values). This is not entirely
+ // working, but it's a start for drawing the outline. So
+ // developers can start playing around with styles.
+ curContext.enable(curContext.POLYGON_OFFSET_FILL);
+ curContext.polygonOffset(1, 1);
+ uniformf("uColor3d", programObject3D, "uColor", fillStyle);
+ curContext.drawArrays(curContext.TRIANGLE_STRIP, 0, sphereVerts.length / 3);
+ curContext.disable(curContext.POLYGON_OFFSET_FILL);
+ }
+
+ // Draw the sphere outline.
+ if (lineWidth > 0 && doStroke) {
+ curContext.useProgram(programObject2D);
+ uniformMatrix("uModel2d", programObject2D, "uModel", false, model.array());
+ uniformMatrix("uView2d", programObject2D, "uView", false, view.array());
+ vertexAttribPointer("aVertex2d", programObject2D, "aVertex", 3, sphereBuffer);
+ disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
+ uniformf("uColor2d", programObject2D, "uColor", strokeStyle);
+ uniformi("uIsDrawingText", programObject2D, "uIsDrawingText", false);
+ curContext.drawArrays(curContext.LINE_STRIP, 0, sphereVerts.length / 3);
+ }
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Coordinates
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns the three-dimensional X, Y, Z position in model space. This returns
+ * the X value for a given coordinate based on the current set of transformations
+ * (scale, rotate, translate, etc.) The X value can be used to place an object
+ * in space relative to the location of the original point once the transformations
+ * are no longer in use.<br />
+ * <br />
+ *
+ * @param {int | float} x 3D x coordinate to be mapped
+ * @param {int | float} y 3D y coordinate to be mapped
+ * @param {int | float} z 3D z coordinate to be mapped
+ *
+ * @returns {float}
+ *
+ * @see modelY
+ * @see modelZ
+ */
+ p.modelX = function(x, y, z) {
+ var mv = modelView.array();
+ var ci = cameraInv.array();
+
+ var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
+ var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
+ var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
+ var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];
+
+ var ox = ci[0] * ax + ci[1] * ay + ci[2] * az + ci[3] * aw;
+ var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;
+
+ return (ow !== 0) ? ox / ow : ox;
+ };
+
+ /**
+ * Returns the three-dimensional X, Y, Z position in model space. This returns
+ * the Y value for a given coordinate based on the current set of transformations
+ * (scale, rotate, translate, etc.) The Y value can be used to place an object in
+ * space relative to the location of the original point once the transformations
+ * are no longer in use.<br />
+ * <br />
+ *
+ * @param {int | float} x 3D x coordinate to be mapped
+ * @param {int | float} y 3D y coordinate to be mapped
+ * @param {int | float} z 3D z coordinate to be mapped
+ *
+ * @returns {float}
+ *
+ * @see modelX
+ * @see modelZ
+ */
+ p.modelY = function(x, y, z) {
+ var mv = modelView.array();
+ var ci = cameraInv.array();
+
+ var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
+ var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
+ var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
+ var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];
+
+ var oy = ci[4] * ax + ci[5] * ay + ci[6] * az + ci[7] * aw;
+ var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;
+
+ return (ow !== 0) ? oy / ow : oy;
+ };
+
+ /**
+ * Returns the three-dimensional X, Y, Z position in model space. This returns
+ * the Z value for a given coordinate based on the current set of transformations
+ * (scale, rotate, translate, etc.) The Z value can be used to place an object in
+ * space relative to the location of the original point once the transformations
+ * are no longer in use.
+ *
+ * @param {int | float} x 3D x coordinate to be mapped
+ * @param {int | float} y 3D y coordinate to be mapped
+ * @param {int | float} z 3D z coordinate to be mapped
+ *
+ * @returns {float}
+ *
+ * @see modelX
+ * @see modelY
+ */
+ p.modelZ = function(x, y, z) {
+ var mv = modelView.array();
+ var ci = cameraInv.array();
+
+ var ax = mv[0] * x + mv[1] * y + mv[2] * z + mv[3];
+ var ay = mv[4] * x + mv[5] * y + mv[6] * z + mv[7];
+ var az = mv[8] * x + mv[9] * y + mv[10] * z + mv[11];
+ var aw = mv[12] * x + mv[13] * y + mv[14] * z + mv[15];
+
+ var oz = ci[8] * ax + ci[9] * ay + ci[10] * az + ci[11] * aw;
+ var ow = ci[12] * ax + ci[13] * ay + ci[14] * az + ci[15] * aw;
+
+ return (ow !== 0) ? oz / ow : oz;
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Material Properties
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Sets the ambient reflectance for shapes drawn to the screen. This is
+ * combined with the ambient light component of environment. The color
+ * components set through the parameters define the reflectance. For example in
+ * the default color mode, setting v1=255, v2=126, v3=0, would cause all the
+ * red light to reflect and half of the green light to reflect. Used in combination
+ * with <b>emissive()</b>, <b>specular()</b>, and <b>shininess()</b> in setting
+ * the materal properties of shapes.
+ *
+ * @param {int | float} gray
+ *
+ * @returns none
+ *
+ * @see emissive
+ * @see specular
+ * @see shininess
+ */
+ Drawing2D.prototype.ambient = DrawingShared.prototype.a3DOnlyFunction;
+
+ Drawing3D.prototype.ambient = function(v1, v2, v3) {
+ curContext.useProgram(programObject3D);
+ uniformi("uUsingMat3d", programObject3D, "uUsingMat", true);
+ var col = p.color(v1, v2, v3);
+ uniformf("uMaterialAmbient3d", programObject3D, "uMaterialAmbient", p.color.toGLArray(col).slice(0, 3));
+ };
+
+ /**
+ * Sets the emissive color of the material used for drawing shapes
+ * drawn to the screen. Used in combination with ambient(), specular(),
+ * and shininess() in setting the material properties of shapes.
+ *
+ * Can be called in the following ways:
+ *
+ * emissive(gray)
+ * @param {int | float} gray number specifying value between white and black
+ *
+ * emissive(color)
+ * @param {color} color any value of the color datatype
+ *
+ * emissive(v1, v2, v3)
+ * @param {int | float} v1 red or hue value
+ * @param {int | float} v2 green or saturation value
+ * @param {int | float} v3 blue or brightness value
+ *
+ * @returns none
+ *
+ * @see ambient
+ * @see specular
+ * @see shininess
+ */
+ Drawing2D.prototype.emissive = DrawingShared.prototype.a3DOnlyFunction;
+
+ Drawing3D.prototype.emissive = function(v1, v2, v3) {
+ curContext.useProgram(programObject3D);
+ uniformi("uUsingMat3d", programObject3D, "uUsingMat", true);
+ var col = p.color(v1, v2, v3);
+ uniformf("uMaterialEmissive3d", programObject3D, "uMaterialEmissive", p.color.toGLArray(col).slice(0, 3));
+ };
+
+ /**
+ * Sets the amount of gloss in the surface of shapes. Used in combination with
+ * <b>ambient()</b>, <b>specular()</b>, and <b>emissive()</b> in setting the
+ * material properties of shapes.
+ *
+ * @param {float} shine degree of shininess
+ *
+ * @returns none
+ */
+ Drawing2D.prototype.shininess = DrawingShared.prototype.a3DOnlyFunction;
+
+ Drawing3D.prototype.shininess = function(shine) {
+ curContext.useProgram(programObject3D);
+ uniformi("uUsingMat3d", programObject3D, "uUsingMat", true);
+ uniformf("uShininess3d", programObject3D, "uShininess", shine);
+ };
+
+ /**
+ * Sets the specular color of the materials used for shapes drawn to the screen,
+ * which sets the color of hightlights. Specular refers to light which bounces
+ * off a surface in a perferred direction (rather than bouncing in all directions
+ * like a diffuse light). Used in combination with emissive(), ambient(), and
+ * shininess() in setting the material properties of shapes.
+ *
+ * Can be called in the following ways:
+ *
+ * specular(gray)
+ * @param {int | float} gray number specifying value between white and black
+ *
+ * specular(gray, alpha)
+ * @param {int | float} gray number specifying value between white and black
+ * @param {int | float} alpha opacity
+ *
+ * specular(color)
+ * @param {color} color any value of the color datatype
+ *
+ * specular(v1, v2, v3)
+ * @param {int | float} v1 red or hue value
+ * @param {int | float} v2 green or saturation value
+ * @param {int | float} v3 blue or brightness value
+ *
+ * specular(v1, v2, v3, alpha)
+ * @param {int | float} v1 red or hue value
+ * @param {int | float} v2 green or saturation value
+ * @param {int | float} v3 blue or brightness value
+ * @param {int | float} alpha opacity
+ *
+ * @returns none
+ *
+ * @see ambient
+ * @see emissive
+ * @see shininess
+ */
+ Drawing2D.prototype.specular = DrawingShared.prototype.a3DOnlyFunction;
+
+ Drawing3D.prototype.specular = function(v1, v2, v3) {
+ curContext.useProgram(programObject3D);
+ uniformi("uUsingMat3d", programObject3D, "uUsingMat", true);
+ var col = p.color(v1, v2, v3);
+ uniformf("uMaterialSpecular3d", programObject3D, "uMaterialSpecular", p.color.toGLArray(col).slice(0, 3));
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Coordinates
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Takes a three-dimensional X, Y, Z position and returns the X value for
+ * where it will appear on a (two-dimensional) screen.
+ *
+ * @param {int | float} x 3D x coordinate to be mapped
+ * @param {int | float} y 3D y coordinate to be mapped
+ * @param {int | float} z 3D z optional coordinate to be mapped
+ *
+ * @returns {float}
+ *
+ * @see screenY
+ * @see screenZ
+ */
+ p.screenX = function( x, y, z ) {
+ var mv = modelView.array();
+ if( mv.length === 16 )
+ {
+ var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3];
+ var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7];
+ var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11];
+ var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15];
+
+ var pj = projection.array();
+
+ var ox = pj[ 0]*ax + pj[ 1]*ay + pj[ 2]*az + pj[ 3]*aw;
+ var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw;
+
+ if ( ow !== 0 ){
+ ox /= ow;
+ }
+ return p.width * ( 1 + ox ) / 2.0;
+ }
+ // We assume that we're in 2D
+ return modelView.multX(x, y);
+ };
+
+ /**
+ * Takes a three-dimensional X, Y, Z position and returns the Y value for
+ * where it will appear on a (two-dimensional) screen.
+ *
+ * @param {int | float} x 3D x coordinate to be mapped
+ * @param {int | float} y 3D y coordinate to be mapped
+ * @param {int | float} z 3D z optional coordinate to be mapped
+ *
+ * @returns {float}
+ *
+ * @see screenX
+ * @see screenZ
+ */
+ p.screenY = function screenY( x, y, z ) {
+ var mv = modelView.array();
+ if( mv.length === 16 ) {
+ var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3];
+ var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7];
+ var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11];
+ var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15];
+
+ var pj = projection.array();
+
+ var oy = pj[ 4]*ax + pj[ 5]*ay + pj[ 6]*az + pj[ 7]*aw;
+ var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw;
+
+ if ( ow !== 0 ){
+ oy /= ow;
+ }
+ return p.height * ( 1 + oy ) / 2.0;
+ }
+ // We assume that we're in 2D
+ return modelView.multY(x, y);
+ };
+
+ /**
+ * Takes a three-dimensional X, Y, Z position and returns the Z value for
+ * where it will appear on a (two-dimensional) screen.
+ *
+ * @param {int | float} x 3D x coordinate to be mapped
+ * @param {int | float} y 3D y coordinate to be mapped
+ * @param {int | float} z 3D z coordinate to be mapped
+ *
+ * @returns {float}
+ *
+ * @see screenX
+ * @see screenY
+ */
+ p.screenZ = function screenZ( x, y, z ) {
+ var mv = modelView.array();
+ if( mv.length !== 16 ) {
+ return 0;
+ }
+
+ var pj = projection.array();
+
+ var ax = mv[ 0]*x + mv[ 1]*y + mv[ 2]*z + mv[ 3];
+ var ay = mv[ 4]*x + mv[ 5]*y + mv[ 6]*z + mv[ 7];
+ var az = mv[ 8]*x + mv[ 9]*y + mv[10]*z + mv[11];
+ var aw = mv[12]*x + mv[13]*y + mv[14]*z + mv[15];
+
+ var oz = pj[ 8]*ax + pj[ 9]*ay + pj[10]*az + pj[11]*aw;
+ var ow = pj[12]*ax + pj[13]*ay + pj[14]*az + pj[15]*aw;
+
+ if ( ow !== 0 ) {
+ oz /= ow;
+ }
+ return ( oz + 1 ) / 2.0;
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Style functions
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * The fill() function sets the color used to fill shapes. For example, if you run <b>fill(204, 102, 0)</b>, all subsequent shapes will be filled with orange.
+ * This color is either specified in terms of the RGB or HSB color depending on the current <b>colorMode()</b>
+ *(the default color space is RGB, with each value in the range from 0 to 255).
+ * <br><br>When using hexadecimal notation to specify a color, use "#" or "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA).
+ * The # syntax uses six digits to specify a color (the way colors are specified in HTML and CSS). When using the hexadecimal notation starting with "0x",
+ * the hexadecimal value must be specified with eight characters; the first two characters define the alpha component and the remainder the red, green, and blue components.
+ * <br><br>The value for the parameter "gray" must be less than or equal to the current maximum value as specified by <b>colorMode()</b>. The default maximum value is 255.
+ * <br><br>To change the color of an image (or a texture), use tint().
+ *
+ * @param {int|float} gray number specifying value between white and black
+ * @param {int|float} value1 red or hue value
+ * @param {int|float} value2 green or saturation value
+ * @param {int|float} value3 blue or brightness value
+ * @param {int|float} alpha opacity of the fill
+ * @param {Color} color any value of the color datatype
+ * @param {int} hex color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
+ *
+ * @see #noFill()
+ * @see #stroke()
+ * @see #tint()
+ * @see #background()
+ * @see #colorMode()
+ */
+ DrawingShared.prototype.fill = function() {
+ var color = p.color.apply(this, arguments);
+ if(color === currentFillColor && doFill) {
+ return;
+ }
+ doFill = true;
+ currentFillColor = color;
+ };
+
+ Drawing2D.prototype.fill = function() {
+ DrawingShared.prototype.fill.apply(this, arguments);
+ isFillDirty = true;
+ };
+
+ Drawing3D.prototype.fill = function() {
+ DrawingShared.prototype.fill.apply(this, arguments);
+ fillStyle = p.color.toGLArray(currentFillColor);
+ };
+
+ function executeContextFill() {
+ if(doFill) {
+ if(isFillDirty) {
+ curContext.fillStyle = p.color.toString(currentFillColor);
+ isFillDirty = false;
+ }
+ curContext.fill();
+ }
+ }
+
+ /**
+ * The noFill() function disables filling geometry. If both <b>noStroke()</b> and <b>noFill()</b>
+ * are called, no shapes will be drawn to the screen.
+ *
+ * @see #fill()
+ *
+ */
+ p.noFill = function() {
+ doFill = false;
+ };
+
+ /**
+ * The stroke() function sets the color used to draw lines and borders around shapes. This color
+ * is either specified in terms of the RGB or HSB color depending on the
+ * current <b>colorMode()</b> (the default color space is RGB, with each
+ * value in the range from 0 to 255).
+ * <br><br>When using hexadecimal notation to specify a color, use "#" or
+ * "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA). The # syntax uses six
+ * digits to specify a color (the way colors are specified in HTML and CSS).
+ * When using the hexadecimal notation starting with "0x", the hexadecimal
+ * value must be specified with eight characters; the first two characters
+ * define the alpha component and the remainder the red, green, and blue
+ * components.
+ * <br><br>The value for the parameter "gray" must be less than or equal
+ * to the current maximum value as specified by <b>colorMode()</b>.
+ * The default maximum value is 255.
+ *
+ * @param {int|float} gray number specifying value between white and black
+ * @param {int|float} value1 red or hue value
+ * @param {int|float} value2 green or saturation value
+ * @param {int|float} value3 blue or brightness value
+ * @param {int|float} alpha opacity of the stroke
+ * @param {Color} color any value of the color datatype
+ * @param {int} hex color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
+ *
+ * @see #fill()
+ * @see #noStroke()
+ * @see #tint()
+ * @see #background()
+ * @see #colorMode()
+ */
+ DrawingShared.prototype.stroke = function() {
+ var color = p.color.apply(this, arguments);
+ if(color === currentStrokeColor && doStroke) {
+ return;
+ }
+ doStroke = true;
+ currentStrokeColor = color;
+ };
+
+ Drawing2D.prototype.stroke = function() {
+ DrawingShared.prototype.stroke.apply(this, arguments);
+ isStrokeDirty = true;
+ };
+
+ Drawing3D.prototype.stroke = function() {
+ DrawingShared.prototype.stroke.apply(this, arguments);
+ strokeStyle = p.color.toGLArray(currentStrokeColor);
+ };
+
+ function executeContextStroke() {
+ if(doStroke) {
+ if(isStrokeDirty) {
+ curContext.strokeStyle = p.color.toString(currentStrokeColor);
+ isStrokeDirty = false;
+ }
+ curContext.stroke();
+ }
+ }
+
+ /**
+ * The noStroke() function disables drawing the stroke (outline). If both <b>noStroke()</b> and
+ * <b>noFill()</b> are called, no shapes will be drawn to the screen.
+ *
+ * @see #stroke()
+ */
+ p.noStroke = function() {
+ doStroke = false;
+ };
+
+ /**
+ * The strokeWeight() function sets the width of the stroke used for lines, points, and the border around shapes.
+ * All widths are set in units of pixels.
+ *
+ * @param {int|float} w the weight (in pixels) of the stroke
+ */
+ DrawingShared.prototype.strokeWeight = function(w) {
+ lineWidth = w;
+ };
+
+ Drawing2D.prototype.strokeWeight = function(w) {
+ DrawingShared.prototype.strokeWeight.apply(this, arguments);
+ curContext.lineWidth = w;
+ };
+
+ Drawing3D.prototype.strokeWeight = function(w) {
+ DrawingShared.prototype.strokeWeight.apply(this, arguments);
+
+ // Processing groups the weight of points and lines under this one function,
+ // but for WebGL, we need to set a uniform for points and call a function for line.
+
+ curContext.useProgram(programObject2D);
+ uniformf("pointSize2d", programObject2D, "uPointSize", w);
+
+ curContext.useProgram(programObjectUnlitShape);
+ uniformf("pointSizeUnlitShape", programObjectUnlitShape, "uPointSize", w);
+
+ curContext.lineWidth(w);
+ };
+
+ /**
+ * The strokeCap() function sets the style for rendering line endings. These ends are either squared, extended, or rounded and
+ * specified with the corresponding parameters SQUARE, PROJECT, and ROUND. The default cap is ROUND.
+ * This function is not available with the P2D, P3D, or OPENGL renderers
+ *
+ * @param {int} value Either SQUARE, PROJECT, or ROUND
+ */
+ p.strokeCap = function(value) {
+ drawing.$ensureContext().lineCap = value;
+ };
+
+ /**
+ * The strokeJoin() function sets the style of the joints which connect line segments.
+ * These joints are either mitered, beveled, or rounded and specified with the corresponding parameters MITER, BEVEL, and ROUND. The default joint is MITER.
+ * This function is not available with the P2D, P3D, or OPENGL renderers
+ *
+ * @param {int} value Either SQUARE, PROJECT, or ROUND
+ */
+ p.strokeJoin = function(value) {
+ drawing.$ensureContext().lineJoin = value;
+ };
+
+ /**
+ * The smooth() function draws all geometry with smooth (anti-aliased) edges. This will slow down the frame rate of the application,
+ * but will enhance the visual refinement. <br/><br/>
+ * Note that smooth() will also improve image quality of resized images, and noSmooth() will disable image (and font) smoothing altogether.
+ * When working with a 3D sketch, smooth will draw points as circles rather than squares.
+ *
+ * @see #noSmooth()
+ * @see #hint()
+ * @see #size()
+ */
+
+ Drawing2D.prototype.smooth = function() {
+ renderSmooth = true;
+ var style = curElement.style;
+ style.setProperty("image-rendering", "optimizeQuality", "important");
+ style.setProperty("-ms-interpolation-mode", "bicubic", "important");
+ if (curContext.hasOwnProperty("mozImageSmoothingEnabled")) {
+ curContext.mozImageSmoothingEnabled = true;
+ }
+ };
+
+ Drawing3D.prototype.smooth = function(){
+ renderSmooth = true;
+ };
+
+ /**
+ * The noSmooth() function draws all geometry with jagged (aliased) edges.
+ *
+ * @see #smooth()
+ */
+
+ Drawing2D.prototype.noSmooth = function() {
+ renderSmooth = false;
+ var style = curElement.style;
+ style.setProperty("image-rendering", "optimizeSpeed", "important");
+ style.setProperty("image-rendering", "-moz-crisp-edges", "important");
+ style.setProperty("image-rendering", "-webkit-optimize-contrast", "important");
+ style.setProperty("image-rendering", "optimize-contrast", "important");
+ style.setProperty("-ms-interpolation-mode", "nearest-neighbor", "important");
+ if (curContext.hasOwnProperty("mozImageSmoothingEnabled")) {
+ curContext.mozImageSmoothingEnabled = false;
+ }
+ };
+
+ Drawing3D.prototype.noSmooth = function(){
+ renderSmooth = false;
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Vector drawing functions
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * The point() function draws a point, a coordinate in space at the dimension of one pixel.
+ * The first parameter is the horizontal value for the point, the second
+ * value is the vertical value for the point, and the optional third value
+ * is the depth value. Drawing this shape in 3D using the <b>z</b>
+ * parameter requires the P3D or OPENGL parameter in combination with
+ * size as shown in the above example.
+ *
+ * @param {int|float} x x-coordinate of the point
+ * @param {int|float} y y-coordinate of the point
+ * @param {int|float} z z-coordinate of the point
+ *
+ * @see #beginShape()
+ */
+ Drawing2D.prototype.point = function(x, y) {
+ if (!doStroke) {
+ return;
+ }
+ if (!renderSmooth) {
+ x = Math.round(x);
+ y = Math.round(y);
+ }
+ curContext.fillStyle = p.color.toString(currentStrokeColor);
+ isFillDirty = true;
+ // Draw a circle for any point larger than 1px
+ if (lineWidth > 1) {
+ curContext.beginPath();
+ curContext.arc(x, y, lineWidth / 2, 0, PConstants.TWO_PI, false);
+ curContext.fill();
+ } else {
+ curContext.fillRect(x, y, 1, 1);
+ }
+ };
+
+ Drawing3D.prototype.point = function(x, y, z) {
+ var model = new PMatrix3D();
+
+ // move point to position
+ model.translate(x, y, z || 0);
+ model.transpose();
+
+ var view = new PMatrix3D();
+ view.scale(1, -1, 1);
+ view.apply(modelView.array());
+ view.transpose();
+
+ curContext.useProgram(programObject2D);
+ uniformMatrix("uModel2d", programObject2D, "uModel", false, model.array());
+ uniformMatrix("uView2d", programObject2D, "uView", false, view.array());
+
+ if (lineWidth > 0 && doStroke) {
+ // this will be replaced with the new bit shifting color code
+ uniformf("uColor2d", programObject2D, "uColor", strokeStyle);
+ uniformi("uIsDrawingText2d", programObject2D, "uIsDrawingText", false);
+ uniformi("uSmooth2d", programObject2D, "uSmooth", renderSmooth);
+ vertexAttribPointer("aVertex2d", programObject2D, "aVertex", 3, pointBuffer);
+ disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
+ curContext.drawArrays(curContext.POINTS, 0, 1);
+ }
+ };
+
+ /**
+ * Using the <b>beginShape()</b> and <b>endShape()</b> functions allow creating more complex forms.
+ * <b>beginShape()</b> begins recording vertices for a shape and <b>endShape()</b> stops recording.
+ * The value of the <b>MODE</b> parameter tells it which types of shapes to create from the provided vertices.
+ * With no mode specified, the shape can be any irregular polygon. After calling the <b>beginShape()</b> function,
+ * a series of <b>vertex()</b> commands must follow. To stop drawing the shape, call <b>endShape()</b>.
+ * The <b>vertex()</b> function with two parameters specifies a position in 2D and the <b>vertex()</b>
+ * function with three parameters specifies a position in 3D. Each shape will be outlined with the current
+ * stroke color and filled with the fill color.
+ *
+ * @param {int} MODE either POINTS, LINES, TRIANGLES, TRIANGLE_FAN, TRIANGLE_STRIP, QUADS, and QUAD_STRIP.
+ *
+ * @see endShape
+ * @see vertex
+ * @see curveVertex
+ * @see bezierVertex
+ */
+ p.beginShape = function(type) {
+ curShape = type;
+ curvePoints = [];
+ vertArray = [];
+ };
+
+ /**
+ * All shapes are constructed by connecting a series of vertices. <b>vertex()</b> is used to specify the vertex
+ * coordinates for points, lines, triangles, quads, and polygons and is used exclusively within the <b>beginShape()</b>
+ * and <b>endShape()</b> function. <br /><br />Drawing a vertex in 3D using the <b>z</b> parameter requires the P3D or
+ * OPENGL parameter in combination with size as shown in the above example.<br /><br />This function is also used to map a
+ * texture onto the geometry. The <b>texture()</b> function declares the texture to apply to the geometry and the <b>u</b>
+ * and <b>v</b> coordinates set define the mapping of this texture to the form. By default, the coordinates used for
+ * <b>u</b> and <b>v</b> are specified in relation to the image's size in pixels, but this relation can be changed with
+ * <b>textureMode()</b>.
+ *
+ * @param {int | float} x x-coordinate of the vertex
+ * @param {int | float} y y-coordinate of the vertex
+ * @param {boolean} moveto flag to indicate whether this is a new subpath
+ *
+ * @see beginShape
+ * @see endShape
+ * @see bezierVertex
+ * @see curveVertex
+ * @see texture
+ */
+
+ Drawing2D.prototype.vertex = function(x, y, moveTo) {
+ var vert = [];
+
+ if (firstVert) { firstVert = false; }
+ vert.isVert = true;
+
+ vert[0] = x;
+ vert[1] = y;
+ vert[2] = 0;
+ vert[3] = 0;
+ vert[4] = 0;
+
+ // fill and stroke color
+ vert[5] = currentFillColor;
+ vert[6] = currentStrokeColor;
+
+ vertArray.push(vert);
+ if (moveTo) {
+ vertArray[vertArray.length-1].moveTo = moveTo;
+ }
+ };
+
+ Drawing3D.prototype.vertex = function(x, y, z, u, v) {
+ var vert = [];
+
+ if (firstVert) { firstVert = false; }
+ vert.isVert = true;
+
+ if (v === undef && usingTexture) {
+ v = u;
+ u = z;
+ z = 0;
+ }
+
+ // Convert u and v to normalized coordinates
+ if (u !== undef && v !== undef) {
+ if (curTextureMode === PConstants.IMAGE) {
+ u /= curTexture.width;
+ v /= curTexture.height;
+ }
+ u = u > 1 ? 1 : u;
+ u = u < 0 ? 0 : u;
+ v = v > 1 ? 1 : v;
+ v = v < 0 ? 0 : v;
+ }
+
+ vert[0] = x;
+ vert[1] = y;
+ vert[2] = z || 0;
+ vert[3] = u || 0;
+ vert[4] = v || 0;
+
+ // fill rgba
+ vert[5] = fillStyle[0];
+ vert[6] = fillStyle[1];
+ vert[7] = fillStyle[2];
+ vert[8] = fillStyle[3];
+ // stroke rgba
+ vert[9] = strokeStyle[0];
+ vert[10] = strokeStyle[1];
+ vert[11] = strokeStyle[2];
+ vert[12] = strokeStyle[3];
+ //normals
+ vert[13] = normalX;
+ vert[14] = normalY;
+ vert[15] = normalZ;
+
+ vertArray.push(vert);
+ };
+
+ /**
+ * @private
+ * Renders 3D points created from calls to vertex and beginShape/endShape
+ *
+ * @param {Array} vArray an array of vertex coordinate
+ * @param {Array} cArray an array of colours used for the vertices
+ *
+ * @see beginShape
+ * @see endShape
+ * @see vertex
+ */
+ var point3D = function(vArray, cArray){
+ var view = new PMatrix3D();
+ view.scale(1, -1, 1);
+ view.apply(modelView.array());
+ view.transpose();
+
+ curContext.useProgram(programObjectUnlitShape);
+
+ uniformMatrix("uViewUS", programObjectUnlitShape, "uView", false, view.array());
+ uniformi("uSmoothUS", programObjectUnlitShape, "uSmooth", renderSmooth);
+
+ vertexAttribPointer("aVertexUS", programObjectUnlitShape, "aVertex", 3, pointBuffer);
+ curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW);
+
+ vertexAttribPointer("aColorUS", programObjectUnlitShape, "aColor", 4, fillColorBuffer);
+ curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW);
+
+ curContext.drawArrays(curContext.POINTS, 0, vArray.length/3);
+ };
+
+ /**
+ * @private
+ * Renders 3D lines created from calls to beginShape/vertex/endShape - based on the mode specified LINES, LINE_LOOP, etc.
+ *
+ * @param {Array} vArray an array of vertex coordinate
+ * @param {String} mode either LINES, LINE_LOOP, or LINE_STRIP
+ * @param {Array} cArray an array of colours used for the vertices
+ *
+ * @see beginShape
+ * @see endShape
+ * @see vertex
+ */
+ var line3D = function(vArray, mode, cArray){
+ var ctxMode;
+ if (mode === "LINES"){
+ ctxMode = curContext.LINES;
+ }
+ else if(mode === "LINE_LOOP"){
+ ctxMode = curContext.LINE_LOOP;
+ }
+ else{
+ ctxMode = curContext.LINE_STRIP;
+ }
+
+ var view = new PMatrix3D();
+ view.scale(1, -1, 1);
+ view.apply(modelView.array());
+ view.transpose();
+
+ curContext.useProgram(programObjectUnlitShape);
+ uniformMatrix("uViewUS", programObjectUnlitShape, "uView", false, view.array());
+ vertexAttribPointer("aVertexUS", programObjectUnlitShape, "aVertex", 3, lineBuffer);
+ curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW);
+ vertexAttribPointer("aColorUS", programObjectUnlitShape, "aColor", 4, strokeColorBuffer);
+ curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW);
+ curContext.drawArrays(ctxMode, 0, vArray.length/3);
+ };
+
+ /**
+ * @private
+ * Render filled shapes created from calls to beginShape/vertex/endShape - based on the mode specified TRIANGLES, etc.
+ *
+ * @param {Array} vArray an array of vertex coordinate
+ * @param {String} mode either LINES, LINE_LOOP, or LINE_STRIP
+ * @param {Array} cArray an array of colours used for the vertices
+ * @param {Array} tArray an array of u,v coordinates for textures
+ *
+ * @see beginShape
+ * @see endShape
+ * @see vertex
+ */
+ var fill3D = function(vArray, mode, cArray, tArray){
+ var ctxMode;
+ if (mode === "TRIANGLES") {
+ ctxMode = curContext.TRIANGLES;
+ } else if(mode === "TRIANGLE_FAN") {
+ ctxMode = curContext.TRIANGLE_FAN;
+ } else {
+ ctxMode = curContext.TRIANGLE_STRIP;
+ }
+
+ var view = new PMatrix3D();
+ view.scale( 1, -1, 1 );
+ view.apply( modelView.array() );
+ view.transpose();
+
+ curContext.useProgram( programObject3D );
+ uniformMatrix( "model3d", programObject3D, "uModel", false, [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1] );
+ uniformMatrix( "view3d", programObject3D, "uView", false, view.array() );
+ curContext.enable( curContext.POLYGON_OFFSET_FILL );
+ curContext.polygonOffset( 1, 1 );
+ uniformf( "color3d", programObject3D, "uColor", [-1,0,0,0] );
+ vertexAttribPointer( "vertex3d", programObject3D, "aVertex", 3, fillBuffer );
+ curContext.bufferData( curContext.ARRAY_BUFFER, new Float32Array(vArray), curContext.STREAM_DRAW );
+
+ // if we are using a texture and a tint, then overwrite the
+ // contents of the color buffer with the current tint
+ if ( usingTexture && curTint !== null ){
+ curTint3d( cArray );
+ }
+
+ vertexAttribPointer( "aColor3d", programObject3D, "aColor", 4, fillColorBuffer );
+ curContext.bufferData( curContext.ARRAY_BUFFER, new Float32Array(cArray), curContext.STREAM_DRAW );
+
+ // No support for lights....yet
+ disableVertexAttribPointer( "aNormal3d", programObject3D, "aNormal" );
+
+ if ( usingTexture ) {
+ uniformi( "uUsingTexture3d", programObject3D, "uUsingTexture", usingTexture );
+ vertexAttribPointer( "aTexture3d", programObject3D, "aTexture", 2, shapeTexVBO );
+ curContext.bufferData( curContext.ARRAY_BUFFER, new Float32Array(tArray), curContext.STREAM_DRAW );
+ }
+
+ curContext.drawArrays( ctxMode, 0, vArray.length/3 );
+ curContext.disable( curContext.POLYGON_OFFSET_FILL );
+ };
+
+ /**
+ * this series of three operations is used a lot in Drawing2D.prototype.endShape
+ * and has been split off as its own function, to tighten the code and allow for
+ * fewer bugs.
+ */
+ function fillStrokeClose() {
+ executeContextFill();
+ executeContextStroke();
+ curContext.closePath();
+ }
+
+ /**
+ * The endShape() function is the companion to beginShape() and may only be called after beginShape().
+ * When endshape() is called, all of image data defined since the previous call to beginShape() is written
+ * into the image buffer.
+ *
+ * @param {int} MODE Use CLOSE to close the shape
+ *
+ * @see beginShape
+ */
+ Drawing2D.prototype.endShape = function(mode) {
+ // Duplicated in Drawing3D; too many variables used
+ if (vertArray.length === 0) { return; }
+
+ var closeShape = mode === PConstants.CLOSE;
+
+ // if the shape is closed, the first element is also the last element
+ if (closeShape) {
+ vertArray.push(vertArray[0]);
+ }
+
+ var lineVertArray = [];
+ var fillVertArray = [];
+ var colorVertArray = [];
+ var strokeVertArray = [];
+ var texVertArray = [];
+ var cachedVertArray;
+
+ firstVert = true;
+ var i, j, k;
+ var vertArrayLength = vertArray.length;
+
+ for (i = 0; i < vertArrayLength; i++) {
+ cachedVertArray = vertArray[i];
+ for (j = 0; j < 3; j++) {
+ fillVertArray.push(cachedVertArray[j]);
+ }
+ }
+
+ // 5,6,7,8
+ // R,G,B,A - fill colour
+ for (i = 0; i < vertArrayLength; i++) {
+ cachedVertArray = vertArray[i];
+ for (j = 5; j < 9; j++) {
+ colorVertArray.push(cachedVertArray[j]);
+ }
+ }
+
+ // 9,10,11,12
+ // R, G, B, A - stroke colour
+ for (i = 0; i < vertArrayLength; i++) {
+ cachedVertArray = vertArray[i];
+ for (j = 9; j < 13; j++) {
+ strokeVertArray.push(cachedVertArray[j]);
+ }
+ }
+
+ // texture u,v
+ for (i = 0; i < vertArrayLength; i++) {
+ cachedVertArray = vertArray[i];
+ texVertArray.push(cachedVertArray[3]);
+ texVertArray.push(cachedVertArray[4]);
+ }
+
+ // curveVertex
+ if ( isCurve && (curShape === PConstants.POLYGON || curShape === undef) ) {
+ if (vertArrayLength > 3) {
+ var b = [],
+ s = 1 - curTightness;
+ curContext.beginPath();
+ curContext.moveTo(vertArray[1][0], vertArray[1][1]);
+ /*
+ * Matrix to convert from Catmull-Rom to cubic Bezier
+ * where t = curTightness
+ * |0 1 0 0 |
+ * |(t-1)/6 1 (1-t)/6 0 |
+ * |0 (1-t)/6 1 (t-1)/6 |
+ * |0 0 0 0 |
+ */
+ for (i = 1; (i+2) < vertArrayLength; i++) {
+ cachedVertArray = vertArray[i];
+ b[0] = [cachedVertArray[0], cachedVertArray[1]];
+ b[1] = [cachedVertArray[0] + (s * vertArray[i+1][0] - s * vertArray[i-1][0]) / 6,
+ cachedVertArray[1] + (s * vertArray[i+1][1] - s * vertArray[i-1][1]) / 6];
+ b[2] = [vertArray[i+1][0] + (s * vertArray[i][0] - s * vertArray[i+2][0]) / 6,
+ vertArray[i+1][1] + (s * vertArray[i][1] - s * vertArray[i+2][1]) / 6];
+ b[3] = [vertArray[i+1][0], vertArray[i+1][1]];
+ curContext.bezierCurveTo(b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1]);
+ }
+ fillStrokeClose();
+ }
+ }
+
+ // bezierVertex
+ else if ( isBezier && (curShape === PConstants.POLYGON || curShape === undef) ) {
+ curContext.beginPath();
+ for (i = 0; i < vertArrayLength; i++) {
+ cachedVertArray = vertArray[i];
+ if (vertArray[i].isVert) { //if it is a vertex move to the position
+ if (vertArray[i].moveTo) {
+ curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
+ } else {
+ curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
+ }
+ } else { //otherwise continue drawing bezier
+ curContext.bezierCurveTo(vertArray[i][0], vertArray[i][1], vertArray[i][2], vertArray[i][3], vertArray[i][4], vertArray[i][5]);
+ }
+ }
+ fillStrokeClose();
+ }
+
+ // render the vertices provided
+ else {
+ if (curShape === PConstants.POINTS) {
+ for (i = 0; i < vertArrayLength; i++) {
+ cachedVertArray = vertArray[i];
+ if (doStroke) {
+ p.stroke(cachedVertArray[6]);
+ }
+ p.point(cachedVertArray[0], cachedVertArray[1]);
+ }
+ } else if (curShape === PConstants.LINES) {
+ for (i = 0; (i + 1) < vertArrayLength; i+=2) {
+ cachedVertArray = vertArray[i];
+ if (doStroke) {
+ p.stroke(vertArray[i+1][6]);
+ }
+ p.line(cachedVertArray[0], cachedVertArray[1], vertArray[i+1][0], vertArray[i+1][1]);
+ }
+ } else if (curShape === PConstants.TRIANGLES) {
+ for (i = 0; (i + 2) < vertArrayLength; i+=3) {
+ cachedVertArray = vertArray[i];
+ curContext.beginPath();
+ curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
+ curContext.lineTo(vertArray[i+1][0], vertArray[i+1][1]);
+ curContext.lineTo(vertArray[i+2][0], vertArray[i+2][1]);
+ curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
+
+ if (doFill) {
+ p.fill(vertArray[i+2][5]);
+ executeContextFill();
+ }
+ if (doStroke) {
+ p.stroke(vertArray[i+2][6]);
+ executeContextStroke();
+ }
+
+ curContext.closePath();
+ }
+ } else if (curShape === PConstants.TRIANGLE_STRIP) {
+ for (i = 0; (i+1) < vertArrayLength; i++) {
+ cachedVertArray = vertArray[i];
+ curContext.beginPath();
+ curContext.moveTo(vertArray[i+1][0], vertArray[i+1][1]);
+ curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
+
+ if (doStroke) {
+ p.stroke(vertArray[i+1][6]);
+ }
+ if (doFill) {
+ p.fill(vertArray[i+1][5]);
+ }
+
+ if (i + 2 < vertArrayLength) {
+ curContext.lineTo(vertArray[i+2][0], vertArray[i+2][1]);
+ if (doStroke) {
+ p.stroke(vertArray[i+2][6]);
+ }
+ if (doFill) {
+ p.fill(vertArray[i+2][5]);
+ }
+ }
+ fillStrokeClose();
+ }
+ } else if (curShape === PConstants.TRIANGLE_FAN) {
+ if (vertArrayLength > 2) {
+ curContext.beginPath();
+ curContext.moveTo(vertArray[0][0], vertArray[0][1]);
+ curContext.lineTo(vertArray[1][0], vertArray[1][1]);
+ curContext.lineTo(vertArray[2][0], vertArray[2][1]);
+
+ if (doFill) {
+ p.fill(vertArray[2][5]);
+ executeContextFill();
+ }
+ if (doStroke) {
+ p.stroke(vertArray[2][6]);
+ executeContextStroke();
+ }
+
+ curContext.closePath();
+ for (i = 3; i < vertArrayLength; i++) {
+ cachedVertArray = vertArray[i];
+ curContext.beginPath();
+ curContext.moveTo(vertArray[0][0], vertArray[0][1]);
+ curContext.lineTo(vertArray[i-1][0], vertArray[i-1][1]);
+ curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
+
+ if (doFill) {
+ p.fill(cachedVertArray[5]);
+ executeContextFill();
+ }
+ if (doStroke) {
+ p.stroke(cachedVertArray[6]);
+ executeContextStroke();
+ }
+
+ curContext.closePath();
+ }
+ }
+ } else if (curShape === PConstants.QUADS) {
+ for (i = 0; (i + 3) < vertArrayLength; i+=4) {
+ cachedVertArray = vertArray[i];
+ curContext.beginPath();
+ curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
+ for (j = 1; j < 4; j++) {
+ curContext.lineTo(vertArray[i+j][0], vertArray[i+j][1]);
+ }
+ curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
+
+ if (doFill) {
+ p.fill(vertArray[i+3][5]);
+ executeContextFill();
+ }
+ if (doStroke) {
+ p.stroke(vertArray[i+3][6]);
+ executeContextStroke();
+ }
+
+ curContext.closePath();
+ }
+ } else if (curShape === PConstants.QUAD_STRIP) {
+ if (vertArrayLength > 3) {
+ for (i = 0; (i+1) < vertArrayLength; i+=2) {
+ cachedVertArray = vertArray[i];
+ curContext.beginPath();
+ if (i+3 < vertArrayLength) {
+ curContext.moveTo(vertArray[i+2][0], vertArray[i+2][1]);
+ curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
+ curContext.lineTo(vertArray[i+1][0], vertArray[i+1][1]);
+ curContext.lineTo(vertArray[i+3][0], vertArray[i+3][1]);
+
+ if (doFill) {
+ p.fill(vertArray[i+3][5]);
+ }
+ if (doStroke) {
+ p.stroke(vertArray[i+3][6]);
+ }
+ } else {
+ curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
+ curContext.lineTo(vertArray[i+1][0], vertArray[i+1][1]);
+ }
+ fillStrokeClose();
+ }
+ }
+ } else {
+ curContext.beginPath();
+ curContext.moveTo(vertArray[0][0], vertArray[0][1]);
+ for (i = 1; i < vertArrayLength; i++) {
+ cachedVertArray = vertArray[i];
+ if (cachedVertArray.isVert) { //if it is a vertex move to the position
+ if (cachedVertArray.moveTo) {
+ curContext.moveTo(cachedVertArray[0], cachedVertArray[1]);
+ } else {
+ curContext.lineTo(cachedVertArray[0], cachedVertArray[1]);
+ }
+ }
+ }
+ fillStrokeClose();
+ }
+ }
+
+ // Reset some settings
+ isCurve = false;
+ isBezier = false;
+ curveVertArray = [];
+ curveVertCount = 0;
+
+ // If the shape is closed, the first element was added as last element.
+ // We must remove it again to prevent the list of vertices from growing
+ // over successive calls to endShape(CLOSE)
+ if (closeShape) {
+ vertArray.pop();
+ }
+ };
+
+ Drawing3D.prototype.endShape = function(mode) {
+ // Duplicated in Drawing3D; too many variables used
+ if (vertArray.length === 0) { return; }
+
+ var closeShape = mode === PConstants.CLOSE;
+ var lineVertArray = [];
+ var fillVertArray = [];
+ var colorVertArray = [];
+ var strokeVertArray = [];
+ var texVertArray = [];
+ var cachedVertArray;
+
+ firstVert = true;
+ var i, j, k;
+ var vertArrayLength = vertArray.length;
+
+ for (i = 0; i < vertArrayLength; i++) {
+ cachedVertArray = vertArray[i];
+ for (j = 0; j < 3; j++) {
+ fillVertArray.push(cachedVertArray[j]);
+ }
+ }
+
+ // 5,6,7,8
+ // R,G,B,A - fill colour
+ for (i = 0; i < vertArrayLength; i++) {
+ cachedVertArray = vertArray[i];
+ for (j = 5; j < 9; j++) {
+ colorVertArray.push(cachedVertArray[j]);
+ }
+ }
+
+ // 9,10,11,12
+ // R, G, B, A - stroke colour
+ for (i = 0; i < vertArrayLength; i++) {
+ cachedVertArray = vertArray[i];
+ for (j = 9; j < 13; j++) {
+ strokeVertArray.push(cachedVertArray[j]);
+ }
+ }
+
+ // texture u,v
+ for (i = 0; i < vertArrayLength; i++) {
+ cachedVertArray = vertArray[i];
+ texVertArray.push(cachedVertArray[3]);
+ texVertArray.push(cachedVertArray[4]);
+ }
+
+ // if shape is closed, push the first point into the last point (including colours)
+ if (closeShape) {
+ fillVertArray.push(vertArray[0][0]);
+ fillVertArray.push(vertArray[0][1]);
+ fillVertArray.push(vertArray[0][2]);
+
+ for (i = 5; i < 9; i++) {
+ colorVertArray.push(vertArray[0][i]);
+ }
+
+ for (i = 9; i < 13; i++) {
+ strokeVertArray.push(vertArray[0][i]);
+ }
+
+ texVertArray.push(vertArray[0][3]);
+ texVertArray.push(vertArray[0][4]);
+ }
+ // End duplication
+
+ // curveVertex
+ if ( isCurve && (curShape === PConstants.POLYGON || curShape === undef) ) {
+ lineVertArray = fillVertArray;
+ if (doStroke) {
+ line3D(lineVertArray, null, strokeVertArray);
+ }
+ if (doFill) {
+ fill3D(fillVertArray, null, colorVertArray);
+ }
+ }
+ // bezierVertex
+ else if ( isBezier && (curShape === PConstants.POLYGON || curShape === undef) ) {
+ lineVertArray = fillVertArray;
+ lineVertArray.splice(lineVertArray.length - 3);
+ strokeVertArray.splice(strokeVertArray.length - 4);
+ if (doStroke) {
+ line3D(lineVertArray, null, strokeVertArray);
+ }
+ if (doFill) {
+ fill3D(fillVertArray, "TRIANGLES", colorVertArray);
+ }
+ }
+
+ // render the vertices provided
+ else {
+ if (curShape === PConstants.POINTS) { // if POINTS was the specified parameter in beginShape
+ for (i = 0; i < vertArrayLength; i++) { // loop through and push the point location information to the array
+ cachedVertArray = vertArray[i];
+ for (j = 0; j < 3; j++) {
+ lineVertArray.push(cachedVertArray[j]);
+ }
+ }
+ point3D(lineVertArray, strokeVertArray); // render function for points
+ } else if (curShape === PConstants.LINES) { // if LINES was the specified parameter in beginShape
+ for (i = 0; i < vertArrayLength; i++) { // loop through and push the point location information to the array
+ cachedVertArray = vertArray[i];
+ for (j = 0; j < 3; j++) {
+ lineVertArray.push(cachedVertArray[j]);
+ }
+ }
+ for (i = 0; i < vertArrayLength; i++) { // loop through and push the color information to the array
+ cachedVertArray = vertArray[i];
+ for (j = 5; j < 9; j++) {
+ colorVertArray.push(cachedVertArray[j]);
+ }
+ }
+ line3D(lineVertArray, "LINES", strokeVertArray); // render function for lines
+ } else if (curShape === PConstants.TRIANGLES) { // if TRIANGLES was the specified parameter in beginShape
+ if (vertArrayLength > 2) {
+ for (i = 0; (i+2) < vertArrayLength; i+=3) { // loop through the array per triangle
+ fillVertArray = [];
+ texVertArray = [];
+ lineVertArray = [];
+ colorVertArray = [];
+ strokeVertArray = [];
+ for (j = 0; j < 3; j++) {
+ for (k = 0; k < 3; k++) { // loop through and push
+ lineVertArray.push(vertArray[i+j][k]); // the line point location information
+ fillVertArray.push(vertArray[i+j][k]); // and fill point location information
+ }
+ }
+ for (j = 0; j < 3; j++) { // loop through and push the texture information
+ for (k = 3; k < 5; k++) {
+ texVertArray.push(vertArray[i+j][k]);
+ }
+ }
+ for (j = 0; j < 3; j++) {
+ for (k = 5; k < 9; k++) { // loop through and push
+ colorVertArray.push(vertArray[i+j][k]); // the colour information
+ strokeVertArray.push(vertArray[i+j][k+4]);// and the stroke information
+ }
+ }
+ if (doStroke) {
+ line3D(lineVertArray, "LINE_LOOP", strokeVertArray ); // line render function
+ }
+ if (doFill || usingTexture) {
+ fill3D(fillVertArray, "TRIANGLES", colorVertArray, texVertArray); // fill shape render function
+ }
+ }
+ }
+ } else if (curShape === PConstants.TRIANGLE_STRIP) { // if TRIANGLE_STRIP was the specified parameter in beginShape
+ if (vertArrayLength > 2) {
+ for (i = 0; (i+2) < vertArrayLength; i++) {
+ lineVertArray = [];
+ fillVertArray = [];
+ strokeVertArray = [];
+ colorVertArray = [];
+ texVertArray = [];
+ for (j = 0; j < 3; j++) {
+ for (k = 0; k < 3; k++) {
+ lineVertArray.push(vertArray[i+j][k]);
+ fillVertArray.push(vertArray[i+j][k]);
+ }
+ }
+ for (j = 0; j < 3; j++) {
+ for (k = 3; k < 5; k++) {
+ texVertArray.push(vertArray[i+j][k]);
+ }
+ }
+ for (j = 0; j < 3; j++) {
+ for (k = 5; k < 9; k++) {
+ strokeVertArray.push(vertArray[i+j][k+4]);
+ colorVertArray.push(vertArray[i+j][k]);
+ }
+ }
+
+ if (doFill || usingTexture) {
+ fill3D(fillVertArray, "TRIANGLE_STRIP", colorVertArray, texVertArray);
+ }
+ if (doStroke) {
+ line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
+ }
+ }
+ }
+ } else if (curShape === PConstants.TRIANGLE_FAN) {
+ if (vertArrayLength > 2) {
+ for (i = 0; i < 3; i++) {
+ cachedVertArray = vertArray[i];
+ for (j = 0; j < 3; j++) {
+ lineVertArray.push(cachedVertArray[j]);
+ }
+ }
+ for (i = 0; i < 3; i++) {
+ cachedVertArray = vertArray[i];
+ for (j = 9; j < 13; j++) {
+ strokeVertArray.push(cachedVertArray[j]);
+ }
+ }
+ if (doStroke) {
+ line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
+ }
+
+ for (i = 2; (i+1) < vertArrayLength; i++) {
+ lineVertArray = [];
+ strokeVertArray = [];
+ lineVertArray.push(vertArray[0][0]);
+ lineVertArray.push(vertArray[0][1]);
+ lineVertArray.push(vertArray[0][2]);
+
+ strokeVertArray.push(vertArray[0][9]);
+ strokeVertArray.push(vertArray[0][10]);
+ strokeVertArray.push(vertArray[0][11]);
+ strokeVertArray.push(vertArray[0][12]);
+
+ for (j = 0; j < 2; j++) {
+ for (k = 0; k < 3; k++) {
+ lineVertArray.push(vertArray[i+j][k]);
+ }
+ }
+ for (j = 0; j < 2; j++) {
+ for (k = 9; k < 13; k++) {
+ strokeVertArray.push(vertArray[i+j][k]);
+ }
+ }
+ if (doStroke) {
+ line3D(lineVertArray, "LINE_STRIP",strokeVertArray);
+ }
+ }
+ if (doFill || usingTexture) {
+ fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray, texVertArray);
+ }
+ }
+ } else if (curShape === PConstants.QUADS) {
+ for (i = 0; (i + 3) < vertArrayLength; i+=4) {
+ lineVertArray = [];
+ for (j = 0; j < 4; j++) {
+ cachedVertArray = vertArray[i+j];
+ for (k = 0; k < 3; k++) {
+ lineVertArray.push(cachedVertArray[k]);
+ }
+ }
+ if (doStroke) {
+ line3D(lineVertArray, "LINE_LOOP",strokeVertArray);
+ }
+
+ if (doFill) {
+ fillVertArray = [];
+ colorVertArray = [];
+ texVertArray = [];
+ for (j = 0; j < 3; j++) {
+ fillVertArray.push(vertArray[i][j]);
+ }
+ for (j = 5; j < 9; j++) {
+ colorVertArray.push(vertArray[i][j]);
+ }
+
+ for (j = 0; j < 3; j++) {
+ fillVertArray.push(vertArray[i+1][j]);
+ }
+ for (j = 5; j < 9; j++) {
+ colorVertArray.push(vertArray[i+1][j]);
+ }
+
+ for (j = 0; j < 3; j++) {
+ fillVertArray.push(vertArray[i+3][j]);
+ }
+ for (j = 5; j < 9; j++) {
+ colorVertArray.push(vertArray[i+3][j]);
+ }
+
+ for (j = 0; j < 3; j++) {
+ fillVertArray.push(vertArray[i+2][j]);
+ }
+ for (j = 5; j < 9; j++) {
+ colorVertArray.push(vertArray[i+2][j]);
+ }
+
+ if (usingTexture) {
+ texVertArray.push(vertArray[i+0][3]);
+ texVertArray.push(vertArray[i+0][4]);
+ texVertArray.push(vertArray[i+1][3]);
+ texVertArray.push(vertArray[i+1][4]);
+ texVertArray.push(vertArray[i+3][3]);
+ texVertArray.push(vertArray[i+3][4]);
+ texVertArray.push(vertArray[i+2][3]);
+ texVertArray.push(vertArray[i+2][4]);
+ }
+
+ fill3D(fillVertArray, "TRIANGLE_STRIP", colorVertArray, texVertArray);
+ }
+ }
+ } else if (curShape === PConstants.QUAD_STRIP) {
+ var tempArray = [];
+ if (vertArrayLength > 3) {
+ for (i = 0; i < 2; i++) {
+ cachedVertArray = vertArray[i];
+ for (j = 0; j < 3; j++) {
+ lineVertArray.push(cachedVertArray[j]);
+ }
+ }
+
+ for (i = 0; i < 2; i++) {
+ cachedVertArray = vertArray[i];
+ for (j = 9; j < 13; j++) {
+ strokeVertArray.push(cachedVertArray[j]);
+ }
+ }
+
+ line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
+ if (vertArrayLength > 4 && vertArrayLength % 2 > 0) {
+ tempArray = fillVertArray.splice(fillVertArray.length - 3);
+ vertArray.pop();
+ }
+ for (i = 0; (i+3) < vertArrayLength; i+=2) {
+ lineVertArray = [];
+ strokeVertArray = [];
+ for (j = 0; j < 3; j++) {
+ lineVertArray.push(vertArray[i+1][j]);
+ }
+ for (j = 0; j < 3; j++) {
+ lineVertArray.push(vertArray[i+3][j]);
+ }
+ for (j = 0; j < 3; j++) {
+ lineVertArray.push(vertArray[i+2][j]);
+ }
+ for (j = 0; j < 3; j++) {
+ lineVertArray.push(vertArray[i+0][j]);
+ }
+ for (j = 9; j < 13; j++) {
+ strokeVertArray.push(vertArray[i+1][j]);
+ }
+ for (j = 9; j < 13; j++) {
+ strokeVertArray.push(vertArray[i+3][j]);
+ }
+ for (j = 9; j < 13; j++) {
+ strokeVertArray.push(vertArray[i+2][j]);
+ }
+ for (j = 9; j < 13; j++) {
+ strokeVertArray.push(vertArray[i+0][j]);
+ }
+ if (doStroke) {
+ line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
+ }
+ }
+
+ if (doFill || usingTexture) {
+ fill3D(fillVertArray, "TRIANGLE_LIST", colorVertArray, texVertArray);
+ }
+ }
+ }
+ // If the user didn't specify a type (LINES, TRIANGLES, etc)
+ else {
+ // If only one vertex was specified, it must be a point
+ if (vertArrayLength === 1) {
+ for (j = 0; j < 3; j++) {
+ lineVertArray.push(vertArray[0][j]);
+ }
+ for (j = 9; j < 13; j++) {
+ strokeVertArray.push(vertArray[0][j]);
+ }
+ point3D(lineVertArray,strokeVertArray);
+ } else {
+ for (i = 0; i < vertArrayLength; i++) {
+ cachedVertArray = vertArray[i];
+ for (j = 0; j < 3; j++) {
+ lineVertArray.push(cachedVertArray[j]);
+ }
+ for (j = 5; j < 9; j++) {
+ strokeVertArray.push(cachedVertArray[j]);
+ }
+ }
+ if (doStroke && closeShape) {
+ line3D(lineVertArray, "LINE_LOOP", strokeVertArray);
+ } else if (doStroke && !closeShape) {
+ line3D(lineVertArray, "LINE_STRIP", strokeVertArray);
+ }
+
+ // fill is ignored if textures are used
+ if (doFill || usingTexture) {
+ fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray, texVertArray);
+ }
+ }
+ }
+ // everytime beginShape is followed by a call to
+ // texture(), texturing it turned back on. We do this to
+ // figure out if the shape should be textured or filled
+ // with a color.
+ usingTexture = false;
+ curContext.useProgram(programObject3D);
+ uniformi("usingTexture3d", programObject3D, "uUsingTexture", usingTexture);
+ }
+
+ // Reset some settings
+ isCurve = false;
+ isBezier = false;
+ curveVertArray = [];
+ curveVertCount = 0;
+ };
+
+ /**
+ * The function splineForward() setup forward-differencing matrix to be used for speedy
+ * curve rendering. It's based on using a specific number
+ * of curve segments and just doing incremental adds for each
+ * vertex of the segment, rather than running the mathematically
+ * expensive cubic equation. This function is used by both curveDetail and bezierDetail.
+ *
+ * @param {int} segments number of curve segments to use when drawing
+ * @param {PMatrix3D} matrix target object for the new matrix
+ */
+ var splineForward = function(segments, matrix) {
+ var f = 1.0 / segments;
+ var ff = f * f;
+ var fff = ff * f;
+
+ matrix.set(0, 0, 0, 1, fff, ff, f, 0, 6 * fff, 2 * ff, 0, 0, 6 * fff, 0, 0, 0);
+ };
+
+ /**
+ * The curveInit() function set the number of segments to use when drawing a Catmull-Rom
+ * curve, and setting the s parameter, which defines how tightly
+ * the curve fits to each vertex. Catmull-Rom curves are actually
+ * a subset of this curve type where the s is set to zero.
+ * This in an internal function used by curveDetail() and curveTightness().
+ */
+ var curveInit = function() {
+ // allocate only if/when used to save startup time
+ if (!curveDrawMatrix) {
+ curveBasisMatrix = new PMatrix3D();
+ curveDrawMatrix = new PMatrix3D();
+ curveInited = true;
+ }
+
+ var s = curTightness;
+ curveBasisMatrix.set((s - 1) / 2, (s + 3) / 2, (-3 - s) / 2, (1 - s) / 2,
+ (1 - s), (-5 - s) / 2, (s + 2), (s - 1) / 2,
+ (s - 1) / 2, 0, (1 - s) / 2, 0, 0, 1, 0, 0);
+
+ splineForward(curveDet, curveDrawMatrix);
+
+ if (!bezierBasisInverse) {
+ //bezierBasisInverse = bezierBasisMatrix.get();
+ //bezierBasisInverse.invert();
+ curveToBezierMatrix = new PMatrix3D();
+ }
+
+ // TODO only needed for PGraphicsJava2D? if so, move it there
+ // actually, it's generally useful for other renderers, so keep it
+ // or hide the implementation elsewhere.
+ curveToBezierMatrix.set(curveBasisMatrix);
+ curveToBezierMatrix.preApply(bezierBasisInverse);
+
+ // multiply the basis and forward diff matrices together
+ // saves much time since this needn't be done for each curve
+ curveDrawMatrix.apply(curveBasisMatrix);
+ };
+
+ /**
+ * Specifies vertex coordinates for Bezier curves. Each call to <b>bezierVertex()</b> defines the position of two control
+ * points and one anchor point of a Bezier curve, adding a new segment to a line or shape. The first time
+ * <b>bezierVertex()</b> is used within a <b>beginShape()</b> call, it must be prefaced with a call to <b>vertex()</b>
+ * to set the first anchor point. This function must be used between <b>beginShape()</b> and <b>endShape()</b> and only
+ * when there is no MODE parameter specified to <b>beginShape()</b>. Using the 3D version of requires rendering with P3D
+ * or OPENGL (see the Environment reference for more information). <br /> <br /> <b>NOTE: </b> Fill does not work properly yet.
+ *
+ * @param {float | int} cx1 The x-coordinate of 1st control point
+ * @param {float | int} cy1 The y-coordinate of 1st control point
+ * @param {float | int} cz1 The z-coordinate of 1st control point
+ * @param {float | int} cx2 The x-coordinate of 2nd control point
+ * @param {float | int} cy2 The y-coordinate of 2nd control point
+ * @param {float | int} cz2 The z-coordinate of 2nd control point
+ * @param {float | int} x The x-coordinate of the anchor point
+ * @param {float | int} y The y-coordinate of the anchor point
+ * @param {float | int} z The z-coordinate of the anchor point
+ *
+ * @see curveVertex
+ * @see vertex
+ * @see bezier
+ */
+ Drawing2D.prototype.bezierVertex = function() {
+ isBezier = true;
+ var vert = [];
+ if (firstVert) {
+ throw ("vertex() must be used at least once before calling bezierVertex()");
+ }
+
+ for (var i = 0; i < arguments.length; i++) {
+ vert[i] = arguments[i];
+ }
+ vertArray.push(vert);
+ vertArray[vertArray.length -1].isVert = false;
+ };
+
+ Drawing3D.prototype.bezierVertex = function() {
+ isBezier = true;
+ var vert = [];
+ if (firstVert) {
+ throw ("vertex() must be used at least once before calling bezierVertex()");
+ }
+
+ if (arguments.length === 9) {
+ if (bezierDrawMatrix === undef) {
+ bezierDrawMatrix = new PMatrix3D();
+ }
+ // setup matrix for forward differencing to speed up drawing
+ var lastPoint = vertArray.length - 1;
+ splineForward( bezDetail, bezierDrawMatrix );
+ bezierDrawMatrix.apply( bezierBasisMatrix );
+ var draw = bezierDrawMatrix.array();
+ var x1 = vertArray[lastPoint][0],
+ y1 = vertArray[lastPoint][1],
+ z1 = vertArray[lastPoint][2];
+ var xplot1 = draw[4] * x1 + draw[5] * arguments[0] + draw[6] * arguments[3] + draw[7] * arguments[6];
+ var xplot2 = draw[8] * x1 + draw[9] * arguments[0] + draw[10]* arguments[3] + draw[11]* arguments[6];
+ var xplot3 = draw[12]* x1 + draw[13]* arguments[0] + draw[14]* arguments[3] + draw[15]* arguments[6];
+
+ var yplot1 = draw[4] * y1 + draw[5] * arguments[1] + draw[6] * arguments[4] + draw[7] * arguments[7];
+ var yplot2 = draw[8] * y1 + draw[9] * arguments[1] + draw[10]* arguments[4] + draw[11]* arguments[7];
+ var yplot3 = draw[12]* y1 + draw[13]* arguments[1] + draw[14]* arguments[4] + draw[15]* arguments[7];
+
+ var zplot1 = draw[4] * z1 + draw[5] * arguments[2] + draw[6] * arguments[5] + draw[7] * arguments[8];
+ var zplot2 = draw[8] * z1 + draw[9] * arguments[2] + draw[10]* arguments[5] + draw[11]* arguments[8];
+ var zplot3 = draw[12]* z1 + draw[13]* arguments[2] + draw[14]* arguments[5] + draw[15]* arguments[8];
+ for (var j = 0; j < bezDetail; j++) {
+ x1 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
+ y1 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
+ z1 += zplot1; zplot1 += zplot2; zplot2 += zplot3;
+ p.vertex(x1, y1, z1);
+ }
+ p.vertex(arguments[6], arguments[7], arguments[8]);
+ }
+ };
+
+ /**
+ * Sets a texture to be applied to vertex points. The <b>texture()</b> function
+ * must be called between <b>beginShape()</b> and <b>endShape()</b> and before
+ * any calls to vertex().
+ *
+ * When textures are in use, the fill color is ignored. Instead, use tint() to
+ * specify the color of the texture as it is applied to the shape.
+ *
+ * @param {PImage} pimage the texture to apply
+ *
+ * @returns none
+ *
+ * @see textureMode
+ * @see beginShape
+ * @see endShape
+ * @see vertex
+ */
+ p.texture = function(pimage) {
+ var curContext = drawing.$ensureContext();
+
+ if (pimage.__texture) {
+ curContext.bindTexture(curContext.TEXTURE_2D, pimage.__texture);
+ } else if (pimage.localName === "canvas") {
+ curContext.bindTexture(curContext.TEXTURE_2D, canTex);
+ curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, pimage);
+ curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR);
+ curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR);
+ curContext.generateMipmap(curContext.TEXTURE_2D);
+ curTexture.width = pimage.width;
+ curTexture.height = pimage.height;
+ } else {
+ var texture = curContext.createTexture(),
+ cvs = document.createElement('canvas'),
+ cvsTextureCtx = cvs.getContext('2d'),
+ pot;
+
+ // WebGL requires power of two textures
+ if (pimage.width & (pimage.width-1) === 0) {
+ cvs.width = pimage.width;
+ } else {
+ pot = 1;
+ while (pot < pimage.width) {
+ pot *= 2;
+ }
+ cvs.width = pot;
+ }
+
+ if (pimage.height & (pimage.height-1) === 0) {
+ cvs.height = pimage.height;
+ } else {
+ pot = 1;
+ while (pot < pimage.height) {
+ pot *= 2;
+ }
+ cvs.height = pot;
+ }
+
+ cvsTextureCtx.drawImage(pimage.sourceImg, 0, 0, pimage.width, pimage.height, 0, 0, cvs.width, cvs.height);
+
+ curContext.bindTexture(curContext.TEXTURE_2D, texture);
+ curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR_MIPMAP_LINEAR);
+ curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR);
+ curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_T, curContext.CLAMP_TO_EDGE);
+ curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_S, curContext.CLAMP_TO_EDGE);
+ curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, cvs);
+ curContext.generateMipmap(curContext.TEXTURE_2D);
+
+ pimage.__texture = texture;
+ curTexture.width = pimage.width;
+ curTexture.height = pimage.height;
+ }
+
+ usingTexture = true;
+ curContext.useProgram(programObject3D);
+ uniformi("usingTexture3d", programObject3D, "uUsingTexture", usingTexture);
+ };
+
+ /**
+ * Sets the coordinate space for texture mapping. There are two options, IMAGE,
+ * which refers to the actual coordinates of the image, and NORMALIZED, which
+ * refers to a normalized space of values ranging from 0 to 1. The default mode
+ * is IMAGE. In IMAGE, if an image is 100 x 200 pixels, mapping the image onto
+ * the entire size of a quad would require the points (0,0) (0,100) (100,200) (0,200).
+ * The same mapping in NORMAL_SPACE is (0,0) (0,1) (1,1) (0,1).
+ *
+ * @param MODE either IMAGE or NORMALIZED
+ *
+ * @returns none
+ *
+ * @see texture
+ */
+ p.textureMode = function(mode){
+ curTextureMode = mode;
+ };
+ /**
+ * The curveVertexSegment() function handle emitting a specific segment of Catmull-Rom curve. Internal helper function used by <b>curveVertex()</b>.
+ */
+ var curveVertexSegment = function(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) {
+ var x0 = x2;
+ var y0 = y2;
+ var z0 = z2;
+
+ var draw = curveDrawMatrix.array();
+
+ var xplot1 = draw[4] * x1 + draw[5] * x2 + draw[6] * x3 + draw[7] * x4;
+ var xplot2 = draw[8] * x1 + draw[9] * x2 + draw[10] * x3 + draw[11] * x4;
+ var xplot3 = draw[12] * x1 + draw[13] * x2 + draw[14] * x3 + draw[15] * x4;
+
+ var yplot1 = draw[4] * y1 + draw[5] * y2 + draw[6] * y3 + draw[7] * y4;
+ var yplot2 = draw[8] * y1 + draw[9] * y2 + draw[10] * y3 + draw[11] * y4;
+ var yplot3 = draw[12] * y1 + draw[13] * y2 + draw[14] * y3 + draw[15] * y4;
+
+ var zplot1 = draw[4] * z1 + draw[5] * z2 + draw[6] * z3 + draw[7] * z4;
+ var zplot2 = draw[8] * z1 + draw[9] * z2 + draw[10] * z3 + draw[11] * z4;
+ var zplot3 = draw[12] * z1 + draw[13] * z2 + draw[14] * z3 + draw[15] * z4;
+
+ p.vertex(x0, y0, z0);
+ for (var j = 0; j < curveDet; j++) {
+ x0 += xplot1; xplot1 += xplot2; xplot2 += xplot3;
+ y0 += yplot1; yplot1 += yplot2; yplot2 += yplot3;
+ z0 += zplot1; zplot1 += zplot2; zplot2 += zplot3;
+ p.vertex(x0, y0, z0);
+ }
+ };
+
+ /**
+ * Specifies vertex coordinates for curves. This function may only be used between <b>beginShape()</b> and
+ * <b>endShape()</b> and only when there is no MODE parameter specified to <b>beginShape()</b>. The first and last points
+ * in a series of <b>curveVertex()</b> lines will be used to guide the beginning and end of a the curve. A minimum of four
+ * points is required to draw a tiny curve between the second and third points. Adding a fifth point with
+ * <b>curveVertex()</b> will draw the curve between the second, third, and fourth points. The <b>curveVertex()</b> function
+ * is an implementation of Catmull-Rom splines. Using the 3D version of requires rendering with P3D or OPENGL (see the
+ * Environment reference for more information). <br /> <br /><b>NOTE: </b> Fill does not work properly yet.
+ *
+ * @param {float | int} x The x-coordinate of the vertex
+ * @param {float | int} y The y-coordinate of the vertex
+ * @param {float | int} z The z-coordinate of the vertex
+ *
+ * @see curve
+ * @see beginShape
+ * @see endShape
+ * @see vertex
+ * @see bezierVertex
+ */
+ Drawing2D.prototype.curveVertex = function(x, y) {
+ isCurve = true;
+
+ p.vertex(x, y);
+ };
+
+ Drawing3D.prototype.curveVertex = function(x, y, z) {
+ isCurve = true;
+
+ if (!curveInited) {
+ curveInit();
+ }
+ var vert = [];
+ vert[0] = x;
+ vert[1] = y;
+ vert[2] = z;
+ curveVertArray.push(vert);
+ curveVertCount++;
+
+ if (curveVertCount > 3) {
+ curveVertexSegment( curveVertArray[curveVertCount-4][0],
+ curveVertArray[curveVertCount-4][1],
+ curveVertArray[curveVertCount-4][2],
+ curveVertArray[curveVertCount-3][0],
+ curveVertArray[curveVertCount-3][1],
+ curveVertArray[curveVertCount-3][2],
+ curveVertArray[curveVertCount-2][0],
+ curveVertArray[curveVertCount-2][1],
+ curveVertArray[curveVertCount-2][2],
+ curveVertArray[curveVertCount-1][0],
+ curveVertArray[curveVertCount-1][1],
+ curveVertArray[curveVertCount-1][2] );
+ }
+ };
+
+ /**
+ * The curve() function draws a curved line on the screen. The first and second parameters
+ * specify the beginning control point and the last two parameters specify
+ * the ending control point. The middle parameters specify the start and
+ * stop of the curve. Longer curves can be created by putting a series of
+ * <b>curve()</b> functions together or using <b>curveVertex()</b>.
+ * An additional function called <b>curveTightness()</b> provides control
+ * for the visual quality of the curve. The <b>curve()</b> function is an
+ * implementation of Catmull-Rom splines. Using the 3D version of requires
+ * rendering with P3D or OPENGL (see the Environment reference for more
+ * information).
+ *
+ * @param {int|float} x1 coordinates for the beginning control point
+ * @param {int|float} y1 coordinates for the beginning control point
+ * @param {int|float} z1 coordinates for the beginning control point
+ * @param {int|float} x2 coordinates for the first point
+ * @param {int|float} y2 coordinates for the first point
+ * @param {int|float} z2 coordinates for the first point
+ * @param {int|float} x3 coordinates for the second point
+ * @param {int|float} y3 coordinates for the second point
+ * @param {int|float} z3 coordinates for the second point
+ * @param {int|float} x4 coordinates for the ending control point
+ * @param {int|float} y4 coordinates for the ending control point
+ * @param {int|float} z4 coordinates for the ending control point
+ *
+ * @see #curveVertex()
+ * @see #curveTightness()
+ * @see #bezier()
+ */
+ Drawing2D.prototype.curve = function(x1, y1, x2, y2, x3, y3, x4, y4) {
+ p.beginShape();
+ p.curveVertex(x1, y1);
+ p.curveVertex(x2, y2);
+ p.curveVertex(x3, y3);
+ p.curveVertex(x4, y4);
+ p.endShape();
+ };
+
+ Drawing3D.prototype.curve = function(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) {
+ if (z4 !== undef) {
+ p.beginShape();
+ p.curveVertex(x1, y1, z1);
+ p.curveVertex(x2, y2, z2);
+ p.curveVertex(x3, y3, z3);
+ p.curveVertex(x4, y4, z4);
+ p.endShape();
+ return;
+ }
+ p.beginShape();
+ p.curveVertex(x1, y1);
+ p.curveVertex(z1, x2);
+ p.curveVertex(y2, z2);
+ p.curveVertex(x3, y3);
+ p.endShape();
+ };
+
+ /**
+ * The curveTightness() function modifies the quality of forms created with <b>curve()</b> and
+ * <b>curveVertex()</b>. The parameter <b>squishy</b> determines how the
+ * curve fits to the vertex points. The value 0.0 is the default value for
+ * <b>squishy</b> (this value defines the curves to be Catmull-Rom splines)
+ * and the value 1.0 connects all the points with straight lines.
+ * Values within the range -5.0 and 5.0 will deform the curves but
+ * will leave them recognizable and as values increase in magnitude,
+ * they will continue to deform.
+ *
+ * @param {float} tightness amount of deformation from the original vertices
+ *
+ * @see #curve()
+ * @see #curveVertex()
+ *
+ */
+ p.curveTightness = function(tightness) {
+ curTightness = tightness;
+ };
+
+ /**
+ * The curveDetail() function sets the resolution at which curves display. The default value is 20.
+ * This function is only useful when using the P3D or OPENGL renderer.
+ *
+ * @param {int} detail resolution of the curves
+ *
+ * @see curve()
+ * @see curveVertex()
+ * @see curveTightness()
+ */
+ p.curveDetail = function(detail) {
+ curveDet = detail;
+ curveInit();
+ };
+
+ /**
+ * Modifies the location from which rectangles draw. The default mode is rectMode(CORNER), which
+ * specifies the location to be the upper left corner of the shape and uses the third and fourth
+ * parameters of rect() to specify the width and height. The syntax rectMode(CORNERS) uses the
+ * first and second parameters of rect() to set the location of one corner and uses the third and
+ * fourth parameters to set the opposite corner. The syntax rectMode(CENTER) draws the image from
+ * its center point and uses the third and forth parameters of rect() to specify the image's width
+ * and height. The syntax rectMode(RADIUS) draws the image from its center point and uses the third
+ * and forth parameters of rect() to specify half of the image's width and height. The parameter must
+ * be written in ALL CAPS because Processing is a case sensitive language. Note: In version 125, the
+ * mode named CENTER_RADIUS was shortened to RADIUS.
+ *
+ * @param {MODE} MODE Either CORNER, CORNERS, CENTER, or RADIUS
+ *
+ * @see rect
+ */
+ p.rectMode = function(aRectMode) {
+ curRectMode = aRectMode;
+ };
+
+ /**
+ * Modifies the location from which images draw. The default mode is imageMode(CORNER), which specifies
+ * the location to be the upper left corner and uses the fourth and fifth parameters of image() to set
+ * the image's width and height. The syntax imageMode(CORNERS) uses the second and third parameters of
+ * image() to set the location of one corner of the image and uses the fourth and fifth parameters to
+ * set the opposite corner. Use imageMode(CENTER) to draw images centered at the given x and y position.
+ * The parameter to imageMode() must be written in ALL CAPS because Processing is a case sensitive language.
+ *
+ * @param {MODE} MODE Either CORNER, CORNERS, or CENTER
+ *
+ * @see loadImage
+ * @see PImage
+ * @see image
+ * @see background
+ */
+ p.imageMode = function(mode) {
+ switch (mode) {
+ case PConstants.CORNER:
+ imageModeConvert = imageModeCorner;
+ break;
+ case PConstants.CORNERS:
+ imageModeConvert = imageModeCorners;
+ break;
+ case PConstants.CENTER:
+ imageModeConvert = imageModeCenter;
+ break;
+ default:
+ throw "Invalid imageMode";
+ }
+ };
+
+ /**
+ * The origin of the ellipse is modified by the ellipseMode() function. The default configuration is
+ * ellipseMode(CENTER), which specifies the location of the ellipse as the center of the shape. The RADIUS
+ * mode is the same, but the width and height parameters to ellipse() specify the radius of the ellipse,
+ * rather than the diameter. The CORNER mode draws the shape from the upper-left corner of its bounding box.
+ * The CORNERS mode uses the four parameters to ellipse() to set two opposing corners of the ellipse's bounding
+ * box. The parameter must be written in "ALL CAPS" because Processing is a case sensitive language.
+ *
+ * @param {MODE} MODE Either CENTER, RADIUS, CORNER, or CORNERS.
+ *
+ * @see ellipse
+ */
+ p.ellipseMode = function(aEllipseMode) {
+ curEllipseMode = aEllipseMode;
+ };
+
+ /**
+ * The arc() function draws an arc in the display window.
+ * Arcs are drawn along the outer edge of an ellipse defined by the
+ * <b>x</b>, <b>y</b>, <b>width</b> and <b>height</b> parameters.
+ * The origin or the arc's ellipse may be changed with the
+ * <b>ellipseMode()</b> function.
+ * The <b>start</b> and <b>stop</b> parameters specify the angles
+ * at which to draw the arc.
+ *
+ * @param {float} a x-coordinate of the arc's ellipse
+ * @param {float} b y-coordinate of the arc's ellipse
+ * @param {float} c width of the arc's ellipse
+ * @param {float} d height of the arc's ellipse
+ * @param {float} start angle to start the arc, specified in radians
+ * @param {float} stop angle to stop the arc, specified in radians
+ *
+ * @see #ellipseMode()
+ * @see #ellipse()
+ */
+ p.arc = function(x, y, width, height, start, stop) {
+ if (width <= 0 || stop < start) { return; }
+
+ if (curEllipseMode === PConstants.CORNERS) {
+ width = width - x;
+ height = height - y;
+ } else if (curEllipseMode === PConstants.RADIUS) {
+ x = x - width;
+ y = y - height;
+ width = width * 2;
+ height = height * 2;
+ } else if (curEllipseMode === PConstants.CENTER) {
+ x = x - width/2;
+ y = y - height/2;
+ }
+ // make sure that we're starting at a useful point
+ while (start < 0) {
+ start += PConstants.TWO_PI;
+ stop += PConstants.TWO_PI;
+ }
+ if (stop - start > PConstants.TWO_PI) {
+ start = 0;
+ stop = PConstants.TWO_PI;
+ }
+ var hr = width / 2,
+ vr = height / 2,
+ centerX = x + hr,
+ centerY = y + vr,
+ step = 1/(hr+vr);
+
+ var drawSlice = (function(x, y, start, step, stop) {
+ return function(p, closed, i, a, e) {
+ i = 0;
+ a = start;
+ e = stop + step;
+ p.beginShape();
+ if(closed) { p.vertex(x-0.5, y-0.5); }
+ for (; a < e; i++, a = i*step + start) {
+ p.vertex(
+ (x + Math.cos(a) * hr)|0,
+ (y + Math.sin(a) * vr)|0
+ );
+ }
+ p.endShape(closed ? PConstants.CLOSE : undefined);
+ };
+ }(centerX+0.5, centerY+0.5, start, step, stop));
+
+ if (doFill) {
+ var savedStroke = doStroke;
+ doStroke = false;
+ drawSlice(p, true);
+ doStroke = savedStroke;
+ }
+
+ if (doStroke) {
+ var savedFill = doFill;
+ doFill = false;
+ drawSlice(p);
+ doFill = savedFill;
+ }
+ };
+
+ /**
+ * Draws a line (a direct path between two points) to the screen. The version of line() with four parameters
+ * draws the line in 2D. To color a line, use the stroke() function. A line cannot be filled, therefore the
+ * fill() method will not affect the color of a line. 2D lines are drawn with a width of one pixel by default,
+ * but this can be changed with the strokeWeight() function. The version with six parameters allows the line
+ * to be placed anywhere within XYZ space. Drawing this shape in 3D using the z parameter requires the P3D or
+ * OPENGL parameter in combination with size.
+ *
+ * @param {int|float} x1 x-coordinate of the first point
+ * @param {int|float} y1 y-coordinate of the first point
+ * @param {int|float} z1 z-coordinate of the first point
+ * @param {int|float} x2 x-coordinate of the second point
+ * @param {int|float} y2 y-coordinate of the second point
+ * @param {int|float} z2 z-coordinate of the second point
+ *
+ * @see strokeWeight
+ * @see strokeJoin
+ * @see strokeCap
+ * @see beginShape
+ */
+ Drawing2D.prototype.line = function(x1, y1, x2, y2) {
+ if (!doStroke) {
+ return;
+ }
+ if (!renderSmooth) {
+ x1 = Math.round(x1);
+ x2 = Math.round(x2);
+ y1 = Math.round(y1);
+ y2 = Math.round(y2);
+ }
+
+ // A line is only defined if it has different start and end coordinates.
+ // If they are the same, we call point instead.
+ if (x1 === x2 && y1 === y2) {
+ p.point(x1, y1);
+ return;
+ }
+
+ var swap = undef,
+ lineCap = undef,
+ drawCrisp = true,
+ currentModelView = modelView.array(),
+ identityMatrix = [1, 0, 0, 0, 1, 0];
+ // Test if any transformations have been applied to the sketch
+ for (var i = 0; i < 6 && drawCrisp; i++) {
+ drawCrisp = currentModelView[i] === identityMatrix[i];
+ }
+ /* Draw crisp lines if the line is vertical or horizontal with the following method
+ * If any transformations have been applied to the sketch, don't make the line crisp
+ * If the line is directed up or to the left, reverse it by swapping x1/x2 or y1/y2
+ * Make the line 1 pixel longer to work around cross-platform canvas implementations
+ * If the lineWidth is odd, translate the line by 0.5 in the perpendicular direction
+ * Even lineWidths do not need to be translated because the canvas will draw them on pixel boundaries
+ * Change the cap to butt-end to work around cross-platform canvas implementations
+ * Reverse the translate and lineCap canvas state changes after drawing the line
+ */
+ if (drawCrisp) {
+ if (x1 === x2) {
+ if (y1 > y2) {
+ swap = y1;
+ y1 = y2;
+ y2 = swap;
+ }
+ y2++;
+ if (lineWidth % 2 === 1) {
+ curContext.translate(0.5, 0.0);
+ }
+ } else if (y1 === y2) {
+ if (x1 > x2) {
+ swap = x1;
+ x1 = x2;
+ x2 = swap;
+ }
+ x2++;
+ if (lineWidth % 2 === 1) {
+ curContext.translate(0.0, 0.5);
+ }
+ }
+ if (lineWidth === 1) {
+ lineCap = curContext.lineCap;
+ curContext.lineCap = 'butt';
+ }
+ }
+ curContext.beginPath();
+ curContext.moveTo(x1 || 0, y1 || 0);
+ curContext.lineTo(x2 || 0, y2 || 0);
+ executeContextStroke();
+ if (drawCrisp) {
+ if (x1 === x2 && lineWidth % 2 === 1) {
+ curContext.translate(-0.5, 0.0);
+ } else if (y1 === y2 && lineWidth % 2 === 1) {
+ curContext.translate(0.0, -0.5);
+ }
+ if (lineWidth === 1) {
+ curContext.lineCap = lineCap;
+ }
+ }
+ };
+
+ Drawing3D.prototype.line = function(x1, y1, z1, x2, y2, z2) {
+ if (y2 === undef || z2 === undef) { // 2D line called in 3D context
+ z2 = 0;
+ y2 = x2;
+ x2 = z1;
+ z1 = 0;
+ }
+
+ // a line is only defined if it has different start and end coordinates.
+ // If they are the same, we call point instead.
+ if (x1===x2 && y1===y2 && z1===z2) {
+ p.point(x1,y1,z1);
+ return;
+ }
+
+ var lineVerts = [x1, y1, z1, x2, y2, z2];
+
+ var view = new PMatrix3D();
+ view.scale(1, -1, 1);
+ view.apply(modelView.array());
+ view.transpose();
+
+ if (lineWidth > 0 && doStroke) {
+ curContext.useProgram(programObject2D);
+
+ uniformMatrix("uModel2d", programObject2D, "uModel", false, [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]);
+ uniformMatrix("uView2d", programObject2D, "uView", false, view.array());
+
+ uniformf("uColor2d", programObject2D, "uColor", strokeStyle);
+ uniformi("uIsDrawingText", programObject2D, "uIsDrawingText", false);
+
+ vertexAttribPointer("aVertex2d", programObject2D, "aVertex", 3, lineBuffer);
+ disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
+
+ curContext.bufferData(curContext.ARRAY_BUFFER, new Float32Array(lineVerts), curContext.STREAM_DRAW);
+ curContext.drawArrays(curContext.LINES, 0, 2);
+ }
+ };
+
+ /**
+ * Draws a Bezier curve on the screen. These curves are defined by a series of anchor and control points. The first
+ * two parameters specify the first anchor point and the last two parameters specify the other anchor point. The
+ * middle parameters specify the control points which define the shape of the curve. Bezier curves were developed
+ * by French engineer Pierre Bezier. Using the 3D version of requires rendering with P3D or OPENGL (see the
+ * Environment reference for more information).
+ *
+ * @param {int | float} x1,y1,z1 coordinates for the first anchor point
+ * @param {int | float} cx1,cy1,cz1 coordinates for the first control point
+ * @param {int | float} cx2,cy2,cz2 coordinates for the second control point
+ * @param {int | float} x2,y2,z2 coordinates for the second anchor point
+ *
+ * @see bezierVertex
+ * @see curve
+ */
+ Drawing2D.prototype.bezier = function() {
+ if (arguments.length !== 8) {
+ throw("You must use 8 parameters for bezier() in 2D mode");
+ }
+
+ p.beginShape();
+ p.vertex( arguments[0], arguments[1] );
+ p.bezierVertex( arguments[2], arguments[3],
+ arguments[4], arguments[5],
+ arguments[6], arguments[7] );
+ p.endShape();
+ };
+
+ Drawing3D.prototype.bezier = function() {
+ if (arguments.length !== 12) {
+ throw("You must use 12 parameters for bezier() in 3D mode");
+ }
+
+ p.beginShape();
+ p.vertex( arguments[0], arguments[1], arguments[2] );
+ p.bezierVertex( arguments[3], arguments[4], arguments[5],
+ arguments[6], arguments[7], arguments[8],
+ arguments[9], arguments[10], arguments[11] );
+ p.endShape();
+ };
+
+ /**
+ * Sets the resolution at which Beziers display. The default value is 20. This function is only useful when using the P3D
+ * or OPENGL renderer as the default (JAVA2D) renderer does not use this information.
+ *
+ * @param {int} detail resolution of the curves
+ *
+ * @see curve
+ * @see curveVertex
+ * @see curveTightness
+ */
+ p.bezierDetail = function( detail ){
+ bezDetail = detail;
+ };
+
+ /**
+ * The bezierPoint() function evalutes quadratic bezier at point t for points a, b, c, d.
+ * The parameter t varies between 0 and 1. The a and d parameters are the
+ * on-curve points, b and c are the control points. To make a two-dimensional
+ * curve, call this function once with the x coordinates and a second time
+ * with the y coordinates to get the location of a bezier curve at t.
+ *
+ * @param {float} a coordinate of first point on the curve
+ * @param {float} b coordinate of first control point
+ * @param {float} c coordinate of second control point
+ * @param {float} d coordinate of second point on the curve
+ * @param {float} t value between 0 and 1
+ *
+ * @see #bezier()
+ * @see #bezierVertex()
+ * @see #curvePoint()
+ */
+ p.bezierPoint = function(a, b, c, d, t) {
+ return (1 - t) * (1 - t) * (1 - t) * a + 3 * (1 - t) * (1 - t) * t * b + 3 * (1 - t) * t * t * c + t * t * t * d;
+ };
+
+ /**
+ * The bezierTangent() function calculates the tangent of a point on a Bezier curve. There is a good
+ * definition of "tangent" at Wikipedia: <a href="http://en.wikipedia.org/wiki/Tangent" target="new">http://en.wikipedia.org/wiki/Tangent</a>
+ *
+ * @param {float} a coordinate of first point on the curve
+ * @param {float} b coordinate of first control point
+ * @param {float} c coordinate of second control point
+ * @param {float} d coordinate of second point on the curve
+ * @param {float} t value between 0 and 1
+ *
+ * @see #bezier()
+ * @see #bezierVertex()
+ * @see #curvePoint()
+ */
+ p.bezierTangent = function(a, b, c, d, t) {
+ return (3 * t * t * (-a + 3 * b - 3 * c + d) + 6 * t * (a - 2 * b + c) + 3 * (-a + b));
+ };
+
+ /**
+ * The curvePoint() function evalutes the Catmull-Rom curve at point t for points a, b, c, d. The
+ * parameter t varies between 0 and 1, a and d are points on the curve,
+ * and b and c are the control points. This can be done once with the x
+ * coordinates and a second time with the y coordinates to get the
+ * location of a curve at t.
+ *
+ * @param {int|float} a coordinate of first point on the curve
+ * @param {int|float} b coordinate of second point on the curve
+ * @param {int|float} c coordinate of third point on the curve
+ * @param {int|float} d coordinate of fourth point on the curve
+ * @param {float} t value between 0 and 1
+ *
+ * @see #curve()
+ * @see #curveVertex()
+ * @see #bezierPoint()
+ */
+ p.curvePoint = function(a, b, c, d, t) {
+ return 0.5 * ((2 * b) + (-a + c) * t + (2 * a - 5 * b + 4 * c - d) * t * t + (-a + 3 * b - 3 * c + d) * t * t * t);
+ };
+
+ /**
+ * The curveTangent() function calculates the tangent of a point on a Catmull-Rom curve.
+ * There is a good definition of "tangent" at Wikipedia: <a href="http://en.wikipedia.org/wiki/Tangent" target="new">http://en.wikipedia.org/wiki/Tangent</a>.
+ *
+ * @param {int|float} a coordinate of first point on the curve
+ * @param {int|float} b coordinate of first control point
+ * @param {int|float} c coordinate of second control point
+ * @param {int|float} d coordinate of second point on the curve
+ * @param {float} t value between 0 and 1
+ *
+ * @see #curve()
+ * @see #curveVertex()
+ * @see #curvePoint()
+ * @see #bezierTangent()
+ */
+ p.curveTangent = function(a, b, c, d, t) {
+ return 0.5 * ((-a + c) + 2 * (2 * a - 5 * b + 4 * c - d) * t + 3 * (-a + 3 * b - 3 * c + d) * t * t);
+ };
+
+ /**
+ * A triangle is a plane created by connecting three points. The first two arguments specify the first point,
+ * the middle two arguments specify the second point, and the last two arguments specify the third point.
+ *
+ * @param {int | float} x1 x-coordinate of the first point
+ * @param {int | float} y1 y-coordinate of the first point
+ * @param {int | float} x2 x-coordinate of the second point
+ * @param {int | float} y2 y-coordinate of the second point
+ * @param {int | float} x3 x-coordinate of the third point
+ * @param {int | float} y3 y-coordinate of the third point
+ */
+ p.triangle = function(x1, y1, x2, y2, x3, y3) {
+ p.beginShape(PConstants.TRIANGLES);
+ p.vertex(x1, y1, 0);
+ p.vertex(x2, y2, 0);
+ p.vertex(x3, y3, 0);
+ p.endShape();
+ };
+
+ /**
+ * A quad is a quadrilateral, a four sided polygon. It is similar to a rectangle, but the angles between its
+ * edges are not constrained to ninety degrees. The first pair of parameters (x1,y1) sets the first vertex
+ * and the subsequent pairs should proceed clockwise or counter-clockwise around the defined shape.
+ *
+ * @param {float | int} x1 x-coordinate of the first corner
+ * @param {float | int} y1 y-coordinate of the first corner
+ * @param {float | int} x2 x-coordinate of the second corner
+ * @param {float | int} y2 y-coordinate of the second corner
+ * @param {float | int} x3 x-coordinate of the third corner
+ * @param {float | int} y3 y-coordinate of the third corner
+ * @param {float | int} x4 x-coordinate of the fourth corner
+ * @param {float | int} y4 y-coordinate of the fourth corner
+ */
+ p.quad = function(x1, y1, x2, y2, x3, y3, x4, y4) {
+ p.beginShape(PConstants.QUADS);
+ p.vertex(x1, y1, 0);
+ p.vertex(x2, y2, 0);
+ p.vertex(x3, y3, 0);
+ p.vertex(x4, y4, 0);
+ p.endShape();
+ };
+
+ var roundedRect$2d = function(x, y, width, height, tl, tr, br, bl) {
+ if (bl === undef) {
+ tr = tl;
+ br = tl;
+ bl = tl;
+ }
+ var halfWidth = width / 2,
+ halfHeight = height / 2;
+ if (tl > halfWidth || tl > halfHeight) {
+ tl = Math.min(halfWidth, halfHeight);
+ }
+ if (tr > halfWidth || tr > halfHeight) {
+ tr = Math.min(halfWidth, halfHeight);
+ }
+ if (br > halfWidth || br > halfHeight) {
+ br = Math.min(halfWidth, halfHeight);
+ }
+ if (bl > halfWidth || bl > halfHeight) {
+ bl = Math.min(halfWidth, halfHeight);
+ }
+ // Translate the stroke by (0.5, 0.5) to draw a crisp border
+ if (!doFill || doStroke) {
+ curContext.translate(0.5, 0.5);
+ }
+ curContext.beginPath();
+ curContext.moveTo(x + tl, y);
+ curContext.lineTo(x + width - tr, y);
+ curContext.quadraticCurveTo(x + width, y, x + width, y + tr);
+ curContext.lineTo(x + width, y + height - br);
+ curContext.quadraticCurveTo(x + width, y + height, x + width - br, y + height);
+ curContext.lineTo(x + bl, y + height);
+ curContext.quadraticCurveTo(x, y + height, x, y + height - bl);
+ curContext.lineTo(x, y + tl);
+ curContext.quadraticCurveTo(x, y, x + tl, y);
+ if (!doFill || doStroke) {
+ curContext.translate(-0.5, -0.5);
+ }
+ executeContextFill();
+ executeContextStroke();
+ };
+
+ /**
+ * Draws a rectangle to the screen. A rectangle is a four-sided shape with every angle at ninety
+ * degrees. The first two parameters set the location, the third sets the width, and the fourth
+ * sets the height. The origin is changed with the rectMode() function.
+ *
+ * @param {int|float} x x-coordinate of the rectangle
+ * @param {int|float} y y-coordinate of the rectangle
+ * @param {int|float} width width of the rectangle
+ * @param {int|float} height height of the rectangle
+ *
+ * @see rectMode
+ * @see quad
+ */
+ Drawing2D.prototype.rect = function(x, y, width, height, tl, tr, br, bl) {
+ if (!width && !height) {
+ return;
+ }
+
+ if (curRectMode === PConstants.CORNERS) {
+ width -= x;
+ height -= y;
+ } else if (curRectMode === PConstants.RADIUS) {
+ width *= 2;
+ height *= 2;
+ x -= width / 2;
+ y -= height / 2;
+ } else if (curRectMode === PConstants.CENTER) {
+ x -= width / 2;
+ y -= height / 2;
+ }
+
+ if (!renderSmooth) {
+ x = Math.round(x);
+ y = Math.round(y);
+ width = Math.round(width);
+ height = Math.round(height);
+ }
+ if (tl !== undef) {
+ roundedRect$2d(x, y, width, height, tl, tr, br, bl);
+ return;
+ }
+
+ // Translate the line by (0.5, 0.5) to draw a crisp rectangle border
+ if (doStroke && lineWidth % 2 === 1) {
+ curContext.translate(0.5, 0.5);
+ }
+ curContext.beginPath();
+ curContext.rect(x, y, width, height);
+ executeContextFill();
+ executeContextStroke();
+ if (doStroke && lineWidth % 2 === 1) {
+ curContext.translate(-0.5, -0.5);
+ }
+ };
+
+ Drawing3D.prototype.rect = function(x, y, width, height, tl, tr, br, bl) {
+ if (tl !== undef) {
+ throw "rect() with rounded corners is not supported in 3D mode";
+ }
+
+ if (curRectMode === PConstants.CORNERS) {
+ width -= x;
+ height -= y;
+ } else if (curRectMode === PConstants.RADIUS) {
+ width *= 2;
+ height *= 2;
+ x -= width / 2;
+ y -= height / 2;
+ } else if (curRectMode === PConstants.CENTER) {
+ x -= width / 2;
+ y -= height / 2;
+ }
+
+ // Modeling transformation
+ var model = new PMatrix3D();
+ model.translate(x, y, 0);
+ model.scale(width, height, 1);
+ model.transpose();
+
+ // viewing transformation needs to have Y flipped
+ // becuase that's what Processing does.
+ var view = new PMatrix3D();
+ view.scale(1, -1, 1);
+ view.apply(modelView.array());
+ view.transpose();
+
+ if (lineWidth > 0 && doStroke) {
+ curContext.useProgram(programObject2D);
+ uniformMatrix("uModel2d", programObject2D, "uModel", false, model.array());
+ uniformMatrix("uView2d", programObject2D, "uView", false, view.array());
+ uniformf("uColor2d", programObject2D, "uColor", strokeStyle);
+ uniformi("uIsDrawingText2d", programObject2D, "uIsDrawingText", false);
+ vertexAttribPointer("aVertex2d", programObject2D, "aVertex", 3, rectBuffer);
+ disableVertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord");
+ curContext.drawArrays(curContext.LINE_LOOP, 0, rectVerts.length / 3);
+ }
+
+ if (doFill) {
+ curContext.useProgram(programObject3D);
+ uniformMatrix("uModel3d", programObject3D, "uModel", false, model.array());
+ uniformMatrix("uView3d", programObject3D, "uView", false, view.array());
+
+ // fix stitching problems. (lines get occluded by triangles
+ // since they share the same depth values). This is not entirely
+ // working, but it's a start for drawing the outline. So
+ // developers can start playing around with styles.
+ curContext.enable(curContext.POLYGON_OFFSET_FILL);
+ curContext.polygonOffset(1, 1);
+
+ uniformf("color3d", programObject3D, "uColor", fillStyle);
+
+ if(lightCount > 0){
+ var v = new PMatrix3D();
+ v.set(view);
+
+ var m = new PMatrix3D();
+ m.set(model);
+
+ v.mult(m);
+
+ var normalMatrix = new PMatrix3D();
+ normalMatrix.set(v);
+ normalMatrix.invert();
+ normalMatrix.transpose();
+
+ uniformMatrix("uNormalTransform3d", programObject3D, "uNormalTransform", false, normalMatrix.array());
+ vertexAttribPointer("aNormal3d", programObject3D, "aNormal", 3, rectNormBuffer);
+ }
+ else{
+ disableVertexAttribPointer("normal3d", programObject3D, "aNormal");
+ }
+
+ vertexAttribPointer("vertex3d", programObject3D, "aVertex", 3, rectBuffer);
+
+ curContext.drawArrays(curContext.TRIANGLE_FAN, 0, rectVerts.length / 3);
+ curContext.disable(curContext.POLYGON_OFFSET_FILL);
+ }
+ };
+
+ /**
+ * Draws an ellipse (oval) in the display window. An ellipse with an equal <b>width</b> and <b>height</b> is a circle.
+ * The first two parameters set the location, the third sets the width, and the fourth sets the height. The origin may be
+ * changed with the <b>ellipseMode()</b> function.
+ *
+ * @param {float|int} x x-coordinate of the ellipse
+ * @param {float|int} y y-coordinate of the ellipse
+ * @param {float|int} width width of the ellipse
+ * @param {float|int} height height of the ellipse
+ *
+ * @see ellipseMode
+ */
+ Drawing2D.prototype.ellipse = function(x, y, width, height) {
+ x = x || 0;
+ y = y || 0;
+
+ if (width <= 0 && height <= 0) {
+ return;
+ }
+
+ if (curEllipseMode === PConstants.RADIUS) {
+ width *= 2;
+ height *= 2;
+ } else if (curEllipseMode === PConstants.CORNERS) {
+ width = width - x;
+ height = height - y;
+ x += width / 2;
+ y += height / 2;
+ } else if (curEllipseMode === PConstants.CORNER) {
+ x += width / 2;
+ y += height / 2;
+ }
+
+ // Shortcut for drawing a 2D circle
+ if (width === height) {
+ curContext.beginPath();
+ curContext.arc(x, y, width / 2, 0, PConstants.TWO_PI, false);
+ executeContextFill();
+ executeContextStroke();
+ } else {
+ var w = width / 2,
+ h = height / 2,
+ C = 0.5522847498307933,
+ c_x = C * w,
+ c_y = C * h;
+
+ p.beginShape();
+ p.vertex(x + w, y);
+ p.bezierVertex(x + w, y - c_y, x + c_x, y - h, x, y - h);
+ p.bezierVertex(x - c_x, y - h, x - w, y - c_y, x - w, y);
+ p.bezierVertex(x - w, y + c_y, x - c_x, y + h, x, y + h);
+ p.bezierVertex(x + c_x, y + h, x + w, y + c_y, x + w, y);
+ p.endShape();
+ }
+ };
+
+ Drawing3D.prototype.ellipse = function(x, y, width, height) {
+ x = x || 0;
+ y = y || 0;
+
+ if (width <= 0 && height <= 0) {
+ return;
+ }
+
+ if (curEllipseMode === PConstants.RADIUS) {
+ width *= 2;
+ height *= 2;
+ } else if (curEllipseMode === PConstants.CORNERS) {
+ width = width - x;
+ height = height - y;
+ x += width / 2;
+ y += height / 2;
+ } else if (curEllipseMode === PConstants.CORNER) {
+ x += width / 2;
+ y += height / 2;
+ }
+
+ var w = width / 2,
+ h = height / 2,
+ C = 0.5522847498307933,
+ c_x = C * w,
+ c_y = C * h;
+
+ p.beginShape();
+ p.vertex(x + w, y);
+ p.bezierVertex(x + w, y - c_y, 0, x + c_x, y - h, 0, x, y - h, 0);
+ p.bezierVertex(x - c_x, y - h, 0, x - w, y - c_y, 0, x - w, y, 0);
+ p.bezierVertex(x - w, y + c_y, 0, x - c_x, y + h, 0, x, y + h, 0);
+ p.bezierVertex(x + c_x, y + h, 0, x + w, y + c_y, 0, x + w, y, 0);
+ p.endShape();
+
+ if (doFill) {
+ //temporary workaround to not working fills for bezier -- will fix later
+ var xAv = 0, yAv = 0, i, j;
+ for (i = 0; i < vertArray.length; i++) {
+ xAv += vertArray[i][0];
+ yAv += vertArray[i][1];
+ }
+ xAv /= vertArray.length;
+ yAv /= vertArray.length;
+ var vert = [],
+ fillVertArray = [],
+ colorVertArray = [];
+ vert[0] = xAv;
+ vert[1] = yAv;
+ vert[2] = 0;
+ vert[3] = 0;
+ vert[4] = 0;
+ vert[5] = fillStyle[0];
+ vert[6] = fillStyle[1];
+ vert[7] = fillStyle[2];
+ vert[8] = fillStyle[3];
+ vert[9] = strokeStyle[0];
+ vert[10] = strokeStyle[1];
+ vert[11] = strokeStyle[2];
+ vert[12] = strokeStyle[3];
+ vert[13] = normalX;
+ vert[14] = normalY;
+ vert[15] = normalZ;
+ vertArray.unshift(vert);
+ for (i = 0; i < vertArray.length; i++) {
+ for (j = 0; j < 3; j++) {
+ fillVertArray.push(vertArray[i][j]);
+ }
+ for (j = 5; j < 9; j++) {
+ colorVertArray.push(vertArray[i][j]);
+ }
+ }
+ fill3D(fillVertArray, "TRIANGLE_FAN", colorVertArray);
+ }
+ };
+
+ /**
+ * Sets the current normal vector. This is for drawing three dimensional shapes and surfaces and
+ * specifies a vector perpendicular to the surface of the shape which determines how lighting affects
+ * it. Processing attempts to automatically assign normals to shapes, but since that's imperfect,
+ * this is a better option when you want more control. This function is identical to glNormal3f() in OpenGL.
+ *
+ * @param {float} nx x direction
+ * @param {float} ny y direction
+ * @param {float} nz z direction
+ *
+ * @see beginShape
+ * @see endShape
+ * @see lights
+ */
+ p.normal = function(nx, ny, nz) {
+ if (arguments.length !== 3 || !(typeof nx === "number" && typeof ny === "number" && typeof nz === "number")) {
+ throw "normal() requires three numeric arguments.";
+ }
+
+ normalX = nx;
+ normalY = ny;
+ normalZ = nz;
+
+ if (curShape !== 0) {
+ if (normalMode === PConstants.NORMAL_MODE_AUTO) {
+ normalMode = PConstants.NORMAL_MODE_SHAPE;
+ } else if (normalMode === PConstants.NORMAL_MODE_SHAPE) {
+ normalMode = PConstants.NORMAL_MODE_VERTEX;
+ }
+ }
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Raster drawing functions
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Saves an image from the display window. Images are saved in TIFF, TARGA, JPEG, and PNG format
+ * depending on the extension within the filename parameter. For example, "image.tif" will have
+ * a TIFF image and "image.png" will save a PNG image. If no extension is included in the filename,
+ * the image will save in TIFF format and .tif will be added to the name. These files are saved to
+ * the sketch's folder, which may be opened by selecting "Show sketch folder" from the "Sketch" menu.
+ * It is not possible to use save() while running the program in a web browser. All images saved
+ * from the main drawing window will be opaque. To save images without a background, use createGraphics().
+ *
+ * @param {String} filename any sequence of letters and numbers
+ *
+ * @see saveFrame
+ * @see createGraphics
+ */
+ p.save = function(file, img) {
+ // file is unused at the moment
+ // may implement this differently in later release
+ if (img !== undef) {
+ return window.open(img.toDataURL(),"_blank");
+ }
+ return window.open(p.externals.canvas.toDataURL(),"_blank");
+ };
+
+ var saveNumber = 0;
+
+ p.saveFrame = function(file) {
+ if(file === undef) {
+ // use default name template if parameter is not specified
+ file = "screen-####.png";
+ }
+ // Increment changeable part: screen-0000.png, screen-0001.png, ...
+ var frameFilename = file.replace(/#+/, function(all) {
+ var s = "" + (saveNumber++);
+ while(s.length < all.length) {
+ s = "0" + s;
+ }
+ return s;
+ });
+ p.save(frameFilename);
+ };
+
+ var utilityContext2d = document.createElement("canvas").getContext("2d");
+
+ var canvasDataCache = [undef, undef, undef]; // we need three for now
+
+ function getCanvasData(obj, w, h) {
+ var canvasData = canvasDataCache.shift();
+
+ if (canvasData === undef) {
+ canvasData = {};
+ canvasData.canvas = document.createElement("canvas");
+ canvasData.context = canvasData.canvas.getContext('2d');
+ }
+
+ canvasDataCache.push(canvasData);
+
+ var canvas = canvasData.canvas, context = canvasData.context,
+ width = w || obj.width, height = h || obj.height;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ if (!obj) {
+ context.clearRect(0, 0, width, height);
+ } else if ("data" in obj) { // ImageData
+ context.putImageData(obj, 0, 0);
+ } else {
+ context.clearRect(0, 0, width, height);
+ context.drawImage(obj, 0, 0, width, height);
+ }
+ return canvasData;
+ }
+
+ /**
+ * Handle the sketch code for pixels[] and pixels.length
+ * parser code converts pixels[] to getPixels()
+ * or setPixels(), .length becomes getLength()
+ */
+ function buildPixelsObject(pImage) {
+ return {
+
+ getLength: (function(aImg) {
+ return function() {
+ if (aImg.isRemote) {
+ throw "Image is loaded remotely. Cannot get length.";
+ } else {
+ return aImg.imageData.data.length ? aImg.imageData.data.length/4 : 0;
+ }
+ };
+ }(pImage)),
+
+ getPixel: (function(aImg) {
+ return function(i) {
+ var offset = i*4,
+ data = aImg.imageData.data;
+
+ if (aImg.isRemote) {
+ throw "Image is loaded remotely. Cannot get pixels.";
+ }
+
+ return (data[offset+3] << 24) & PConstants.ALPHA_MASK |
+ (data[offset] << 16) & PConstants.RED_MASK |
+ (data[offset+1] << 8) & PConstants.GREEN_MASK |
+ data[offset+2] & PConstants.BLUE_MASK;
+ };
+ }(pImage)),
+
+ setPixel: (function(aImg) {
+ return function(i, c) {
+ var offset = i*4,
+ data = aImg.imageData.data;
+
+ if (aImg.isRemote) {
+ throw "Image is loaded remotely. Cannot set pixel.";
+ }
+
+ data[offset+0] = (c & PConstants.RED_MASK) >>> 16;
+ data[offset+1] = (c & PConstants.GREEN_MASK) >>> 8;
+ data[offset+2] = (c & PConstants.BLUE_MASK);
+ data[offset+3] = (c & PConstants.ALPHA_MASK) >>> 24;
+ aImg.__isDirty = true;
+ };
+ }(pImage)),
+
+ toArray: (function(aImg) {
+ return function() {
+ var arr = [],
+ data = aImg.imageData.data,
+ length = aImg.width * aImg.height;
+
+ if (aImg.isRemote) {
+ throw "Image is loaded remotely. Cannot get pixels.";
+ }
+
+ for (var i = 0, offset = 0; i < length; i++, offset += 4) {
+ arr.push( (data[offset+3] << 24) & PConstants.ALPHA_MASK |
+ (data[offset] << 16) & PConstants.RED_MASK |
+ (data[offset+1] << 8) & PConstants.GREEN_MASK |
+ data[offset+2] & PConstants.BLUE_MASK );
+ }
+ return arr;
+ };
+ }(pImage)),
+
+ set: (function(aImg) {
+ return function(arr) {
+ var offset,
+ data,
+ c;
+ if (this.isRemote) {
+ throw "Image is loaded remotely. Cannot set pixels.";
+ }
+
+ data = aImg.imageData.data;
+ for (var i = 0, aL = arr.length; i < aL; i++) {
+ c = arr[i];
+ offset = i*4;
+
+ data[offset+0] = (c & PConstants.RED_MASK) >>> 16;
+ data[offset+1] = (c & PConstants.GREEN_MASK) >>> 8;
+ data[offset+2] = (c & PConstants.BLUE_MASK);
+ data[offset+3] = (c & PConstants.ALPHA_MASK) >>> 24;
+ }
+ aImg.__isDirty = true;
+ };
+ }(pImage))
+
+ };
+ }
+
+ /**
+ * Datatype for storing images. Processing can display .gif, .jpg, .tga, and .png images. Images may be
+ * displayed in 2D and 3D space. Before an image is used, it must be loaded with the loadImage() function.
+ * The PImage object contains fields for the width and height of the image, as well as an array called
+ * pixels[] which contains the values for every pixel in the image. A group of methods, described below,
+ * allow easy access to the image's pixels and alpha channel and simplify the process of compositing.
+ * Before using the pixels[] array, be sure to use the loadPixels() method on the image to make sure that the
+ * pixel data is properly loaded. To create a new image, use the createImage() function (do not use new PImage()).
+ *
+ * @param {int} width image width
+ * @param {int} height image height
+ * @param {MODE} format Either RGB, ARGB, ALPHA (grayscale alpha channel)
+ *
+ * @returns {PImage}
+ *
+ * @see loadImage
+ * @see imageMode
+ * @see createImage
+ */
+ var PImage = function(aWidth, aHeight, aFormat) {
+
+ // Keep track of whether or not the cached imageData has been touched.
+ this.__isDirty = false;
+
+ if (aWidth instanceof HTMLImageElement) {
+ // convert an <img> to a PImage
+ this.fromHTMLImageData(aWidth);
+ } else if (aHeight || aFormat) {
+ this.width = aWidth || 1;
+ this.height = aHeight || 1;
+
+ // Stuff a canvas into sourceImg so image() calls can use drawImage like an <img>
+ var canvas = this.sourceImg = document.createElement("canvas");
+ canvas.width = this.width;
+ canvas.height = this.height;
+
+ var imageData = this.imageData = canvas.getContext('2d').createImageData(this.width, this.height);
+ this.format = (aFormat === PConstants.ARGB || aFormat === PConstants.ALPHA) ? aFormat : PConstants.RGB;
+ if (this.format === PConstants.RGB) {
+ // Set the alpha channel of an RGB image to opaque.
+ for (var i = 3, data = this.imageData.data, len = data.length; i < len; i += 4) {
+ data[i] = 255;
+ }
+ }
+
+ this.__isDirty = true;
+ this.updatePixels();
+ } else {
+ this.width = 0;
+ this.height = 0;
+ this.imageData = utilityContext2d.createImageData(1, 1);
+ this.format = PConstants.ARGB;
+ }
+
+ this.pixels = buildPixelsObject(this);
+ };
+ PImage.prototype = {
+
+ /**
+ * Temporary hack to deal with cross-Processing-instance created PImage. See
+ * tickets #1623 and #1644.
+ */
+ __isPImage: true,
+
+ /**
+ * @member PImage
+ * Updates the image with the data in its pixels[] array. Use in conjunction with loadPixels(). If
+ * you're only reading pixels from the array, there's no need to call updatePixels().
+ * Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the rule
+ * is that any time you want to manipulate the pixels[] array, you must first call loadPixels(), and
+ * after changes have been made, call updatePixels(). Even if the renderer may not seem to use this
+ * function in the current Processing release, this will always be subject to change.
+ * Currently, none of the renderers use the additional parameters to updatePixels().
+ */
+ updatePixels: function() {
+ var canvas = this.sourceImg;
+ if (canvas && canvas instanceof HTMLCanvasElement && this.__isDirty) {
+ canvas.getContext('2d').putImageData(this.imageData, 0, 0);
+ }
+ this.__isDirty = false;
+ },
+
+ fromHTMLImageData: function(htmlImg) {
+ // convert an <img> to a PImage
+ var canvasData = getCanvasData(htmlImg);
+ try {
+ var imageData = canvasData.context.getImageData(0, 0, htmlImg.width, htmlImg.height);
+ this.fromImageData(imageData);
+ } catch(e) {
+ if (htmlImg.width && htmlImg.height) {
+ this.isRemote = true;
+ this.width = htmlImg.width;
+ this.height = htmlImg.height;
+ }
+ }
+ this.sourceImg = htmlImg;
+ },
+
+ 'get': function(x, y, w, h) {
+ if (!arguments.length) {
+ return p.get(this);
+ }
+ if (arguments.length === 2) {
+ return p.get(x, y, this);
+ }
+ if (arguments.length === 4) {
+ return p.get(x, y, w, h, this);
+ }
+ },
+
+ /**
+ * @member PImage
+ * Changes the color of any pixel or writes an image directly into the image. The x and y parameter
+ * specify the pixel or the upper-left corner of the image. The color parameter specifies the color value.
+ * Setting the color of a single pixel with set(x, y) is easy, but not as fast as putting the data
+ * directly into pixels[]. The equivalent statement to "set(x, y, #000000)" using pixels[] is
+ * "pixels[y*width+x] = #000000". Processing requires calling loadPixels() to load the display window
+ * data into the pixels[] array before getting the values and calling updatePixels() to update the window.
+ *
+ * @param {int} x x-coordinate of the pixel or upper-left corner of the image
+ * @param {int} y y-coordinate of the pixel or upper-left corner of the image
+ * @param {color} color any value of the color datatype
+ *
+ * @see get
+ * @see pixels[]
+ * @see copy
+ */
+ 'set': function(x, y, c) {
+ p.set(x, y, c, this);
+ this.__isDirty = true;
+ },
+
+ /**
+ * @member PImage
+ * Blends a region of pixels into the image specified by the img parameter. These copies utilize full
+ * alpha channel support and a choice of the following modes to blend the colors of source pixels (A)
+ * with the ones of pixels in the destination image (B):
+ * BLEND - linear interpolation of colours: C = A*factor + B
+ * ADD - additive blending with white clip: C = min(A*factor + B, 255)
+ * SUBTRACT - subtractive blending with black clip: C = max(B - A*factor, 0)
+ * DARKEST - only the darkest colour succeeds: C = min(A*factor, B)
+ * LIGHTEST - only the lightest colour succeeds: C = max(A*factor, B)
+ * DIFFERENCE - subtract colors from underlying image.
+ * EXCLUSION - similar to DIFFERENCE, but less extreme.
+ * MULTIPLY - Multiply the colors, result will always be darker.
+ * SCREEN - Opposite multiply, uses inverse values of the colors.
+ * OVERLAY - A mix of MULTIPLY and SCREEN. Multiplies dark values, and screens light values.
+ * HARD_LIGHT - SCREEN when greater than 50% gray, MULTIPLY when lower.
+ * SOFT_LIGHT - Mix of DARKEST and LIGHTEST. Works like OVERLAY, but not as harsh.
+ * DODGE - Lightens light tones and increases contrast, ignores darks. Called "Color Dodge" in Illustrator and Photoshop.
+ * BURN - Darker areas are applied, increasing contrast, ignores lights. Called "Color Burn" in Illustrator and Photoshop.
+ * All modes use the alpha information (highest byte) of source image pixels as the blending factor.
+ * If the source and destination regions are different sizes, the image will be automatically resized to
+ * match the destination size. If the srcImg parameter is not used, the display window is used as the source image.
+ * This function ignores imageMode().
+ *
+ * @param {int} x X coordinate of the source's upper left corner
+ * @param {int} y Y coordinate of the source's upper left corner
+ * @param {int} width source image width
+ * @param {int} height source image height
+ * @param {int} dx X coordinate of the destinations's upper left corner
+ * @param {int} dy Y coordinate of the destinations's upper left corner
+ * @param {int} dwidth destination image width
+ * @param {int} dheight destination image height
+ * @param {PImage} srcImg an image variable referring to the source image
+ * @param {MODE} MODE Either BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, DIFFERENCE, EXCLUSION,
+ * MULTIPLY, SCREEN, OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, BURN
+ *
+ * @see alpha
+ * @see copy
+ */
+ blend: function(srcImg, x, y, width, height, dx, dy, dwidth, dheight, MODE) {
+ if (arguments.length === 9) {
+ p.blend(this, srcImg, x, y, width, height, dx, dy, dwidth, dheight, this);
+ } else if (arguments.length === 10) {
+ p.blend(srcImg, x, y, width, height, dx, dy, dwidth, dheight, MODE, this);
+ }
+ delete this.sourceImg;
+ },
+
+ /**
+ * @member PImage
+ * Copies a region of pixels from one image into another. If the source and destination regions
+ * aren't the same size, it will automatically resize source pixels to fit the specified target region.
+ * No alpha information is used in the process, however if the source image has an alpha channel set,
+ * it will be copied as well. This function ignores imageMode().
+ *
+ * @param {int} sx X coordinate of the source's upper left corner
+ * @param {int} sy Y coordinate of the source's upper left corner
+ * @param {int} swidth source image width
+ * @param {int} sheight source image height
+ * @param {int} dx X coordinate of the destinations's upper left corner
+ * @param {int} dy Y coordinate of the destinations's upper left corner
+ * @param {int} dwidth destination image width
+ * @param {int} dheight destination image height
+ * @param {PImage} srcImg an image variable referring to the source image
+ *
+ * @see alpha
+ * @see blend
+ */
+ copy: function(srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, dheight) {
+ if (arguments.length === 8) {
+ p.blend(this, srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, PConstants.REPLACE, this);
+ } else if (arguments.length === 9) {
+ p.blend(srcImg, sx, sy, swidth, sheight, dx, dy, dwidth, dheight, PConstants.REPLACE, this);
+ }
+ delete this.sourceImg;
+ },
+
+ /**
+ * @member PImage
+ * Filters an image as defined by one of the following modes:
+ * THRESHOLD - converts the image to black and white pixels depending if they are above or below
+ * the threshold defined by the level parameter. The level must be between 0.0 (black) and 1.0(white).
+ * If no level is specified, 0.5 is used.
+ * GRAY - converts any colors in the image to grayscale equivalents
+ * INVERT - sets each pixel to its inverse value
+ * POSTERIZE - limits each channel of the image to the number of colors specified as the level parameter
+ * BLUR - executes a Guassian blur with the level parameter specifying the extent of the blurring.
+ * If no level parameter is used, the blur is equivalent to Guassian blur of radius 1.
+ * OPAQUE - sets the alpha channel to entirely opaque.
+ * ERODE - reduces the light areas with the amount defined by the level parameter.
+ * DILATE - increases the light areas with the amount defined by the level parameter
+ *
+ * @param {MODE} MODE Either THRESHOLD, GRAY, INVERT, POSTERIZE, BLUR, OPAQUE, ERODE, or DILATE
+ * @param {int|float} param in the range from 0 to 1
+ */
+ filter: function(mode, param) {
+ if (arguments.length === 2) {
+ p.filter(mode, param, this);
+ } else if (arguments.length === 1) {
+ // no param specified, send null to show its invalid
+ p.filter(mode, null, this);
+ }
+ delete this.sourceImg;
+ },
+
+ /**
+ * @member PImage
+ * Saves the image into a file. Images are saved in TIFF, TARGA, JPEG, and PNG format depending on
+ * the extension within the filename parameter. For example, "image.tif" will have a TIFF image and
+ * "image.png" will save a PNG image. If no extension is included in the filename, the image will save
+ * in TIFF format and .tif will be added to the name. These files are saved to the sketch's folder,
+ * which may be opened by selecting "Show sketch folder" from the "Sketch" menu. It is not possible to
+ * use save() while running the program in a web browser.
+ * To save an image created within the code, rather than through loading, it's necessary to make the
+ * image with the createImage() function so it is aware of the location of the program and can therefore
+ * save the file to the right place. See the createImage() reference for more information.
+ *
+ * @param {String} filename a sequence of letters and numbers
+ */
+ save: function(file){
+ p.save(file,this);
+ },
+
+ /**
+ * @member PImage
+ * Resize the image to a new width and height. To make the image scale proportionally, use 0 as the
+ * value for the wide or high parameter.
+ *
+ * @param {int} wide the resized image width
+ * @param {int} high the resized image height
+ *
+ * @see get
+ */
+ resize: function(w, h) {
+ if (this.isRemote) { // Remote images cannot access imageData
+ throw "Image is loaded remotely. Cannot resize.";
+ }
+ if (this.width !== 0 || this.height !== 0) {
+ // make aspect ratio if w or h is 0
+ if (w === 0 && h !== 0) {
+ w = Math.floor(this.width / this.height * h);
+ } else if (h === 0 && w !== 0) {
+ h = Math.floor(this.height / this.width * w);
+ }
+ // put 'this.imageData' into a new canvas
+ var canvas = getCanvasData(this.imageData).canvas;
+ // pull imageData object out of canvas into ImageData object
+ var imageData = getCanvasData(canvas, w, h).context.getImageData(0, 0, w, h);
+ // set this as new pimage
+ this.fromImageData(imageData);
+ }
+ },
+
+ /**
+ * @member PImage
+ * Masks part of an image from displaying by loading another image and using it as an alpha channel.
+ * This mask image should only contain grayscale data, but only the blue color channel is used. The
+ * mask image needs to be the same size as the image to which it is applied.
+ * In addition to using a mask image, an integer array containing the alpha channel data can be
+ * specified directly. This method is useful for creating dynamically generated alpha masks. This
+ * array must be of the same length as the target image's pixels array and should contain only grayscale
+ * data of values between 0-255.
+ *
+ * @param {PImage} maskImg any PImage object used as the alpha channel for "img", needs to be same
+ * size as "img"
+ * @param {int[]} maskArray any array of Integer numbers used as the alpha channel, needs to be same
+ * length as the image's pixel array
+ */
+ mask: function(mask) {
+ var obj = this.toImageData(),
+ i,
+ size;
+
+ if (mask instanceof PImage || mask.__isPImage) {
+ if (mask.width === this.width && mask.height === this.height) {
+ mask = mask.toImageData();
+
+ for (i = 2, size = this.width * this.height * 4; i < size; i += 4) {
+ // using it as an alpha channel
+ obj.data[i + 1] = mask.data[i];
+ // but only the blue color channel
+ }
+ } else {
+ throw "mask must have the same dimensions as PImage.";
+ }
+ } else if (mask instanceof Array) {
+ if (this.width * this.height === mask.length) {
+ for (i = 0, size = mask.length; i < size; ++i) {
+ obj.data[i * 4 + 3] = mask[i];
+ }
+ } else {
+ throw "mask array must be the same length as PImage pixels array.";
+ }
+ }
+
+ this.fromImageData(obj);
+ },
+
+ // These are intentionally left blank for PImages, we work live with pixels and draw as necessary
+ /**
+ * @member PImage
+ * Loads the pixel data for the image into its pixels[] array. This function must always be called
+ * before reading from or writing to pixels[].
+ * Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the
+ * rule is that any time you want to manipulate the pixels[] array, you must first call loadPixels(),
+ * and after changes have been made, call updatePixels(). Even if the renderer may not seem to use
+ * this function in the current Processing release, this will always be subject to change.
+ */
+ loadPixels: noop,
+
+ toImageData: function() {
+ if (this.isRemote) {
+ return this.sourceImg;
+ }
+
+ if (!this.__isDirty) {
+ return this.imageData;
+ }
+
+ var canvasData = getCanvasData(this.sourceImg);
+ return canvasData.context.getImageData(0, 0, this.width, this.height);
+ },
+
+ toDataURL: function() {
+ if (this.isRemote) { // Remote images cannot access imageData
+ throw "Image is loaded remotely. Cannot create dataURI.";
+ }
+ var canvasData = getCanvasData(this.imageData);
+ return canvasData.canvas.toDataURL();
+ },
+
+ fromImageData: function(canvasImg) {
+ var w = canvasImg.width,
+ h = canvasImg.height,
+ canvas = document.createElement('canvas'),
+ ctx = canvas.getContext('2d');
+
+ this.width = canvas.width = w;
+ this.height = canvas.height = h;
+
+ ctx.putImageData(canvasImg, 0, 0);
+
+ // changed for 0.9
+ this.format = PConstants.ARGB;
+
+ this.imageData = canvasImg;
+ this.sourceImg = canvas;
+ }
+ };
+
+ p.PImage = PImage;
+
+ /**
+ * Creates a new PImage (the datatype for storing images). This provides a fresh buffer of pixels to play
+ * with. Set the size of the buffer with the width and height parameters. The format parameter defines how
+ * the pixels are stored. See the PImage reference for more information.
+ * Be sure to include all three parameters, specifying only the width and height (but no format) will
+ * produce a strange error.
+ * Advanced users please note that createImage() should be used instead of the syntax new PImage().
+ *
+ * @param {int} width image width
+ * @param {int} height image height
+ * @param {MODE} format Either RGB, ARGB, ALPHA (grayscale alpha channel)
+ *
+ * @returns {PImage}
+ *
+ * @see PImage
+ * @see PGraphics
+ */
+ p.createImage = function(w, h, mode) {
+ return new PImage(w,h,mode);
+ };
+
+ // Loads an image for display. Type is an extension. Callback is fired on load.
+ /**
+ * Loads an image into a variable of type PImage. Four types of images ( .gif, .jpg, .tga, .png) images may
+ * be loaded. To load correctly, images must be located in the data directory of the current sketch. In most
+ * cases, load all images in setup() to preload them at the start of the program. Loading images inside draw()
+ * will reduce the speed of a program.
+ * The filename parameter can also be a URL to a file found online. For security reasons, a Processing sketch
+ * found online can only download files from the same server from which it came. Getting around this restriction
+ * requires a signed applet.
+ * The extension parameter is used to determine the image type in cases where the image filename does not end
+ * with a proper extension. Specify the extension as the second parameter to loadImage(), as shown in the
+ * third example on this page.
+ * If an image is not loaded successfully, the null value is returned and an error message will be printed to
+ * the console. The error message does not halt the program, however the null value may cause a NullPointerException
+ * if your code does not check whether the value returned from loadImage() is null.
+ * Depending on the type of error, a PImage object may still be returned, but the width and height of the image
+ * will be set to -1. This happens if bad image data is returned or cannot be decoded properly. Sometimes this happens
+ * with image URLs that produce a 403 error or that redirect to a password prompt, because loadImage() will attempt
+ * to interpret the HTML as image data.
+ *
+ * @param {String} filename name of file to load, can be .gif, .jpg, .tga, or a handful of other image
+ * types depending on your platform.
+ * @param {String} extension the type of image to load, for example "png", "gif", "jpg"
+ *
+ * @returns {PImage}
+ *
+ * @see PImage
+ * @see image
+ * @see imageMode
+ * @see background
+ */
+ p.loadImage = function(file, type, callback) {
+ // if type is specified, we just ignore it
+
+ var pimg;
+ // if image is in the preloader cache return a new PImage
+ if (curSketch.imageCache.images[file]) {
+ pimg = new PImage(curSketch.imageCache.images[file]);
+ pimg.loaded = true;
+ return pimg;
+ }
+ // else async load it
+ pimg = new PImage();
+ var img = document.createElement('img');
+
+ pimg.sourceImg = img;
+
+ img.onload = (function(aImage, aPImage, aCallback) {
+ var image = aImage;
+ var pimg = aPImage;
+ var callback = aCallback;
+ return function() {
+ // change the <img> object into a PImage now that its loaded
+ pimg.fromHTMLImageData(image);
+ pimg.loaded = true;
+ if (callback) {
+ callback();
+ }
+ };
+ }(img, pimg, callback));
+
+ img.src = file; // needs to be called after the img.onload function is declared or it wont work in opera
+ return pimg;
+ };
+
+ // async loading of large images, same functionality as loadImage above
+ /**
+ * This function load images on a separate thread so that your sketch does not freeze while images load during
+ * setup(). While the image is loading, its width and height will be 0. If an error occurs while loading the image,
+ * its width and height will be set to -1. You'll know when the image has loaded properly because its width and
+ * height will be greater than 0. Asynchronous image loading (particularly when downloading from a server) can
+ * dramatically improve performance.
+ * The extension parameter is used to determine the image type in cases where the image filename does not end
+ * with a proper extension. Specify the extension as the second parameter to requestImage().
+ *
+ * @param {String} filename name of file to load, can be .gif, .jpg, .tga, or a handful of other image
+ * types depending on your platform.
+ * @param {String} extension the type of image to load, for example "png", "gif", "jpg"
+ *
+ * @returns {PImage}
+ *
+ * @see PImage
+ * @see loadImage
+ */
+ p.requestImage = p.loadImage;
+
+ function get$2(x,y) {
+ var data;
+ // return the color at x,y (int) of curContext
+ if (x >= p.width || x < 0 || y < 0 || y >= p.height) {
+ // x,y is outside image return transparent black
+ return 0;
+ }
+
+ // loadPixels() has been called
+ if (isContextReplaced) {
+ var offset = ((0|x) + p.width * (0|y)) * 4;
+ data = p.imageData.data;
+ return (data[offset + 3] << 24) & PConstants.ALPHA_MASK |
+ (data[offset] << 16) & PConstants.RED_MASK |
+ (data[offset + 1] << 8) & PConstants.GREEN_MASK |
+ data[offset + 2] & PConstants.BLUE_MASK;
+ }
+
+ // x,y is inside canvas space
+ data = p.toImageData(0|x, 0|y, 1, 1).data;
+ return (data[3] << 24) & PConstants.ALPHA_MASK |
+ (data[0] << 16) & PConstants.RED_MASK |
+ (data[1] << 8) & PConstants.GREEN_MASK |
+ data[2] & PConstants.BLUE_MASK;
+ }
+ function get$3(x,y,img) {
+ if (img.isRemote) { // Remote images cannot access imageData
+ throw "Image is loaded remotely. Cannot get x,y.";
+ }
+ // PImage.get(x,y) was called, return the color (int) at x,y of img
+ var offset = y * img.width * 4 + (x * 4),
+ data = img.imageData.data;
+ return (data[offset + 3] << 24) & PConstants.ALPHA_MASK |
+ (data[offset] << 16) & PConstants.RED_MASK |
+ (data[offset + 1] << 8) & PConstants.GREEN_MASK |
+ data[offset + 2] & PConstants.BLUE_MASK;
+ }
+ function get$4(x, y, w, h) {
+ // return a PImage of w and h from cood x,y of curContext
+ var c = new PImage(w, h, PConstants.ARGB);
+ c.fromImageData(p.toImageData(x, y, w, h));
+ return c;
+ }
+ function get$5(x, y, w, h, img) {
+ if (img.isRemote) { // Remote images cannot access imageData
+ throw "Image is loaded remotely. Cannot get x,y,w,h.";
+ }
+ // PImage.get(x,y,w,h) was called, return x,y,w,h PImage of img
+ // offset start point needs to be *4
+ var c = new PImage(w, h, PConstants.ARGB), cData = c.imageData.data,
+ imgWidth = img.width, imgHeight = img.height, imgData = img.imageData.data;
+ // Don't need to copy pixels from the image outside ranges.
+ var startRow = Math.max(0, -y), startColumn = Math.max(0, -x),
+ stopRow = Math.min(h, imgHeight - y), stopColumn = Math.min(w, imgWidth - x);
+ for (var i = startRow; i < stopRow; ++i) {
+ var sourceOffset = ((y + i) * imgWidth + (x + startColumn)) * 4;
+ var targetOffset = (i * w + startColumn) * 4;
+ for (var j = startColumn; j < stopColumn; ++j) {
+ cData[targetOffset++] = imgData[sourceOffset++];
+ cData[targetOffset++] = imgData[sourceOffset++];
+ cData[targetOffset++] = imgData[sourceOffset++];
+ cData[targetOffset++] = imgData[sourceOffset++];
+ }
+ }
+ c.__isDirty = true;
+ return c;
+ }
+
+ // Gets a single pixel or block of pixels from the current Canvas Context or a PImage
+ /**
+ * Reads the color of any pixel or grabs a section of an image. If no parameters are specified, the entire
+ * image is returned. Get the value of one pixel by specifying an x,y coordinate. Get a section of the display
+ * window by specifying an additional width and height parameter. If the pixel requested is outside of the image
+ * window, black is returned. The numbers returned are scaled according to the current color ranges, but only RGB
+ * values are returned by this function. For example, even though you may have drawn a shape with colorMode(HSB),
+ * the numbers returned will be in RGB.
+ * Getting the color of a single pixel with get(x, y) is easy, but not as fast as grabbing the data directly
+ * from pixels[]. The equivalent statement to "get(x, y)" using pixels[] is "pixels[y*width+x]". Processing
+ * requires calling loadPixels() to load the display window data into the pixels[] array before getting the values.
+ * This function ignores imageMode().
+ *
+ * @param {int} x x-coordinate of the pixel
+ * @param {int} y y-coordinate of the pixel
+ * @param {int} width width of pixel rectangle to get
+ * @param {int} height height of pixel rectangle to get
+ *
+ * @returns {Color|PImage}
+ *
+ * @see set
+ * @see pixels[]
+ * @see imageMode
+ */
+ p.get = function(x, y, w, h, img) {
+ // for 0 2 and 4 arguments use curContext, otherwise PImage.get was called
+ if (img !== undefined) {
+ return get$5(x, y, w, h, img);
+ }
+ if (h !== undefined) {
+ return get$4(x, y, w, h);
+ }
+ if (w !== undefined) {
+ return get$3(x, y, w);
+ }
+ if (y !== undefined) {
+ return get$2(x, y);
+ }
+ if (x !== undefined) {
+ // PImage.get() was called, return a new PImage
+ return get$5(0, 0, x.width, x.height, x);
+ }
+
+ return get$4(0, 0, p.width, p.height);
+ };
+
+ /**
+ * Creates and returns a new <b>PGraphics</b> object of the types P2D, P3D, and JAVA2D. Use this class if you need to draw
+ * into an off-screen graphics buffer. It's not possible to use <b>createGraphics()</b> with OPENGL, because it doesn't
+ * allow offscreen use. The DXF and PDF renderers require the filename parameter. <br /><br /> It's important to call
+ * any drawing commands between beginDraw() and endDraw() statements. This is also true for any commands that affect
+ * drawing, such as smooth() or colorMode().<br /><br /> Unlike the main drawing surface which is completely opaque,
+ * surfaces created with createGraphics() can have transparency. This makes it possible to draw into a graphics and
+ * maintain the alpha channel.
+ *
+ * @param {int} width width in pixels
+ * @param {int} height height in pixels
+ * @param {int} renderer Either P2D, P3D, JAVA2D, PDF, DXF
+ * @param {String} filename the name of the file (not supported yet)
+ */
+ p.createGraphics = function(w, h, render) {
+ var pg = new Processing();
+ pg.size(w, h, render);
+ pg.background(0,0);
+ return pg;
+ };
+
+ // pixels caching
+ function resetContext() {
+ if(isContextReplaced) {
+ curContext = originalContext;
+ isContextReplaced = false;
+
+ p.updatePixels();
+ }
+ }
+ function SetPixelContextWrapper() {
+ function wrapFunction(newContext, name) {
+ function wrapper() {
+ resetContext();
+ curContext[name].apply(curContext, arguments);
+ }
+ newContext[name] = wrapper;
+ }
+ function wrapProperty(newContext, name) {
+ function getter() {
+ resetContext();
+ return curContext[name];
+ }
+ function setter(value) {
+ resetContext();
+ curContext[name] = value;
+ }
+ p.defineProperty(newContext, name, { get: getter, set: setter });
+ }
+ for(var n in curContext) {
+ if(typeof curContext[n] === 'function') {
+ wrapFunction(this, n);
+ } else {
+ wrapProperty(this, n);
+ }
+ }
+ }
+ function replaceContext() {
+ if(isContextReplaced) {
+ return;
+ }
+ p.loadPixels();
+ if(proxyContext === null) {
+ originalContext = curContext;
+ proxyContext = new SetPixelContextWrapper();
+ }
+ isContextReplaced = true;
+ curContext = proxyContext;
+ setPixelsCached = 0;
+ }
+
+ function set$3(x, y, c) {
+ if (x < p.width && x >= 0 && y >= 0 && y < p.height) {
+ replaceContext();
+ p.pixels.setPixel((0|x)+p.width*(0|y), c);
+ if(++setPixelsCached > maxPixelsCached) {
+ resetContext();
+ }
+ }
+ }
+ function set$4(x, y, obj, img) {
+ if (img.isRemote) { // Remote images cannot access imageData
+ throw "Image is loaded remotely. Cannot set x,y.";
+ }
+ var c = p.color.toArray(obj);
+ var offset = y * img.width * 4 + (x*4);
+ var data = img.imageData.data;
+ data[offset] = c[0];
+ data[offset+1] = c[1];
+ data[offset+2] = c[2];
+ data[offset+3] = c[3];
+ }
+
+ // Paints a pixel array into the canvas
+ /**
+ * Changes the color of any pixel or writes an image directly into the display window. The x and y parameters
+ * specify the pixel to change and the color parameter specifies the color value. The color parameter is affected
+ * by the current color mode (the default is RGB values from 0 to 255). When setting an image, the x and y
+ * parameters define the coordinates for the upper-left corner of the image.
+ * Setting the color of a single pixel with set(x, y) is easy, but not as fast as putting the data directly
+ * into pixels[]. The equivalent statement to "set(x, y, #000000)" using pixels[] is "pixels[y*width+x] = #000000".
+ * You must call loadPixels() to load the display window data into the pixels[] array before setting the values
+ * and calling updatePixels() to update the window with any changes. This function ignores imageMode().
+ *
+ * @param {int} x x-coordinate of the pixel
+ * @param {int} y y-coordinate of the pixel
+ * @param {Color} obj any value of the color datatype
+ * @param {PImage} img any valid variable of type PImage
+ *
+ * @see get
+ * @see pixels[]
+ * @see imageMode
+ */
+ p.set = function(x, y, obj, img) {
+ var color, oldFill;
+ if (arguments.length === 3) {
+ // called p.set(), was it with a color or a img ?
+ if (typeof obj === "number") {
+ set$3(x, y, obj);
+ } else if (obj instanceof PImage || obj.__isPImage) {
+ p.image(obj, x, y);
+ }
+ } else if (arguments.length === 4) {
+ // PImage.set(x,y,c) was called, set coordinate x,y color to c of img
+ set$4(x, y, obj, img);
+ }
+ };
+ p.imageData = {};
+
+ // handle the sketch code for pixels[]
+ // parser code converts pixels[] to getPixels() or setPixels(),
+ // .length becomes getLength()
+ /**
+ * Array containing the values for all the pixels in the display window. These values are of the color datatype.
+ * This array is the size of the display window. For example, if the image is 100x100 pixels, there will be 10000
+ * values and if the window is 200x300 pixels, there will be 60000 values. The index value defines the position
+ * of a value within the array. For example, the statment color b = pixels[230] will set the variable b to be
+ * equal to the value at that location in the array.
+ * Before accessing this array, the data must loaded with the loadPixels() function. After the array data has
+ * been modified, the updatePixels() function must be run to update the changes.
+ *
+ * @param {int} index must not exceed the size of the array
+ *
+ * @see loadPixels
+ * @see updatePixels
+ * @see get
+ * @see set
+ * @see PImage
+ */
+ p.pixels = {
+ getLength: function() { return p.imageData.data.length ? p.imageData.data.length/4 : 0; },
+ getPixel: function(i) {
+ var offset = i*4, data = p.imageData.data;
+ return (data[offset+3] << 24) & 0xff000000 |
+ (data[offset+0] << 16) & 0x00ff0000 |
+ (data[offset+1] << 8) & 0x0000ff00 |
+ data[offset+2] & 0x000000ff;
+ },
+ setPixel: function(i,c) {
+ var offset = i*4, data = p.imageData.data;
+ data[offset+0] = (c & 0x00ff0000) >>> 16; // RED_MASK
+ data[offset+1] = (c & 0x0000ff00) >>> 8; // GREEN_MASK
+ data[offset+2] = (c & 0x000000ff); // BLUE_MASK
+ data[offset+3] = (c & 0xff000000) >>> 24; // ALPHA_MASK
+ },
+ toArray: function() {
+ var arr = [], length = p.imageData.width * p.imageData.height, data = p.imageData.data;
+ for (var i = 0, offset = 0; i < length; i++, offset += 4) {
+ arr.push((data[offset+3] << 24) & 0xff000000 |
+ (data[offset+0] << 16) & 0x00ff0000 |
+ (data[offset+1] << 8) & 0x0000ff00 |
+ data[offset+2] & 0x000000ff);
+ }
+ return arr;
+ },
+ set: function(arr) {
+ for (var i = 0, aL = arr.length; i < aL; i++) {
+ this.setPixel(i, arr[i]);
+ }
+ }
+ };
+
+ // Gets a 1-Dimensional pixel array from Canvas
+ /**
+ * Loads the pixel data for the display window into the pixels[] array. This function must always be called
+ * before reading from or writing to pixels[].
+ * Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the rule is that
+ * any time you want to manipulate the pixels[] array, you must first call loadPixels(), and after changes
+ * have been made, call updatePixels(). Even if the renderer may not seem to use this function in the current
+ * Processing release, this will always be subject to change.
+ *
+ * @see pixels[]
+ * @see updatePixels
+ */
+ p.loadPixels = function() {
+ p.imageData = drawing.$ensureContext().getImageData(0, 0, p.width, p.height);
+ };
+
+ // Draws a 1-Dimensional pixel array to Canvas
+ /**
+ * Updates the display window with the data in the pixels[] array. Use in conjunction with loadPixels(). If
+ * you're only reading pixels from the array, there's no need to call updatePixels() unless there are changes.
+ * Certain renderers may or may not seem to require loadPixels() or updatePixels(). However, the rule is that
+ * any time you want to manipulate the pixels[] array, you must first call loadPixels(), and after changes
+ * have been made, call updatePixels(). Even if the renderer may not seem to use this function in the current
+ * Processing release, this will always be subject to change.
+ * Currently, none of the renderers use the additional parameters to updatePixels(), however this may be
+ * implemented in the future.
+ *
+ * @see loadPixels
+ * @see pixels[]
+ */
+ p.updatePixels = function() {
+ if (p.imageData) {
+ drawing.$ensureContext().putImageData(p.imageData, 0, 0);
+ }
+ };
+
+ /**
+ * Set various hints and hacks for the renderer. This is used to handle obscure rendering features that cannot be
+ * implemented in a consistent manner across renderers. Many options will often graduate to standard features
+ * instead of hints over time.
+ * hint(ENABLE_OPENGL_4X_SMOOTH) - Enable 4x anti-aliasing for OpenGL. This can help force anti-aliasing if
+ * it has not been enabled by the user. On some graphics cards, this can also be set by the graphics driver's
+ * control panel, however not all cards make this available. This hint must be called immediately after the
+ * size() command because it resets the renderer, obliterating any settings and anything drawn (and like size(),
+ * re-running the code that came before it again).
+ * hint(DISABLE_OPENGL_2X_SMOOTH) - In Processing 1.0, Processing always enables 2x smoothing when the OpenGL
+ * renderer is used. This hint disables the default 2x smoothing and returns the smoothing behavior found in
+ * earlier releases, where smooth() and noSmooth() could be used to enable and disable smoothing, though the
+ * quality was inferior.
+ * hint(ENABLE_NATIVE_FONTS) - Use the native version fonts when they are installed, rather than the bitmapped
+ * version from a .vlw file. This is useful with the JAVA2D renderer setting, as it will improve font rendering
+ * speed. This is not enabled by default, because it can be misleading while testing because the type will look
+ * great on your machine (because you have the font installed) but lousy on others' machines if the identical
+ * font is unavailable. This option can only be set per-sketch, and must be called before any use of textFont().
+ * hint(DISABLE_DEPTH_TEST) - Disable the zbuffer, allowing you to draw on top of everything at will. When depth
+ * testing is disabled, items will be drawn to the screen sequentially, like a painting. This hint is most often
+ * used to draw in 3D, then draw in 2D on top of it (for instance, to draw GUI controls in 2D on top of a 3D
+ * interface). Starting in release 0149, this will also clear the depth buffer. Restore the default with
+ * hint(ENABLE_DEPTH_TEST), but note that with the depth buffer cleared, any 3D drawing that happens later in
+ * draw() will ignore existing shapes on the screen.
+ * hint(ENABLE_DEPTH_SORT) - Enable primitive z-sorting of triangles and lines in P3D and OPENGL. This can slow
+ * performance considerably, and the algorithm is not yet perfect. Restore the default with hint(DISABLE_DEPTH_SORT).
+ * hint(DISABLE_OPENGL_ERROR_REPORT) - Speeds up the OPENGL renderer setting by not checking for errors while
+ * running. Undo with hint(ENABLE_OPENGL_ERROR_REPORT).
+ * As of release 0149, unhint() has been removed in favor of adding additional ENABLE/DISABLE constants to reset
+ * the default behavior. This prevents the double negatives, and also reinforces which hints can be enabled or disabled.
+ *
+ * @param {MODE} item constant: name of the hint to be enabled or disabled
+ *
+ * @see PGraphics
+ * @see createGraphics
+ * @see size
+ */
+ p.hint = function(which) {
+ var curContext = drawing.$ensureContext();
+ if (which === PConstants.DISABLE_DEPTH_TEST) {
+ curContext.disable(curContext.DEPTH_TEST);
+ curContext.depthMask(false);
+ curContext.clear(curContext.DEPTH_BUFFER_BIT);
+ }
+ else if (which === PConstants.ENABLE_DEPTH_TEST) {
+ curContext.enable(curContext.DEPTH_TEST);
+ curContext.depthMask(true);
+ }
+ else if (which === PConstants.ENABLE_OPENGL_2X_SMOOTH ||
+ which === PConstants.ENABLE_OPENGL_4X_SMOOTH){
+ renderSmooth = true;
+ }
+ else if (which === PConstants.DISABLE_OPENGL_2X_SMOOTH){
+ renderSmooth = false;
+ }
+ };
+
+ /**
+ * The background() function sets the color used for the background of the Processing window.
+ * The default background is light gray. In the <b>draw()</b> function, the background color is used to clear the display window at the beginning of each frame.
+ * An image can also be used as the background for a sketch, however its width and height must be the same size as the sketch window.
+ * To resize an image 'b' to the size of the sketch window, use b.resize(width, height).
+ * Images used as background will ignore the current <b>tint()</b> setting.
+ * For the main drawing surface, the alpha value will be ignored. However,
+ * alpha can be used on PGraphics objects from <b>createGraphics()</b>. This is
+ * the only way to set all the pixels partially transparent, for instance.
+ * If the 'gray' parameter is passed in the function sets the background to a grayscale value, based on the
+ * current colorMode.
+ * <p>
+ * Note that background() should be called before any transformations occur,
+ * because some implementations may require the current transformation matrix
+ * to be identity before drawing.
+ *
+ * @param {int|float} gray specifies a value between white and black
+ * @param {int|float} value1 red or hue value (depending on the current color mode)
+ * @param {int|float} value2 green or saturation value (depending on the current color mode)
+ * @param {int|float} value3 blue or brightness value (depending on the current color mode)
+ * @param {int|float} alpha opacity of the background
+ * @param {Color} color any value of the color datatype
+ * @param {int} hex color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
+ * @param {PImage} image an instance of a PImage to use as a background
+ *
+ * @see #stroke()
+ * @see #fill()
+ * @see #tint()
+ * @see #colorMode()
+ */
+ var backgroundHelper = function(arg1, arg2, arg3, arg4) {
+ var obj;
+
+ if (arg1 instanceof PImage || arg1.__isPImage) {
+ obj = arg1;
+
+ if (!obj.loaded) {
+ throw "Error using image in background(): PImage not loaded.";
+ }
+ if(obj.width !== p.width || obj.height !== p.height){
+ throw "Background image must be the same dimensions as the canvas.";
+ }
+ } else {
+ obj = p.color(arg1, arg2, arg3, arg4);
+ }
+
+ backgroundObj = obj;
+ };
+
+ Drawing2D.prototype.background = function(arg1, arg2, arg3, arg4) {
+ if (arg1 !== undef) {
+ backgroundHelper(arg1, arg2, arg3, arg4);
+ }
+
+ if (backgroundObj instanceof PImage || backgroundObj.__isPImage) {
+ saveContext();
+ curContext.setTransform(1, 0, 0, 1, 0, 0);
+ p.image(backgroundObj, 0, 0);
+ restoreContext();
+ } else {
+ saveContext();
+ curContext.setTransform(1, 0, 0, 1, 0, 0);
+
+ // If the background is transparent
+ if (p.alpha(backgroundObj) !== colorModeA) {
+ curContext.clearRect(0,0, p.width, p.height);
+ }
+ curContext.fillStyle = p.color.toString(backgroundObj);
+ curContext.fillRect(0, 0, p.width, p.height);
+ isFillDirty = true;
+ restoreContext();
+ }
+ };
+
+ Drawing3D.prototype.background = function(arg1, arg2, arg3, arg4) {
+ if (arguments.length > 0) {
+ backgroundHelper(arg1, arg2, arg3, arg4);
+ }
+
+ var c = p.color.toGLArray(backgroundObj);
+ curContext.clearColor(c[0], c[1], c[2], c[3]);
+ curContext.clear(curContext.COLOR_BUFFER_BIT | curContext.DEPTH_BUFFER_BIT);
+
+ // An image as a background in 3D is not implemented yet
+ };
+
+ // Draws an image to the Canvas
+ /**
+ * Displays images to the screen. The images must be in the sketch's "data" directory to load correctly. Select "Add
+ * file..." from the "Sketch" menu to add the image. Processing currently works with GIF, JPEG, and Targa images. The
+ * color of an image may be modified with the tint() function and if a GIF has transparency, it will maintain its
+ * transparency. The img parameter specifies the image to display and the x and y parameters define the location of
+ * the image from its upper-left corner. The image is displayed at its original size unless the width and height
+ * parameters specify a different size. The imageMode() function changes the way the parameters work. A call to
+ * imageMode(CORNERS) will change the width and height parameters to define the x and y values of the opposite
+ * corner of the image.
+ *
+ * @param {PImage} img the image to display
+ * @param {int|float} x x-coordinate of the image
+ * @param {int|float} y y-coordinate of the image
+ * @param {int|float} width width to display the image
+ * @param {int|float} height height to display the image
+ *
+ * @see loadImage
+ * @see PImage
+ * @see imageMode
+ * @see tint
+ * @see background
+ * @see alpha
+ */
+ Drawing2D.prototype.image = function(img, x, y, w, h) {
+ // Fix fractional positions
+ x = Math.round(x);
+ y = Math.round(y);
+
+ if (img.width > 0) {
+ var wid = w || img.width;
+ var hgt = h || img.height;
+
+ var bounds = imageModeConvert(x || 0, y || 0, w || img.width, h || img.height, arguments.length < 4);
+ var fastImage = !!img.sourceImg && curTint === null;
+ if (fastImage) {
+ var htmlElement = img.sourceImg;
+ if (img.__isDirty) {
+ img.updatePixels();
+ }
+ // Using HTML element's width and height in case if the image was resized.
+ curContext.drawImage(htmlElement, 0, 0,
+ htmlElement.width, htmlElement.height, bounds.x, bounds.y, bounds.w, bounds.h);
+ } else {
+ var obj = img.toImageData();
+
+ // Tint the image
+ if (curTint !== null) {
+ curTint(obj);
+ img.__isDirty = true;
+ }
+
+ curContext.drawImage(getCanvasData(obj).canvas, 0, 0,
+ img.width, img.height, bounds.x, bounds.y, bounds.w, bounds.h);
+ }
+ }
+ };
+
+ Drawing3D.prototype.image = function(img, x, y, w, h) {
+ if (img.width > 0) {
+ // Fix fractional positions
+ x = Math.round(x);
+ y = Math.round(y);
+ w = w || img.width;
+ h = h || img.height;
+
+ p.beginShape(p.QUADS);
+ p.texture(img);
+ p.vertex(x, y, 0, 0, 0);
+ p.vertex(x, y+h, 0, 0, h);
+ p.vertex(x+w, y+h, 0, w, h);
+ p.vertex(x+w, y, 0, w, 0);
+ p.endShape();
+ }
+ };
+
+ /**
+ * The tint() function sets the fill value for displaying images. Images can be tinted to
+ * specified colors or made transparent by setting the alpha.
+ * <br><br>To make an image transparent, but not change it's color,
+ * use white as the tint color and specify an alpha value. For instance,
+ * tint(255, 128) will make an image 50% transparent (unless
+ * <b>colorMode()</b> has been used).
+ *
+ * <br><br>When using hexadecimal notation to specify a color, use "#" or
+ * "0x" before the values (e.g. #CCFFAA, 0xFFCCFFAA). The # syntax uses six
+ * digits to specify a color (the way colors are specified in HTML and CSS).
+ * When using the hexadecimal notation starting with "0x", the hexadecimal
+ * value must be specified with eight characters; the first two characters
+ * define the alpha component and the remainder the red, green, and blue
+ * components.
+ * <br><br>The value for the parameter "gray" must be less than or equal
+ * to the current maximum value as specified by <b>colorMode()</b>.
+ * The default maximum value is 255.
+ * <br><br>The tint() method is also used to control the coloring of
+ * textures in 3D.
+ *
+ * @param {int|float} gray any valid number
+ * @param {int|float} alpha opacity of the image
+ * @param {int|float} value1 red or hue value
+ * @param {int|float} value2 green or saturation value
+ * @param {int|float} value3 blue or brightness value
+ * @param {int|float} color any value of the color datatype
+ * @param {int} hex color value in hexadecimal notation (i.e. #FFCC00 or 0xFFFFCC00)
+ *
+ * @see #noTint()
+ * @see #image()
+ */
+ p.tint = function(a1, a2, a3, a4) {
+ var tintColor = p.color(a1, a2, a3, a4);
+ var r = p.red(tintColor) / colorModeX;
+ var g = p.green(tintColor) / colorModeY;
+ var b = p.blue(tintColor) / colorModeZ;
+ var a = p.alpha(tintColor) / colorModeA;
+ curTint = function(obj) {
+ var data = obj.data,
+ length = 4 * obj.width * obj.height;
+ for (var i = 0; i < length;) {
+ data[i++] *= r;
+ data[i++] *= g;
+ data[i++] *= b;
+ data[i++] *= a;
+ }
+ };
+ // for overriding the color buffer when 3d rendering
+ curTint3d = function(data){
+ for (var i = 0; i < data.length;) {
+ data[i++] = r;
+ data[i++] = g;
+ data[i++] = b;
+ data[i++] = a;
+ }
+ };
+ };
+
+ /**
+ * The noTint() function removes the current fill value for displaying images and reverts to displaying images with their original hues.
+ *
+ * @see #tint()
+ * @see #image()
+ */
+ p.noTint = function() {
+ curTint = null;
+ curTint3d = null;
+ };
+
+ /**
+ * Copies a region of pixels from the display window to another area of the display window and copies a region of pixels from an
+ * image used as the srcImg parameter into the display window. If the source and destination regions aren't the same size, it will
+ * automatically resize the source pixels to fit the specified target region. No alpha information is used in the process, however
+ * if the source image has an alpha channel set, it will be copied as well. This function ignores imageMode().
+ *
+ * @param {int} x X coordinate of the source's upper left corner
+ * @param {int} y Y coordinate of the source's upper left corner
+ * @param {int} width source image width
+ * @param {int} height source image height
+ * @param {int} dx X coordinate of the destination's upper left corner
+ * @param {int} dy Y coordinate of the destination's upper left corner
+ * @param {int} dwidth destination image width
+ * @param {int} dheight destination image height
+ * @param {PImage} srcImg image variable referring to the source image
+ *
+ * @see blend
+ * @see get
+ */
+ p.copy = function(src, sx, sy, sw, sh, dx, dy, dw, dh) {
+ if (dh === undef) {
+ // shift everything, and introduce p
+ dh = dw;
+ dw = dy;
+ dy = dx;
+ dx = sh;
+ sh = sw;
+ sw = sy;
+ sy = sx;
+ sx = src;
+ src = p;
+ }
+ p.blend(src, sx, sy, sw, sh, dx, dy, dw, dh, PConstants.REPLACE);
+ };
+
+ /**
+ * Blends a region of pixels from one image into another (or in itself again) with full alpha channel support. There
+ * is a choice of the following modes to blend the source pixels (A) with the ones of pixels in the destination image (B):
+ * BLEND - linear interpolation of colours: C = A*factor + B
+ * ADD - additive blending with white clip: C = min(A*factor + B, 255)
+ * SUBTRACT - subtractive blending with black clip: C = max(B - A*factor, 0)
+ * DARKEST - only the darkest colour succeeds: C = min(A*factor, B)
+ * LIGHTEST - only the lightest colour succeeds: C = max(A*factor, B)
+ * DIFFERENCE - subtract colors from underlying image.
+ * EXCLUSION - similar to DIFFERENCE, but less extreme.
+ * MULTIPLY - Multiply the colors, result will always be darker.
+ * SCREEN - Opposite multiply, uses inverse values of the colors.
+ * OVERLAY - A mix of MULTIPLY and SCREEN. Multiplies dark values, and screens light values.
+ * HARD_LIGHT - SCREEN when greater than 50% gray, MULTIPLY when lower.
+ * SOFT_LIGHT - Mix of DARKEST and LIGHTEST. Works like OVERLAY, but not as harsh.
+ * DODGE - Lightens light tones and increases contrast, ignores darks. Called "Color Dodge" in Illustrator and Photoshop.
+ * BURN - Darker areas are applied, increasing contrast, ignores lights. Called "Color Burn" in Illustrator and Photoshop.
+ * All modes use the alpha information (highest byte) of source image pixels as the blending factor. If the source and
+ * destination regions are different sizes, the image will be automatically resized to match the destination size. If the
+ * srcImg parameter is not used, the display window is used as the source image. This function ignores imageMode().
+ *
+ * @param {int} x X coordinate of the source's upper left corner
+ * @param {int} y Y coordinate of the source's upper left corner
+ * @param {int} width source image width
+ * @param {int} height source image height
+ * @param {int} dx X coordinate of the destination's upper left corner
+ * @param {int} dy Y coordinate of the destination's upper left corner
+ * @param {int} dwidth destination image width
+ * @param {int} dheight destination image height
+ * @param {PImage} srcImg image variable referring to the source image
+ * @param {PImage} MODE Either BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, DIFFERENCE, EXCLUSION, MULTIPLY, SCREEN,
+ * OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, BURN
+ * @see filter
+ */
+ p.blend = function(src, sx, sy, sw, sh, dx, dy, dw, dh, mode, pimgdest) {
+ if (src.isRemote) {
+ throw "Image is loaded remotely. Cannot blend image.";
+ }
+
+ if (mode === undef) {
+ // shift everything, and introduce p
+ mode = dh;
+ dh = dw;
+ dw = dy;
+ dy = dx;
+ dx = sh;
+ sh = sw;
+ sw = sy;
+ sy = sx;
+ sx = src;
+ src = p;
+ }
+
+ var sx2 = sx + sw,
+ sy2 = sy + sh,
+ dx2 = dx + dw,
+ dy2 = dy + dh,
+ dest = pimgdest || p;
+
+ // check if pimgdest is there and pixels, if so this was a call from pimg.blend
+ if (pimgdest === undef || mode === undef) {
+ p.loadPixels();
+ }
+
+ src.loadPixels();
+
+ if (src === p && p.intersect(sx, sy, sx2, sy2, dx, dy, dx2, dy2)) {
+ p.blit_resize(p.get(sx, sy, sx2 - sx, sy2 - sy), 0, 0, sx2 - sx - 1, sy2 - sy - 1,
+ dest.imageData.data, dest.width, dest.height, dx, dy, dx2, dy2, mode);
+ } else {
+ p.blit_resize(src, sx, sy, sx2, sy2, dest.imageData.data, dest.width, dest.height, dx, dy, dx2, dy2, mode);
+ }
+
+ if (pimgdest === undef) {
+ p.updatePixels();
+ }
+ };
+
+ // helper function for filter()
+ var buildBlurKernel = function(r) {
+ var radius = p.floor(r * 3.5), i;
+ radius = (radius < 1) ? 1 : ((radius < 248) ? radius : 248);
+ if (p.shared.blurRadius !== radius) {
+ p.shared.blurRadius = radius;
+ p.shared.blurKernelSize = 1 + (p.shared.blurRadius<<1);
+ p.shared.blurKernel = new Float32Array(p.shared.blurKernelSize);
+ var sharedBlurKernal = p.shared.blurKernel;
+ var sharedBlurKernelSize = p.shared.blurKernelSize;
+ var sharedBlurRadius = p.shared.blurRadius;
+ // init blurKernel
+ for (i = 0; i < sharedBlurKernelSize; i++) {
+ sharedBlurKernal[i] = 0;
+ }
+ var radiusiSquared = (radius - 1) * (radius - 1);
+ for (i = 1; i < radius; i++) {
+ sharedBlurKernal[radius + i] = sharedBlurKernal[radius-i] = radiusiSquared;
+ }
+ sharedBlurKernal[radius] = radius * radius;
+ }
+ };
+
+ var blurARGB = function(r, aImg) {
+ var sum, cr, cg, cb, ca, c, m;
+ var read, ri, ym, ymi, bk0;
+ var wh = aImg.pixels.getLength();
+ var r2 = new Float32Array(wh);
+ var g2 = new Float32Array(wh);
+ var b2 = new Float32Array(wh);
+ var a2 = new Float32Array(wh);
+ var yi = 0;
+ var x, y, i, offset;
+
+ buildBlurKernel(r);
+
+ var aImgHeight = aImg.height;
+ var aImgWidth = aImg.width;
+ var sharedBlurKernelSize = p.shared.blurKernelSize;
+ var sharedBlurRadius = p.shared.blurRadius;
+ var sharedBlurKernal = p.shared.blurKernel;
+ var pix = aImg.imageData.data;
+
+ for (y = 0; y < aImgHeight; y++) {
+ for (x = 0; x < aImgWidth; x++) {
+ cb = cg = cr = ca = sum = 0;
+ read = x - sharedBlurRadius;
+ if (read<0) {
+ bk0 = -read;
+ read = 0;
+ } else {
+ if (read >= aImgWidth) {
+ break;
+ }
+ bk0=0;
+ }
+ for (i = bk0; i < sharedBlurKernelSize; i++) {
+ if (read >= aImgWidth) {
+ break;
+ }
+ offset = (read + yi) *4;
+ m = sharedBlurKernal[i];
+ ca += m * pix[offset + 3];
+ cr += m * pix[offset];
+ cg += m * pix[offset + 1];
+ cb += m * pix[offset + 2];
+ sum += m;
+ read++;
+ }
+ ri = yi + x;
+ a2[ri] = ca / sum;
+ r2[ri] = cr / sum;
+ g2[ri] = cg / sum;
+ b2[ri] = cb / sum;
+ }
+ yi += aImgWidth;
+ }
+
+ yi = 0;
+ ym = -sharedBlurRadius;
+ ymi = ym*aImgWidth;
+
+ for (y = 0; y < aImgHeight; y++) {
+ for (x = 0; x < aImgWidth; x++) {
+ cb = cg = cr = ca = sum = 0;
+ if (ym<0) {
+ bk0 = ri = -ym;
+ read = x;
+ } else {
+ if (ym >= aImgHeight) {
+ break;
+ }
+ bk0 = 0;
+ ri = ym;
+ read = x + ymi;
+ }
+ for (i = bk0; i < sharedBlurKernelSize; i++) {
+ if (ri >= aImgHeight) {
+ break;
+ }
+ m = sharedBlurKernal[i];
+ ca += m * a2[read];
+ cr += m * r2[read];
+ cg += m * g2[read];
+ cb += m * b2[read];
+ sum += m;
+ ri++;
+ read += aImgWidth;
+ }
+ offset = (x + yi) *4;
+ pix[offset] = cr / sum;
+ pix[offset + 1] = cg / sum;
+ pix[offset + 2] = cb / sum;
+ pix[offset + 3] = ca / sum;
+ }
+ yi += aImgWidth;
+ ymi += aImgWidth;
+ ym++;
+ }
+ };
+
+ // helper funtion for ERODE and DILATE modes of filter()
+ var dilate = function(isInverted, aImg) {
+ var currIdx = 0;
+ var maxIdx = aImg.pixels.getLength();
+ var out = new Int32Array(maxIdx);
+ var currRowIdx, maxRowIdx, colOrig, colOut, currLum;
+ var idxRight, idxLeft, idxUp, idxDown,
+ colRight, colLeft, colUp, colDown,
+ lumRight, lumLeft, lumUp, lumDown;
+
+ if (!isInverted) {
+ // erosion (grow light areas)
+ while (currIdx<maxIdx) {
+ currRowIdx = currIdx;
+ maxRowIdx = currIdx + aImg.width;
+ while (currIdx < maxRowIdx) {
+ colOrig = colOut = aImg.pixels.getPixel(currIdx);
+ idxLeft = currIdx - 1;
+ idxRight = currIdx + 1;
+ idxUp = currIdx - aImg.width;
+ idxDown = currIdx + aImg.width;
+ if (idxLeft < currRowIdx) {
+ idxLeft = currIdx;
+ }
+ if (idxRight >= maxRowIdx) {
+ idxRight = currIdx;
+ }
+ if (idxUp < 0) {
+ idxUp = 0;
+ }
+ if (idxDown >= maxIdx) {
+ idxDown = currIdx;
+ }
+ colUp = aImg.pixels.getPixel(idxUp);
+ colLeft = aImg.pixels.getPixel(idxLeft);
+ colDown = aImg.pixels.getPixel(idxDown);
+ colRight = aImg.pixels.getPixel(idxRight);
+
+ // compute luminance
+ currLum = 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff);
+ lumLeft = 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff);
+ lumRight = 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff);
+ lumUp = 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff);
+ lumDown = 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff);
+
+ if (lumLeft > currLum) {
+ colOut = colLeft;
+ currLum = lumLeft;
+ }
+ if (lumRight > currLum) {
+ colOut = colRight;
+ currLum = lumRight;
+ }
+ if (lumUp > currLum) {
+ colOut = colUp;
+ currLum = lumUp;
+ }
+ if (lumDown > currLum) {
+ colOut = colDown;
+ currLum = lumDown;
+ }
+ out[currIdx++] = colOut;
+ }
+ }
+ } else {
+ // dilate (grow dark areas)
+ while (currIdx < maxIdx) {
+ currRowIdx = currIdx;
+ maxRowIdx = currIdx + aImg.width;
+ while (currIdx < maxRowIdx) {
+ colOrig = colOut = aImg.pixels.getPixel(currIdx);
+ idxLeft = currIdx - 1;
+ idxRight = currIdx + 1;
+ idxUp = currIdx - aImg.width;
+ idxDown = currIdx + aImg.width;
+ if (idxLeft < currRowIdx) {
+ idxLeft = currIdx;
+ }
+ if (idxRight >= maxRowIdx) {
+ idxRight = currIdx;
+ }
+ if (idxUp < 0) {
+ idxUp = 0;
+ }
+ if (idxDown >= maxIdx) {
+ idxDown = currIdx;
+ }
+ colUp = aImg.pixels.getPixel(idxUp);
+ colLeft = aImg.pixels.getPixel(idxLeft);
+ colDown = aImg.pixels.getPixel(idxDown);
+ colRight = aImg.pixels.getPixel(idxRight);
+
+ // compute luminance
+ currLum = 77*(colOrig>>16&0xff) + 151*(colOrig>>8&0xff) + 28*(colOrig&0xff);
+ lumLeft = 77*(colLeft>>16&0xff) + 151*(colLeft>>8&0xff) + 28*(colLeft&0xff);
+ lumRight = 77*(colRight>>16&0xff) + 151*(colRight>>8&0xff) + 28*(colRight&0xff);
+ lumUp = 77*(colUp>>16&0xff) + 151*(colUp>>8&0xff) + 28*(colUp&0xff);
+ lumDown = 77*(colDown>>16&0xff) + 151*(colDown>>8&0xff) + 28*(colDown&0xff);
+
+ if (lumLeft < currLum) {
+ colOut = colLeft;
+ currLum = lumLeft;
+ }
+ if (lumRight < currLum) {
+ colOut = colRight;
+ currLum = lumRight;
+ }
+ if (lumUp < currLum) {
+ colOut = colUp;
+ currLum = lumUp;
+ }
+ if (lumDown < currLum) {
+ colOut = colDown;
+ currLum = lumDown;
+ }
+ out[currIdx++]=colOut;
+ }
+ }
+ }
+ aImg.pixels.set(out);
+ //p.arraycopy(out,0,pixels,0,maxIdx);
+ };
+
+ /**
+ * Filters the display window as defined by one of the following modes:
+ * THRESHOLD - converts the image to black and white pixels depending if they are above or below the threshold
+ * defined by the level parameter. The level must be between 0.0 (black) and 1.0(white). If no level is specified, 0.5 is used.
+ * GRAY - converts any colors in the image to grayscale equivalents
+ * INVERT - sets each pixel to its inverse value
+ * POSTERIZE - limits each channel of the image to the number of colors specified as the level parameter
+ * BLUR - executes a Guassian blur with the level parameter specifying the extent of the blurring. If no level parameter is
+ * used, the blur is equivalent to Guassian blur of radius 1.
+ * OPAQUE - sets the alpha channel to entirely opaque.
+ * ERODE - reduces the light areas with the amount defined by the level parameter.
+ * DILATE - increases the light areas with the amount defined by the level parameter.
+ *
+ * @param {MODE} MODE Either THRESHOLD, GRAY, INVERT, POSTERIZE, BLUR, OPAQUE, ERODE, or DILATE
+ * @param {int|float} level defines the quality of the filter
+ *
+ * @see blend
+ */
+ p.filter = function(kind, param, aImg){
+ var img, col, lum, i;
+
+ if (arguments.length === 3) {
+ aImg.loadPixels();
+ img = aImg;
+ } else {
+ p.loadPixels();
+ img = p;
+ }
+
+ if (param === undef) {
+ param = null;
+ }
+ if (img.isRemote) { // Remote images cannot access imageData
+ throw "Image is loaded remotely. Cannot filter image.";
+ }
+ // begin filter process
+ var imglen = img.pixels.getLength();
+ switch (kind) {
+ case PConstants.BLUR:
+ var radius = param || 1; // if no param specified, use 1 (default for p5)
+ blurARGB(radius, img);
+ break;
+
+ case PConstants.GRAY:
+ if (img.format === PConstants.ALPHA) { //trouble
+ // for an alpha image, convert it to an opaque grayscale
+ for (i = 0; i < imglen; i++) {
+ col = 255 - img.pixels.getPixel(i);
+ img.pixels.setPixel(i,(0xff000000 | (col << 16) | (col << 8) | col));
+ }
+ img.format = PConstants.RGB; //trouble
+ } else {
+ for (i = 0; i < imglen; i++) {
+ col = img.pixels.getPixel(i);
+ lum = (77*(col>>16&0xff) + 151*(col>>8&0xff) + 28*(col&0xff))>>8;
+ img.pixels.setPixel(i,((col & PConstants.ALPHA_MASK) | lum<<16 | lum<<8 | lum));
+ }
+ }
+ break;
+
+ case PConstants.INVERT:
+ for (i = 0; i < imglen; i++) {
+ img.pixels.setPixel(i, (img.pixels.getPixel(i) ^ 0xffffff));
+ }
+ break;
+
+ case PConstants.POSTERIZE:
+ if (param === null) {
+ throw "Use filter(POSTERIZE, int levels) instead of filter(POSTERIZE)";
+ }
+ var levels = p.floor(param);
+ if ((levels < 2) || (levels > 255)) {
+ throw "Levels must be between 2 and 255 for filter(POSTERIZE, levels)";
+ }
+ var levels1 = levels - 1;
+ for (i = 0; i < imglen; i++) {
+ var rlevel = (img.pixels.getPixel(i) >> 16) & 0xff;
+ var glevel = (img.pixels.getPixel(i) >> 8) & 0xff;
+ var blevel = img.pixels.getPixel(i) & 0xff;
+ rlevel = (((rlevel * levels) >> 8) * 255) / levels1;
+ glevel = (((glevel * levels) >> 8) * 255) / levels1;
+ blevel = (((blevel * levels) >> 8) * 255) / levels1;
+ img.pixels.setPixel(i, ((0xff000000 & img.pixels.getPixel(i)) | (rlevel << 16) | (glevel << 8) | blevel));
+ }
+ break;
+
+ case PConstants.OPAQUE:
+ for (i = 0; i < imglen; i++) {
+ img.pixels.setPixel(i, (img.pixels.getPixel(i) | 0xff000000));
+ }
+ img.format = PConstants.RGB; //trouble
+ break;
+
+ case PConstants.THRESHOLD:
+ if (param === null) {
+ param = 0.5;
+ }
+ if ((param < 0) || (param > 1)) {
+ throw "Level must be between 0 and 1 for filter(THRESHOLD, level)";
+ }
+ var thresh = p.floor(param * 255);
+ for (i = 0; i < imglen; i++) {
+ var max = p.max((img.pixels.getPixel(i) & PConstants.RED_MASK) >> 16, p.max((img.pixels.getPixel(i) & PConstants.GREEN_MASK) >> 8, (img.pixels.getPixel(i) & PConstants.BLUE_MASK)));
+ img.pixels.setPixel(i, ((img.pixels.getPixel(i) & PConstants.ALPHA_MASK) | ((max < thresh) ? 0x000000 : 0xffffff)));
+ }
+ break;
+
+ case PConstants.ERODE:
+ dilate(true, img);
+ break;
+
+ case PConstants.DILATE:
+ dilate(false, img);
+ break;
+ }
+ img.updatePixels();
+ };
+
+
+ // shared variables for blit_resize(), filter_new_scanline(), filter_bilinear(), filter()
+ // change this in the future to not be exposed to p
+ p.shared = {
+ fracU: 0,
+ ifU: 0,
+ fracV: 0,
+ ifV: 0,
+ u1: 0,
+ u2: 0,
+ v1: 0,
+ v2: 0,
+ sX: 0,
+ sY: 0,
+ iw: 0,
+ iw1: 0,
+ ih1: 0,
+ ul: 0,
+ ll: 0,
+ ur: 0,
+ lr: 0,
+ cUL: 0,
+ cLL: 0,
+ cUR: 0,
+ cLR: 0,
+ srcXOffset: 0,
+ srcYOffset: 0,
+ r: 0,
+ g: 0,
+ b: 0,
+ a: 0,
+ srcBuffer: null,
+ blurRadius: 0,
+ blurKernelSize: 0,
+ blurKernel: null
+ };
+
+ p.intersect = function(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2) {
+ var sw = sx2 - sx1 + 1;
+ var sh = sy2 - sy1 + 1;
+ var dw = dx2 - dx1 + 1;
+ var dh = dy2 - dy1 + 1;
+ if (dx1 < sx1) {
+ dw += dx1 - sx1;
+ if (dw > sw) {
+ dw = sw;
+ }
+ } else {
+ var w = sw + sx1 - dx1;
+ if (dw > w) {
+ dw = w;
+ }
+ }
+ if (dy1 < sy1) {
+ dh += dy1 - sy1;
+ if (dh > sh) {
+ dh = sh;
+ }
+ } else {
+ var h = sh + sy1 - dy1;
+ if (dh > h) {
+ dh = h;
+ }
+ }
+ return ! (dw <= 0 || dh <= 0);
+ };
+
+ var blendFuncs = {};
+ blendFuncs[PConstants.BLEND] = p.modes.blend;
+ blendFuncs[PConstants.ADD] = p.modes.add;
+ blendFuncs[PConstants.SUBTRACT] = p.modes.subtract;
+ blendFuncs[PConstants.LIGHTEST] = p.modes.lightest;
+ blendFuncs[PConstants.DARKEST] = p.modes.darkest;
+ blendFuncs[PConstants.REPLACE] = p.modes.replace;
+ blendFuncs[PConstants.DIFFERENCE] = p.modes.difference;
+ blendFuncs[PConstants.EXCLUSION] = p.modes.exclusion;
+ blendFuncs[PConstants.MULTIPLY] = p.modes.multiply;
+ blendFuncs[PConstants.SCREEN] = p.modes.screen;
+ blendFuncs[PConstants.OVERLAY] = p.modes.overlay;
+ blendFuncs[PConstants.HARD_LIGHT] = p.modes.hard_light;
+ blendFuncs[PConstants.SOFT_LIGHT] = p.modes.soft_light;
+ blendFuncs[PConstants.DODGE] = p.modes.dodge;
+ blendFuncs[PConstants.BURN] = p.modes.burn;
+
+ p.blit_resize = function(img, srcX1, srcY1, srcX2, srcY2, destPixels,
+ screenW, screenH, destX1, destY1, destX2, destY2, mode) {
+ var x, y;
+ if (srcX1 < 0) {
+ srcX1 = 0;
+ }
+ if (srcY1 < 0) {
+ srcY1 = 0;
+ }
+ if (srcX2 >= img.width) {
+ srcX2 = img.width - 1;
+ }
+ if (srcY2 >= img.height) {
+ srcY2 = img.height - 1;
+ }
+ var srcW = srcX2 - srcX1;
+ var srcH = srcY2 - srcY1;
+ var destW = destX2 - destX1;
+ var destH = destY2 - destY1;
+
+ if (destW <= 0 || destH <= 0 || srcW <= 0 || srcH <= 0 || destX1 >= screenW ||
+ destY1 >= screenH || srcX1 >= img.width || srcY1 >= img.height) {
+ return;
+ }
+
+ var dx = Math.floor(srcW / destW * PConstants.PRECISIONF);
+ var dy = Math.floor(srcH / destH * PConstants.PRECISIONF);
+
+ var pshared = p.shared;
+
+ pshared.srcXOffset = Math.floor(destX1 < 0 ? -destX1 * dx : srcX1 * PConstants.PRECISIONF);
+ pshared.srcYOffset = Math.floor(destY1 < 0 ? -destY1 * dy : srcY1 * PConstants.PRECISIONF);
+ if (destX1 < 0) {
+ destW += destX1;
+ destX1 = 0;
+ }
+ if (destY1 < 0) {
+ destH += destY1;
+ destY1 = 0;
+ }
+ destW = Math.min(destW, screenW - destX1);
+ destH = Math.min(destH, screenH - destY1);
+
+ var destOffset = destY1 * screenW + destX1;
+ var destColor;
+
+ pshared.srcBuffer = img.imageData.data;
+ pshared.iw = img.width;
+ pshared.iw1 = img.width - 1;
+ pshared.ih1 = img.height - 1;
+
+ // cache for speed
+ var filterBilinear = p.filter_bilinear,
+ filterNewScanline = p.filter_new_scanline,
+ blendFunc = blendFuncs[mode],
+ blendedColor,
+ idx,
+ cULoffset,
+ cURoffset,
+ cLLoffset,
+ cLRoffset,
+ ALPHA_MASK = PConstants.ALPHA_MASK,
+ RED_MASK = PConstants.RED_MASK,
+ GREEN_MASK = PConstants.GREEN_MASK,
+ BLUE_MASK = PConstants.BLUE_MASK,
+ PREC_MAXVAL = PConstants.PREC_MAXVAL,
+ PRECISIONB = PConstants.PRECISIONB,
+ PREC_RED_SHIFT = PConstants.PREC_RED_SHIFT,
+ PREC_ALPHA_SHIFT = PConstants.PREC_ALPHA_SHIFT,
+ srcBuffer = pshared.srcBuffer,
+ min = Math.min;
+
+ for (y = 0; y < destH; y++) {
+
+ pshared.sX = pshared.srcXOffset;
+ pshared.fracV = pshared.srcYOffset & PREC_MAXVAL;
+ pshared.ifV = PREC_MAXVAL - pshared.fracV;
+ pshared.v1 = (pshared.srcYOffset >> PRECISIONB) * pshared.iw;
+ pshared.v2 = min((pshared.srcYOffset >> PRECISIONB) + 1, pshared.ih1) * pshared.iw;
+
+ for (x = 0; x < destW; x++) {
+ idx = (destOffset + x) * 4;
+
+ destColor = (destPixels[idx + 3] << 24) &
+ ALPHA_MASK | (destPixels[idx] << 16) &
+ RED_MASK | (destPixels[idx + 1] << 8) &
+ GREEN_MASK | destPixels[idx + 2] & BLUE_MASK;
+
+ pshared.fracU = pshared.sX & PREC_MAXVAL;
+ pshared.ifU = PREC_MAXVAL - pshared.fracU;
+ pshared.ul = (pshared.ifU * pshared.ifV) >> PRECISIONB;
+ pshared.ll = (pshared.ifU * pshared.fracV) >> PRECISIONB;
+ pshared.ur = (pshared.fracU * pshared.ifV) >> PRECISIONB;
+ pshared.lr = (pshared.fracU * pshared.fracV) >> PRECISIONB;
+ pshared.u1 = (pshared.sX >> PRECISIONB);
+ pshared.u2 = min(pshared.u1 + 1, pshared.iw1);
+
+ cULoffset = (pshared.v1 + pshared.u1) * 4;
+ cURoffset = (pshared.v1 + pshared.u2) * 4;
+ cLLoffset = (pshared.v2 + pshared.u1) * 4;
+ cLRoffset = (pshared.v2 + pshared.u2) * 4;
+
+ pshared.cUL = (srcBuffer[cULoffset + 3] << 24) &
+ ALPHA_MASK | (srcBuffer[cULoffset] << 16) &
+ RED_MASK | (srcBuffer[cULoffset + 1] << 8) &
+ GREEN_MASK | srcBuffer[cULoffset + 2] & BLUE_MASK;
+
+ pshared.cUR = (srcBuffer[cURoffset + 3] << 24) &
+ ALPHA_MASK | (srcBuffer[cURoffset] << 16) &
+ RED_MASK | (srcBuffer[cURoffset + 1] << 8) &
+ GREEN_MASK | srcBuffer[cURoffset + 2] & BLUE_MASK;
+
+ pshared.cLL = (srcBuffer[cLLoffset + 3] << 24) &
+ ALPHA_MASK | (srcBuffer[cLLoffset] << 16) &
+ RED_MASK | (srcBuffer[cLLoffset + 1] << 8) &
+ GREEN_MASK | srcBuffer[cLLoffset + 2] & BLUE_MASK;
+
+ pshared.cLR = (srcBuffer[cLRoffset + 3] << 24) &
+ ALPHA_MASK | (srcBuffer[cLRoffset] << 16) &
+ RED_MASK | (srcBuffer[cLRoffset + 1] << 8) &
+ GREEN_MASK | srcBuffer[cLRoffset + 2] & BLUE_MASK;
+
+ pshared.r = ((pshared.ul * ((pshared.cUL & RED_MASK) >> 16) +
+ pshared.ll * ((pshared.cLL & RED_MASK) >> 16) +
+ pshared.ur * ((pshared.cUR & RED_MASK) >> 16) +
+ pshared.lr * ((pshared.cLR & RED_MASK) >> 16)) << PREC_RED_SHIFT) & RED_MASK;
+ pshared.g = ((pshared.ul * (pshared.cUL & GREEN_MASK) +
+ pshared.ll * (pshared.cLL & GREEN_MASK) +
+ pshared.ur * (pshared.cUR & GREEN_MASK) +
+ pshared.lr * (pshared.cLR & GREEN_MASK)) >>> PRECISIONB) & GREEN_MASK;
+ pshared.b = (pshared.ul * (pshared.cUL & BLUE_MASK) +
+ pshared.ll * (pshared.cLL & BLUE_MASK) +
+ pshared.ur * (pshared.cUR & BLUE_MASK) +
+ pshared.lr * (pshared.cLR & BLUE_MASK)) >>> PRECISIONB;
+ pshared.a = ((pshared.ul * ((pshared.cUL & ALPHA_MASK) >>> 24) +
+ pshared.ll * ((pshared.cLL & ALPHA_MASK) >>> 24) +
+ pshared.ur * ((pshared.cUR & ALPHA_MASK) >>> 24) +
+ pshared.lr * ((pshared.cLR & ALPHA_MASK) >>> 24)) << PREC_ALPHA_SHIFT) & ALPHA_MASK;
+
+ blendedColor = blendFunc(destColor, (pshared.a | pshared.r | pshared.g | pshared.b));
+
+ destPixels[idx] = (blendedColor & RED_MASK) >>> 16;
+ destPixels[idx + 1] = (blendedColor & GREEN_MASK) >>> 8;
+ destPixels[idx + 2] = (blendedColor & BLUE_MASK);
+ destPixels[idx + 3] = (blendedColor & ALPHA_MASK) >>> 24;
+
+ pshared.sX += dx;
+ }
+ destOffset += screenW;
+ pshared.srcYOffset += dy;
+ }
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Font handling
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * loadFont() Loads a font into a variable of type PFont.
+ *
+ * @param {String} name filename of the font to load
+ * @param {int|float} size option font size (used internally)
+ *
+ * @returns {PFont} new PFont object
+ *
+ * @see #PFont
+ * @see #textFont
+ * @see #text
+ * @see #createFont
+ */
+ p.loadFont = function(name, size) {
+ if (name === undef) {
+ throw("font name required in loadFont.");
+ }
+ if (name.indexOf(".svg") === -1) {
+ if (size === undef) {
+ size = curTextFont.size;
+ }
+ return PFont.get(name, size);
+ }
+ // If the font is a glyph, calculate by SVG table
+ var font = p.loadGlyphs(name);
+
+ return {
+ name: name,
+ css: '12px sans-serif',
+ glyph: true,
+ units_per_em: font.units_per_em,
+ horiz_adv_x: 1 / font.units_per_em * font.horiz_adv_x,
+ ascent: font.ascent,
+ descent: font.descent,
+ width: function(str) {
+ var width = 0;
+ var len = str.length;
+ for (var i = 0; i < len; i++) {
+ try {
+ width += parseFloat(p.glyphLook(p.glyphTable[name], str[i]).horiz_adv_x);
+ }
+ catch(e) {
+ Processing.debug(e);
+ }
+ }
+ return width / p.glyphTable[name].units_per_em;
+ }
+ };
+ };
+
+ /**
+ * createFont() Loads a font into a variable of type PFont.
+ * Smooth and charset are ignored in Processing.js.
+ *
+ * @param {String} name filename of the font to load
+ * @param {int|float} size font size in pixels
+ * @param {boolean} smooth not used in Processing.js
+ * @param {char[]} charset not used in Processing.js
+ *
+ * @returns {PFont} new PFont object
+ *
+ * @see #PFont
+ * @see #textFont
+ * @see #text
+ * @see #loadFont
+ */
+ p.createFont = function(name, size) {
+ // because Processing.js only deals with real fonts,
+ // createFont is simply a wrapper for loadFont/2
+ return p.loadFont(name, size);
+ };
+
+ /**
+ * textFont() Sets the current font.
+ *
+ * @param {PFont} pfont the PFont to load as current text font
+ * @param {int|float} size optional font size in pixels
+ *
+ * @see #createFont
+ * @see #loadFont
+ * @see #PFont
+ * @see #text
+ */
+ p.textFont = function(pfont, size) {
+ if (size !== undef) {
+ // If we're using an SVG glyph font, don't load from cache
+ if (!pfont.glyph) {
+ pfont = PFont.get(pfont.name, size);
+ }
+ curTextSize = size;
+ }
+ curTextFont = pfont;
+ curFontName = curTextFont.name;
+ curTextAscent = curTextFont.ascent;
+ curTextDescent = curTextFont.descent;
+ curTextLeading = curTextFont.leading;
+ var curContext = drawing.$ensureContext();
+ curContext.font = curTextFont.css;
+ };
+
+ /**
+ * textSize() Sets the current font size in pixels.
+ *
+ * @param {int|float} size font size in pixels
+ *
+ * @see #textFont
+ * @see #loadFont
+ * @see #PFont
+ * @see #text
+ */
+ p.textSize = function(size) {
+ curTextFont = PFont.get(curFontName, size);
+ curTextSize = size;
+ // recache metrics
+ curTextAscent = curTextFont.ascent;
+ curTextDescent = curTextFont.descent;
+ curTextLeading = curTextFont.leading;
+ var curContext = drawing.$ensureContext();
+ curContext.font = curTextFont.css;
+ };
+
+ /**
+ * textAscent() returns the maximum height a character extends above the baseline of the
+ * current font at its current size, in pixels.
+ *
+ * @returns {float} height of the current font above the baseline, at its current size, in pixels
+ *
+ * @see #textDescent
+ */
+ p.textAscent = function() {
+ return curTextAscent;
+ };
+
+ /**
+ * textDescent() returns the maximum depth a character will protrude below the baseline of
+ * the current font at its current size, in pixels.
+ *
+ * @returns {float} depth of the current font below the baseline, at its current size, in pixels
+ *
+ * @see #textAscent
+ */
+ p.textDescent = function() {
+ return curTextDescent;
+ };
+
+ /**
+ * textLeading() Sets the current font's leading, which is the distance
+ * from baseline to baseline over consecutive lines, with additional vertical
+ * spacing taking into account. Usually this value is 1.2 or 1.25 times the
+ * textsize, but this value can be changed to effect vertically compressed
+ * or stretched text.
+ *
+ * @param {int|float} the desired baseline-to-baseline size in pixels
+ */
+ p.textLeading = function(leading) {
+ curTextLeading = leading;
+ };
+
+ /**
+ * textAlign() Sets the current alignment for drawing text.
+ *
+ * @param {int} ALIGN Horizontal alignment, either LEFT, CENTER, or RIGHT
+ * @param {int} YALIGN optional vertical alignment, either TOP, BOTTOM, CENTER, or BASELINE
+ *
+ * @see #loadFont
+ * @see #PFont
+ * @see #text
+ */
+ p.textAlign = function(xalign, yalign) {
+ horizontalTextAlignment = xalign;
+ verticalTextAlignment = yalign || PConstants.BASELINE;
+ };
+
+ /**
+ * toP5String converts things with arbitrary data type into
+ * string values, for text rendering.
+ *
+ * @param {any} any object that can be converted into a string
+ *
+ * @return {String} the string representation of the input
+ */
+ function toP5String(obj) {
+ if(obj instanceof String) {
+ return obj;
+ }
+ if(typeof obj === 'number') {
+ // check if an int
+ if(obj === (0 | obj)) {
+ return obj.toString();
+ }
+ return p.nf(obj, 0, 3);
+ }
+ if(obj === null || obj === undef) {
+ return "";
+ }
+ return obj.toString();
+ }
+
+ /**
+ * textWidth() Calculates and returns the width of any character or text string in pixels.
+ *
+ * @param {char|String} str char or String to be measured
+ *
+ * @return {float} width of char or String in pixels
+ *
+ * @see #loadFont
+ * @see #PFont
+ * @see #text
+ * @see #textFont
+ */
+ Drawing2D.prototype.textWidth = function(str) {
+ var lines = toP5String(str).split(/\r?\n/g), width = 0;
+ var i, linesCount = lines.length;
+
+ curContext.font = curTextFont.css;
+ for (i = 0; i < linesCount; ++i) {
+ width = Math.max(width, curTextFont.measureTextWidth(lines[i]));
+ }
+ return width | 0;
+ };
+
+ Drawing3D.prototype.textWidth = function(str) {
+ var lines = toP5String(str).split(/\r?\n/g), width = 0;
+ var i, linesCount = lines.length;
+ if (textcanvas === undef) {
+ textcanvas = document.createElement("canvas");
+ }
+
+ var textContext = textcanvas.getContext("2d");
+ textContext.font = curTextFont.css;
+
+ for (i = 0; i < linesCount; ++i) {
+ width = Math.max(width, textContext.measureText(lines[i]).width);
+ }
+ return width | 0;
+ };
+
+ // A lookup table for characters that can not be referenced by Object
+ p.glyphLook = function(font, chr) {
+ try {
+ switch (chr) {
+ case "1":
+ return font.one;
+ case "2":
+ return font.two;
+ case "3":
+ return font.three;
+ case "4":
+ return font.four;
+ case "5":
+ return font.five;
+ case "6":
+ return font.six;
+ case "7":
+ return font.seven;
+ case "8":
+ return font.eight;
+ case "9":
+ return font.nine;
+ case "0":
+ return font.zero;
+ case " ":
+ return font.space;
+ case "$":
+ return font.dollar;
+ case "!":
+ return font.exclam;
+ case '"':
+ return font.quotedbl;
+ case "#":
+ return font.numbersign;
+ case "%":
+ return font.percent;
+ case "&":
+ return font.ampersand;
+ case "'":
+ return font.quotesingle;
+ case "(":
+ return font.parenleft;
+ case ")":
+ return font.parenright;
+ case "*":
+ return font.asterisk;
+ case "+":
+ return font.plus;
+ case ",":
+ return font.comma;
+ case "-":
+ return font.hyphen;
+ case ".":
+ return font.period;
+ case "/":
+ return font.slash;
+ case "_":
+ return font.underscore;
+ case ":":
+ return font.colon;
+ case ";":
+ return font.semicolon;
+ case "<":
+ return font.less;
+ case "=":
+ return font.equal;
+ case ">":
+ return font.greater;
+ case "?":
+ return font.question;
+ case "@":
+ return font.at;
+ case "[":
+ return font.bracketleft;
+ case "\\":
+ return font.backslash;
+ case "]":
+ return font.bracketright;
+ case "^":
+ return font.asciicircum;
+ case "`":
+ return font.grave;
+ case "{":
+ return font.braceleft;
+ case "|":
+ return font.bar;
+ case "}":
+ return font.braceright;
+ case "~":
+ return font.asciitilde;
+ // If the character is not 'special', access it by object reference
+ default:
+ return font[chr];
+ }
+ } catch(e) {
+ Processing.debug(e);
+ }
+ };
+
+ // Print some text to the Canvas
+ Drawing2D.prototype.text$line = function(str, x, y, z, align) {
+ var textWidth = 0, xOffset = 0;
+ // If the font is a standard Canvas font...
+ if (!curTextFont.glyph) {
+ if (str && ("fillText" in curContext)) {
+ if (isFillDirty) {
+ curContext.fillStyle = p.color.toString(currentFillColor);
+ isFillDirty = false;
+ }
+
+ // horizontal offset/alignment
+ if(align === PConstants.RIGHT || align === PConstants.CENTER) {
+ textWidth = curTextFont.measureTextWidth(str);
+
+ if(align === PConstants.RIGHT) {
+ xOffset = -textWidth;
+ } else { // if(align === PConstants.CENTER)
+ xOffset = -textWidth/2;
+ }
+ }
+
+ curContext.fillText(str, x+xOffset, y);
+ }
+ } else {
+ // If the font is a Batik SVG font...
+ var font = p.glyphTable[curFontName];
+ saveContext();
+ curContext.translate(x, y + curTextSize);
+
+ // horizontal offset/alignment
+ if(align === PConstants.RIGHT || align === PConstants.CENTER) {
+ textWidth = font.width(str);
+
+ if(align === PConstants.RIGHT) {
+ xOffset = -textWidth;
+ } else { // if(align === PConstants.CENTER)
+ xOffset = -textWidth/2;
+ }
+ }
+
+ var upem = font.units_per_em,
+ newScale = 1 / upem * curTextSize;
+
+ curContext.scale(newScale, newScale);
+
+ for (var i=0, len=str.length; i < len; i++) {
+ // Test character against glyph table
+ try {
+ p.glyphLook(font, str[i]).draw();
+ } catch(e) {
+ Processing.debug(e);
+ }
+ }
+ restoreContext();
+ }
+ };
+
+ Drawing3D.prototype.text$line = function(str, x, y, z, align) {
+ // handle case for 3d text
+ if (textcanvas === undef) {
+ textcanvas = document.createElement("canvas");
+ }
+ var oldContext = curContext;
+ curContext = textcanvas.getContext("2d");
+ curContext.font = curTextFont.css;
+ var textWidth = curTextFont.measureTextWidth(str);
+ textcanvas.width = textWidth;
+ textcanvas.height = curTextSize;
+ curContext = textcanvas.getContext("2d"); // refreshes curContext
+ curContext.font = curTextFont.css;
+ curContext.textBaseline="top";
+
+ // paint on 2D canvas
+ Drawing2D.prototype.text$line(str,0,0,0,PConstants.LEFT);
+
+ // use it as a texture
+ var aspect = textcanvas.width/textcanvas.height;
+ curContext = oldContext;
+
+ curContext.bindTexture(curContext.TEXTURE_2D, textTex);
+ curContext.texImage2D(curContext.TEXTURE_2D, 0, curContext.RGBA, curContext.RGBA, curContext.UNSIGNED_BYTE, textcanvas);
+ curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MAG_FILTER, curContext.LINEAR);
+ curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_MIN_FILTER, curContext.LINEAR);
+ curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_T, curContext.CLAMP_TO_EDGE);
+ curContext.texParameteri(curContext.TEXTURE_2D, curContext.TEXTURE_WRAP_S, curContext.CLAMP_TO_EDGE);
+ // If we don't have a power of two texture, we can't mipmap it.
+ // curContext.generateMipmap(curContext.TEXTURE_2D);
+
+ // horizontal offset/alignment
+ var xOffset = 0;
+ if (align === PConstants.RIGHT) {
+ xOffset = -textWidth;
+ } else if(align === PConstants.CENTER) {
+ xOffset = -textWidth/2;
+ }
+ var model = new PMatrix3D();
+ var scalefactor = curTextSize * 0.5;
+ model.translate(x+xOffset-scalefactor/2, y-scalefactor, z);
+ model.scale(-aspect*scalefactor, -scalefactor, scalefactor);
+ model.translate(-1, -1, -1);
+ model.transpose();
+
+ var view = new PMatrix3D();
+ view.scale(1, -1, 1);
+ view.apply(modelView.array());
+ view.transpose();
+
+ curContext.useProgram(programObject2D);
+ vertexAttribPointer("aVertex2d", programObject2D, "aVertex", 3, textBuffer);
+ vertexAttribPointer("aTextureCoord2d", programObject2D, "aTextureCoord", 2, textureBuffer);
+ uniformi("uSampler2d", programObject2D, "uSampler", [0]);
+
+ uniformi("uIsDrawingText2d", programObject2D, "uIsDrawingText", true);
+
+ uniformMatrix("uModel2d", programObject2D, "uModel", false, model.array());
+ uniformMatrix("uView2d", programObject2D, "uView", false, view.array());
+ uniformf("uColor2d", programObject2D, "uColor", fillStyle);
+ curContext.bindBuffer(curContext.ELEMENT_ARRAY_BUFFER, indexBuffer);
+ curContext.drawElements(curContext.TRIANGLES, 6, curContext.UNSIGNED_SHORT, 0);
+ };
+
+
+ /**
+ * unbounded text function (z is an optional argument)
+ */
+ function text$4(str, x, y, z) {
+ var lines, linesCount;
+ if(str.indexOf('\n') < 0) {
+ lines = [str];
+ linesCount = 1;
+ } else {
+ lines = str.split(/\r?\n/g);
+ linesCount = lines.length;
+ }
+ // handle text line-by-line
+
+ var yOffset = 0;
+ if(verticalTextAlignment === PConstants.TOP) {
+ yOffset = curTextAscent + curTextDescent;
+ } else if(verticalTextAlignment === PConstants.CENTER) {
+ yOffset = curTextAscent/2 - (linesCount-1)*curTextLeading/2;
+ } else if(verticalTextAlignment === PConstants.BOTTOM) {
+ yOffset = -(curTextDescent + (linesCount-1)*curTextLeading);
+ }
+
+ for(var i=0;i<linesCount;++i) {
+ var line = lines[i];
+ drawing.text$line(line, x, y + yOffset, z, horizontalTextAlignment);
+ yOffset += curTextLeading;
+ }
+ }
+
+
+ /**
+ * box-bounded text function (z is an optional argument)
+ */
+ function text$6(str, x, y, width, height, z) {
+ // 'fail' on 0-valued dimensions
+ if (str.length === 0 || width === 0 || height === 0) {
+ return;
+ }
+ // also 'fail' if the text height is larger than the bounding height
+ if(curTextSize > height) {
+ return;
+ }
+
+ var spaceMark = -1;
+ var start = 0;
+ var lineWidth = 0;
+ var drawCommands = [];
+
+ // run through text, character-by-character
+ for (var charPos=0, len=str.length; charPos < len; charPos++)
+ {
+ var currentChar = str[charPos];
+ var spaceChar = (currentChar === " ");
+ var letterWidth = curTextFont.measureTextWidth(currentChar);
+
+ // if we aren't looking at a newline, and the text still fits, keep processing
+ if (currentChar !== "\n" && (lineWidth + letterWidth <= width)) {
+ if (spaceChar) { spaceMark = charPos; }
+ lineWidth += letterWidth;
+ }
+
+ // if we're looking at a newline, or the text no longer fits, push the section that fit into the drawcommand list
+ else
+ {
+ if (spaceMark + 1 === start) {
+ if(charPos>0) {
+ // Whole line without spaces so far.
+ spaceMark = charPos;
+ } else {
+ // 'fail', because the line can't even fit the first character
+ return;
+ }
+ }
+
+ if (currentChar === "\n") {
+ drawCommands.push({text:str.substring(start, charPos), width: lineWidth});
+ start = charPos + 1;
+ } else {
+ // current is not a newline, which means the line doesn't fit in box. push text.
+ // In Processing 1.5.1, the space is also pushed, so we push up to spaceMark+1,
+ // rather than up to spaceMark, as was the case for Processing 1.5 and earlier.
+ drawCommands.push({text:str.substring(start, spaceMark+1), width: lineWidth});
+ start = spaceMark + 1;
+ }
+
+ // newline + return
+ lineWidth = 0;
+ charPos = start - 1;
+ }
+ }
+
+ // push the remaining text
+ if (start < len) {
+ drawCommands.push({text:str.substring(start), width: lineWidth});
+ }
+
+ // resolve horizontal alignment
+ var xOffset = 1,
+ yOffset = curTextAscent;
+ if (horizontalTextAlignment === PConstants.CENTER) {
+ xOffset = width/2;
+ } else if (horizontalTextAlignment === PConstants.RIGHT) {
+ xOffset = width;
+ }
+
+ // resolve vertical alignment
+ var linesCount = drawCommands.length,
+ visibleLines = Math.min(linesCount, Math.floor(height/curTextLeading));
+ if(verticalTextAlignment === PConstants.TOP) {
+ yOffset = curTextAscent + curTextDescent;
+ } else if(verticalTextAlignment === PConstants.CENTER) {
+ yOffset = (height/2) - curTextLeading * (visibleLines/2 - 1);
+ } else if(verticalTextAlignment === PConstants.BOTTOM) {
+ yOffset = curTextDescent + curTextLeading;
+ }
+
+ var command,
+ drawCommand,
+ leading;
+ for (command = 0; command < linesCount; command++) {
+ leading = command * curTextLeading;
+ // stop if not enough space for one more line draw
+ if (yOffset + leading > height - curTextDescent) {
+ break;
+ }
+ drawCommand = drawCommands[command];
+ drawing.text$line(drawCommand.text, x + xOffset, y + yOffset + leading, z, horizontalTextAlignment);
+ }
+ }
+
+ /**
+ * text() Draws text to the screen.
+ *
+ * @param {String|char|int|float} data the alphanumeric symbols to be displayed
+ * @param {int|float} x x-coordinate of text
+ * @param {int|float} y y-coordinate of text
+ * @param {int|float} z optional z-coordinate of text
+ * @param {String} stringdata optional letters to be displayed
+ * @param {int|float} width optional width of text box
+ * @param {int|float} height optional height of text box
+ *
+ * @see #textAlign
+ * @see #textMode
+ * @see #loadFont
+ * @see #PFont
+ * @see #textFont
+ */
+ p.text = function() {
+ if (textMode === PConstants.SHAPE) {
+ // TODO: requires beginRaw function
+ return;
+ }
+ if (arguments.length === 3) { // for text( str, x, y)
+ text$4(toP5String(arguments[0]), arguments[1], arguments[2], 0);
+ } else if (arguments.length === 4) { // for text( str, x, y, z)
+ text$4(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3]);
+ } else if (arguments.length === 5) { // for text( str, x, y , width, height)
+ text$6(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3], arguments[4], 0);
+ } else if (arguments.length === 6) { // for text( stringdata, x, y , width, height, z)
+ text$6(toP5String(arguments[0]), arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
+ }
+ };
+
+ /**
+ * Sets the way text draws to the screen. In the default configuration (the MODEL mode), it's possible to rotate,
+ * scale, and place letters in two and three dimensional space. <br /><br /> Changing to SCREEN mode draws letters
+ * directly to the front of the window and greatly increases rendering quality and speed when used with the P2D and
+ * P3D renderers. textMode(SCREEN) with OPENGL and JAVA2D (the default) renderers will generally be slower, though
+ * pixel accurate with P2D and P3D. With textMode(SCREEN), the letters draw at the actual size of the font (in pixels)
+ * and therefore calls to <b>textSize()</b> will not affect the size of the letters. To create a font at the size you
+ * desire, use the "Create font..." option in the Tools menu, or use the createFont() function. When using textMode(SCREEN),
+ * any z-coordinate passed to a text() command will be ignored, because your computer screen is...flat!
+ *
+ * @param {int} MODE Either MODEL, SCREEN or SHAPE (not yet supported)
+ *
+ * @see loadFont
+ * @see PFont
+ * @see text
+ * @see textFont
+ * @see createFont
+ */
+ p.textMode = function(mode){
+ textMode = mode;
+ };
+
+ // Load Batik SVG Fonts and parse to pre-def objects for quick rendering
+ p.loadGlyphs = function(url) {
+ var x, y, cx, cy, nx, ny, d, a, lastCom, lenC, horiz_adv_x, getXY = '[0-9\\-]+', path;
+
+ // Return arrays of SVG commands and coords
+ // get this to use p.matchAll() - will need to work around the lack of null return
+ var regex = function(needle, hay) {
+ var i = 0,
+ results = [],
+ latest, regexp = new RegExp(needle, "g");
+ latest = results[i] = regexp.exec(hay);
+ while (latest) {
+ i++;
+ latest = results[i] = regexp.exec(hay);
+ }
+ return results;
+ };
+
+ var buildPath = function(d) {
+ var c = regex("[A-Za-z][0-9\\- ]+|Z", d);
+ var beforePathDraw = function() {
+ saveContext();
+ return drawing.$ensureContext();
+ };
+ var afterPathDraw = function() {
+ executeContextFill();
+ executeContextStroke();
+ restoreContext();
+ };
+
+ // Begin storing path object
+ path = "return {draw:function(){var curContext=beforePathDraw();curContext.beginPath();";
+
+ x = 0;
+ y = 0;
+ cx = 0;
+ cy = 0;
+ nx = 0;
+ ny = 0;
+ d = 0;
+ a = 0;
+ lastCom = "";
+ lenC = c.length - 1;
+
+ // Loop through SVG commands translating to canvas eqivs functions in path object
+ for (var j = 0; j < lenC; j++) {
+ var com = c[j][0], xy = regex(getXY, com);
+
+ switch (com[0]) {
+ case "M":
+ //curContext.moveTo(x,-y);
+ x = parseFloat(xy[0][0]);
+ y = parseFloat(xy[1][0]);
+ path += "curContext.moveTo(" + x + "," + (-y) + ");";
+ break;
+
+ case "L":
+ //curContext.lineTo(x,-y);
+ x = parseFloat(xy[0][0]);
+ y = parseFloat(xy[1][0]);
+ path += "curContext.lineTo(" + x + "," + (-y) + ");";
+ break;
+
+ case "H":
+ //curContext.lineTo(x,-y)
+ x = parseFloat(xy[0][0]);
+ path += "curContext.lineTo(" + x + "," + (-y) + ");";
+ break;
+
+ case "V":
+ //curContext.lineTo(x,-y);
+ y = parseFloat(xy[0][0]);
+ path += "curContext.lineTo(" + x + "," + (-y) + ");";
+ break;
+
+ case "T":
+ //curContext.quadraticCurveTo(cx,-cy,nx,-ny);
+ nx = parseFloat(xy[0][0]);
+ ny = parseFloat(xy[1][0]);
+
+ if (lastCom === "Q" || lastCom === "T") {
+ d = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(cy - y, 2));
+ a = Math.PI + Math.atan2(cx - x, cy - y);
+ cx = x + (Math.sin(a) * (d));
+ cy = y + (Math.cos(a) * (d));
+ } else {
+ cx = x;
+ cy = y;
+ }
+
+ path += "curContext.quadraticCurveTo(" + cx + "," + (-cy) + "," + nx + "," + (-ny) + ");";
+ x = nx;
+ y = ny;
+ break;
+
+ case "Q":
+ //curContext.quadraticCurveTo(cx,-cy,nx,-ny);
+ cx = parseFloat(xy[0][0]);
+ cy = parseFloat(xy[1][0]);
+ nx = parseFloat(xy[2][0]);
+ ny = parseFloat(xy[3][0]);
+ path += "curContext.quadraticCurveTo(" + cx + "," + (-cy) + "," + nx + "," + (-ny) + ");";
+ x = nx;
+ y = ny;
+ break;
+
+ case "Z":
+ //curContext.closePath();
+ path += "curContext.closePath();";
+ break;
+ }
+ lastCom = com[0];
+ }
+
+ path += "afterPathDraw();";
+ path += "curContext.translate(" + horiz_adv_x + ",0);";
+ path += "}}";
+
+ return ((new Function("beforePathDraw", "afterPathDraw", path))(beforePathDraw, afterPathDraw));
+ };
+
+ // Parse SVG font-file into block of Canvas commands
+ var parseSVGFont = function(svg) {
+ // Store font attributes
+ var font = svg.getElementsByTagName("font");
+ p.glyphTable[url].horiz_adv_x = font[0].getAttribute("horiz-adv-x");
+
+ var font_face = svg.getElementsByTagName("font-face")[0];
+ p.glyphTable[url].units_per_em = parseFloat(font_face.getAttribute("units-per-em"));
+ p.glyphTable[url].ascent = parseFloat(font_face.getAttribute("ascent"));
+ p.glyphTable[url].descent = parseFloat(font_face.getAttribute("descent"));
+
+ var glyph = svg.getElementsByTagName("glyph"),
+ len = glyph.length;
+
+ // Loop through each glyph in the SVG
+ for (var i = 0; i < len; i++) {
+ // Store attributes for this glyph
+ var unicode = glyph[i].getAttribute("unicode");
+ var name = glyph[i].getAttribute("glyph-name");
+ horiz_adv_x = glyph[i].getAttribute("horiz-adv-x");
+ if (horiz_adv_x === null) {
+ horiz_adv_x = p.glyphTable[url].horiz_adv_x;
+ }
+ d = glyph[i].getAttribute("d");
+ // Split path commands in glpyh
+ if (d !== undef) {
+ path = buildPath(d);
+ // Store glyph data to table object
+ p.glyphTable[url][name] = {
+ name: name,
+ unicode: unicode,
+ horiz_adv_x: horiz_adv_x,
+ draw: path.draw
+ };
+ }
+ } // finished adding glyphs to table
+ };
+
+ // Load and parse Batik SVG font as XML into a Processing Glyph object
+ var loadXML = function() {
+ var xmlDoc;
+
+ try {
+ xmlDoc = document.implementation.createDocument("", "", null);
+ }
+ catch(e_fx_op) {
+ Processing.debug(e_fx_op.message);
+ return;
+ }
+
+ try {
+ xmlDoc.async = false;
+ xmlDoc.load(url);
+ parseSVGFont(xmlDoc.getElementsByTagName("svg")[0]);
+ }
+ catch(e_sf_ch) {
+ // Google Chrome, Safari etc.
+ Processing.debug(e_sf_ch);
+ try {
+ var xmlhttp = new window.XMLHttpRequest();
+ xmlhttp.open("GET", url, false);
+ xmlhttp.send(null);
+ parseSVGFont(xmlhttp.responseXML.documentElement);
+ }
+ catch(e) {
+ Processing.debug(e_sf_ch);
+ }
+ }
+ };
+
+ // Create a new object in glyphTable to store this font
+ p.glyphTable[url] = {};
+
+ // Begin loading the Batik SVG font...
+ loadXML(url);
+
+ // Return the loaded font for attribute grabbing
+ return p.glyphTable[url];
+ };
+
+ /**
+ * Gets the sketch parameter value. The parameter can be defined as the canvas attribute with
+ * the "data-processing-" prefix or provided in the pjs directive (e.g. param-test="52").
+ * The function tries the canvas attributes, then the pjs directive content.
+ *
+ * @param {String} name The name of the param to read.
+ *
+ * @returns {String} The parameter value, or null if parameter is not defined.
+ */
+ p.param = function(name) {
+ // trying attribute that was specified in CANVAS
+ var attributeName = "data-processing-" + name;
+ if (curElement.hasAttribute(attributeName)) {
+ return curElement.getAttribute(attributeName);
+ }
+ // trying child PARAM elements of the CANVAS
+ for (var i = 0, len = curElement.childNodes.length; i < len; ++i) {
+ var item = curElement.childNodes.item(i);
+ if (item.nodeType !== 1 || item.tagName.toLowerCase() !== "param") {
+ continue;
+ }
+ if (item.getAttribute("name") === name) {
+ return item.getAttribute("value");
+ }
+ }
+ // fallback to default params
+ if (curSketch.params.hasOwnProperty(name)) {
+ return curSketch.params[name];
+ }
+ return null;
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // 2D/3D methods wiring utils
+ ////////////////////////////////////////////////////////////////////////////
+ function wireDimensionalFunctions(mode) {
+ // Drawing2D/Drawing3D
+ if (mode === '3D') {
+ drawing = new Drawing3D();
+ } else if (mode === '2D') {
+ drawing = new Drawing2D();
+ } else {
+ drawing = new DrawingPre();
+ }
+
+ // Wire up functions (Use DrawingPre properties names)
+ for (var i in DrawingPre.prototype) {
+ if (DrawingPre.prototype.hasOwnProperty(i) && i.indexOf("$") < 0) {
+ p[i] = drawing[i];
+ }
+ }
+
+ // Run initialization
+ drawing.$init();
+ }
+
+ function createDrawingPreFunction(name) {
+ return function() {
+ wireDimensionalFunctions("2D");
+ return drawing[name].apply(this, arguments);
+ };
+ }
+ DrawingPre.prototype.translate = createDrawingPreFunction("translate");
+ DrawingPre.prototype.transform = createDrawingPreFunction("transform");
+ DrawingPre.prototype.scale = createDrawingPreFunction("scale");
+ DrawingPre.prototype.pushMatrix = createDrawingPreFunction("pushMatrix");
+ DrawingPre.prototype.popMatrix = createDrawingPreFunction("popMatrix");
+ DrawingPre.prototype.resetMatrix = createDrawingPreFunction("resetMatrix");
+ DrawingPre.prototype.applyMatrix = createDrawingPreFunction("applyMatrix");
+ DrawingPre.prototype.rotate = createDrawingPreFunction("rotate");
+ DrawingPre.prototype.rotateZ = createDrawingPreFunction("rotateZ");
+ DrawingPre.prototype.shearX = createDrawingPreFunction("shearX");
+ DrawingPre.prototype.shearY = createDrawingPreFunction("shearY");
+ DrawingPre.prototype.redraw = createDrawingPreFunction("redraw");
+ DrawingPre.prototype.toImageData = createDrawingPreFunction("toImageData");
+ DrawingPre.prototype.ambientLight = createDrawingPreFunction("ambientLight");
+ DrawingPre.prototype.directionalLight = createDrawingPreFunction("directionalLight");
+ DrawingPre.prototype.lightFalloff = createDrawingPreFunction("lightFalloff");
+ DrawingPre.prototype.lightSpecular = createDrawingPreFunction("lightSpecular");
+ DrawingPre.prototype.pointLight = createDrawingPreFunction("pointLight");
+ DrawingPre.prototype.noLights = createDrawingPreFunction("noLights");
+ DrawingPre.prototype.spotLight = createDrawingPreFunction("spotLight");
+ DrawingPre.prototype.beginCamera = createDrawingPreFunction("beginCamera");
+ DrawingPre.prototype.endCamera = createDrawingPreFunction("endCamera");
+ DrawingPre.prototype.frustum = createDrawingPreFunction("frustum");
+ DrawingPre.prototype.box = createDrawingPreFunction("box");
+ DrawingPre.prototype.sphere = createDrawingPreFunction("sphere");
+ DrawingPre.prototype.ambient = createDrawingPreFunction("ambient");
+ DrawingPre.prototype.emissive = createDrawingPreFunction("emissive");
+ DrawingPre.prototype.shininess = createDrawingPreFunction("shininess");
+ DrawingPre.prototype.specular = createDrawingPreFunction("specular");
+ DrawingPre.prototype.fill = createDrawingPreFunction("fill");
+ DrawingPre.prototype.stroke = createDrawingPreFunction("stroke");
+ DrawingPre.prototype.strokeWeight = createDrawingPreFunction("strokeWeight");
+ DrawingPre.prototype.smooth = createDrawingPreFunction("smooth");
+ DrawingPre.prototype.noSmooth = createDrawingPreFunction("noSmooth");
+ DrawingPre.prototype.point = createDrawingPreFunction("point");
+ DrawingPre.prototype.vertex = createDrawingPreFunction("vertex");
+ DrawingPre.prototype.endShape = createDrawingPreFunction("endShape");
+ DrawingPre.prototype.bezierVertex = createDrawingPreFunction("bezierVertex");
+ DrawingPre.prototype.curveVertex = createDrawingPreFunction("curveVertex");
+ DrawingPre.prototype.curve = createDrawingPreFunction("curve");
+ DrawingPre.prototype.line = createDrawingPreFunction("line");
+ DrawingPre.prototype.bezier = createDrawingPreFunction("bezier");
+ DrawingPre.prototype.rect = createDrawingPreFunction("rect");
+ DrawingPre.prototype.ellipse = createDrawingPreFunction("ellipse");
+ DrawingPre.prototype.background = createDrawingPreFunction("background");
+ DrawingPre.prototype.image = createDrawingPreFunction("image");
+ DrawingPre.prototype.textWidth = createDrawingPreFunction("textWidth");
+ DrawingPre.prototype.text$line = createDrawingPreFunction("text$line");
+ DrawingPre.prototype.$ensureContext = createDrawingPreFunction("$ensureContext");
+ DrawingPre.prototype.$newPMatrix = createDrawingPreFunction("$newPMatrix");
+
+ DrawingPre.prototype.size = function(aWidth, aHeight, aMode) {
+ wireDimensionalFunctions(aMode === PConstants.WEBGL ? "3D" : "2D");
+ p.size(aWidth, aHeight, aMode);
+ };
+
+ DrawingPre.prototype.$init = noop;
+
+ Drawing2D.prototype.$init = function() {
+ // Setup default 2d canvas context.
+ // Moving this here removes the number of times we need to check the 3D variable
+ p.size(p.width, p.height);
+
+ curContext.lineCap = 'round';
+
+ // Set default stroke and fill color
+ p.noSmooth();
+ p.disableContextMenu();
+ };
+ Drawing3D.prototype.$init = function() {
+ // For ref/perf test compatibility until those are fixed
+ p.use3DContext = true;
+ p.disableContextMenu();
+ };
+
+ DrawingShared.prototype.$ensureContext = function() {
+ return curContext;
+ };
+
+ //////////////////////////////////////////////////////////////////////////
+ // Keyboard Events
+ //////////////////////////////////////////////////////////////////////////
+
+ // In order to catch key events in a canvas, it needs to be "specially focusable",
+ // by assigning it a tabindex. If no tabindex is specified on-page, set this to 0.
+ if (!curElement.getAttribute("tabindex")) {
+ curElement.setAttribute("tabindex", 0);
+ }
+
+ function getKeyCode(e) {
+ var code = e.which || e.keyCode;
+ switch (code) {
+ case 13: // ENTER
+ return 10;
+ case 91: // META L (Saf/Mac)
+ case 93: // META R (Saf/Mac)
+ case 224: // META (FF/Mac)
+ return 157;
+ case 57392: // CONTROL (Op/Mac)
+ return 17;
+ case 46: // DELETE
+ return 127;
+ case 45: // INSERT
+ return 155;
+ }
+ return code;
+ }
+
+ function getKeyChar(e) {
+ var c = e.which || e.keyCode;
+ var anyShiftPressed = e.shiftKey || e.ctrlKey || e.altKey || e.metaKey;
+ switch (c) {
+ case 13:
+ c = anyShiftPressed ? 13 : 10; // RETURN vs ENTER (Mac)
+ break;
+ case 8:
+ c = anyShiftPressed ? 127 : 8; // DELETE vs BACKSPACE (Mac)
+ break;
+ }
+ return new Char(c);
+ }
+
+ function suppressKeyEvent(e) {
+ if (typeof e.preventDefault === "function") {
+ e.preventDefault();
+ } else if (typeof e.stopPropagation === "function") {
+ e.stopPropagation();
+ }
+ return false;
+ }
+
+ function updateKeyPressed() {
+ var ch;
+ for (ch in pressedKeysMap) {
+ if (pressedKeysMap.hasOwnProperty(ch)) {
+ p.__keyPressed = true;
+ return;
+ }
+ }
+ p.__keyPressed = false;
+ }
+
+ function resetKeyPressed() {
+ p.__keyPressed = false;
+ pressedKeysMap = [];
+ lastPressedKeyCode = null;
+ }
+
+ function simulateKeyTyped(code, c) {
+ pressedKeysMap[code] = c;
+ lastPressedKeyCode = null;
+ p.key = c;
+ p.keyCode = code;
+ p.keyPressed();
+ p.keyCode = 0;
+ p.keyTyped();
+ updateKeyPressed();
+ }
+
+ function handleKeydown(e) {
+ var code = getKeyCode(e);
+ if (code === PConstants.DELETE) {
+ simulateKeyTyped(code, new Char(127));
+ return;
+ }
+ if (codedKeys.indexOf(code) < 0) {
+ lastPressedKeyCode = code;
+ return;
+ }
+ var c = new Char(PConstants.CODED);
+ p.key = c;
+ p.keyCode = code;
+ pressedKeysMap[code] = c;
+ p.keyPressed();
+ lastPressedKeyCode = null;
+ updateKeyPressed();
+ return suppressKeyEvent(e);
+ }
+
+ function handleKeypress(e) {
+ if (lastPressedKeyCode === null) {
+ return; // processed in handleKeydown
+ }
+ var code = lastPressedKeyCode, c = getKeyChar(e);
+ simulateKeyTyped(code, c);
+ return suppressKeyEvent(e);
+ }
+
+ function handleKeyup(e) {
+ var code = getKeyCode(e), c = pressedKeysMap[code];
+ if (c === undef) {
+ return; // no keyPressed event was generated.
+ }
+ p.key = c;
+ p.keyCode = code;
+ p.keyReleased();
+ delete pressedKeysMap[code];
+ updateKeyPressed();
+ }
+
+ // Send aCode Processing syntax to be converted to JavaScript
+ if (!pgraphicsMode) {
+ if (aCode instanceof Processing.Sketch) {
+ // Use sketch as is
+ curSketch = aCode;
+ } else if (typeof aCode === "function") {
+ // Wrap function with default sketch parameters
+ curSketch = new Processing.Sketch(aCode);
+ } else if (!aCode) {
+ // Empty sketch
+ curSketch = new Processing.Sketch(function (){});
+ } else {
+ //#if PARSER
+ // Compile the code
+ curSketch = Processing.compile(aCode);
+ //#else
+ // throw "PJS compile is not supported";
+ //#endif
+ }
+
+ // Expose internal field for diagnostics and testing
+ p.externals.sketch = curSketch;
+
+ wireDimensionalFunctions();
+
+ // the onfocus and onblur events are handled in two parts.
+ // 1) the p.focused property is handled per sketch
+ curElement.onfocus = function() {
+ p.focused = true;
+ };
+
+ curElement.onblur = function() {
+ p.focused = false;
+ if (!curSketch.options.globalKeyEvents) {
+ resetKeyPressed();
+ }
+ };
+
+ // 2) looping status is handled per page, based on the pauseOnBlur @pjs directive
+ if (curSketch.options.pauseOnBlur) {
+ attachEventHandler(window, 'focus', function() {
+ if (doLoop) {
+ p.loop();
+ }
+ });
+
+ attachEventHandler(window, 'blur', function() {
+ if (doLoop && loopStarted) {
+ p.noLoop();
+ doLoop = true; // make sure to keep this true after the noLoop call
+ }
+ resetKeyPressed();
+ });
+ }
+
+ // if keyboard events should be handled globally, the listeners should
+ // be bound to the document window, rather than to the current canvas
+ var keyTrigger = curSketch.options.globalKeyEvents ? window : curElement;
+ attachEventHandler(keyTrigger, "keydown", handleKeydown);
+ attachEventHandler(keyTrigger, "keypress", handleKeypress);
+ attachEventHandler(keyTrigger, "keyup", handleKeyup);
+
+ // Step through the libraries that were attached at doc load...
+ for (var i in Processing.lib) {
+ if (Processing.lib.hasOwnProperty(i)) {
+ if(Processing.lib[i].hasOwnProperty("attach")) {
+ // use attach function if present
+ Processing.lib[i].attach(p);
+ } else if(Processing.lib[i] instanceof Function) {
+ // Init the libraries in the context of this p_instance (legacy)
+ Processing.lib[i].call(this);
+ }
+ }
+ }
+
+ // sketch execute test interval, used to reschedule
+ // an execute when preloads have not yet finished.
+ var retryInterval = 100;
+
+ var executeSketch = function(processing) {
+ // Don't start until all specified images and fonts in the cache are preloaded
+ if (!(curSketch.imageCache.pending || PFont.preloading.pending(retryInterval))) {
+ // the opera preload cache can only be cleared once we start
+ if (window.opera) {
+ var link,
+ element,
+ operaCache=curSketch.imageCache.operaCache;
+ for (link in operaCache) {
+ if(operaCache.hasOwnProperty(link)) {
+ element = operaCache[link];
+ if (element !== null) {
+ document.body.removeChild(element);
+ }
+ delete(operaCache[link]);
+ }
+ }
+ }
+
+ curSketch.attach(processing, defaultScope);
+
+ // pass a reference to the p instance for this sketch.
+ curSketch.onLoad(processing);
+
+ // Run void setup()
+ if (processing.setup) {
+ processing.setup();
+ // if any transforms were performed in setup reset to identity matrix
+ // so draw loop is unpolluted
+ processing.resetMatrix();
+ curSketch.onSetup();
+ }
+
+ // some pixels can be cached, flushing
+ resetContext();
+
+ if (processing.draw) {
+ if (!doLoop) {
+ processing.redraw();
+ } else {
+ processing.loop();
+ }
+ }
+ } else {
+ window.setTimeout(function() { executeSketch(processing); }, retryInterval);
+ }
+ };
+
+ // Only store an instance of non-createGraphics instances.
+ addInstance(this);
+
+ // The parser adds custom methods to the processing context
+ // this renames p to processing so these methods will run
+ executeSketch(p);
+ } else {
+ // No executable sketch was specified
+ // or called via createGraphics
+ curSketch = new Processing.Sketch();
+
+ wireDimensionalFunctions();
+
+ // Hack to make PGraphics work again after splitting size()
+ p.size = function(w, h, render) {
+ if (render && render === PConstants.WEBGL) {
+ wireDimensionalFunctions('3D');
+ } else {
+ wireDimensionalFunctions('2D');
+ }
+
+ p.size(w, h, render);
+ };
+ }
+ };
+
+ // Place-holder for overridable debugging function
+ Processing.debug = (function() {
+ if ("console" in window) {
+ return function(msg) {
+ window.console.log('Processing.js: ' + msg);
+ };
+ }
+ return noop;
+ }());
+
+ // bind prototype
+ Processing.prototype = defaultScope;
+
+ /**
+ * instance store and lookup
+ */
+ Processing.instances = processingInstances;
+ Processing.getInstanceById = function(name) {
+ return processingInstances[processingInstanceIds[name]];
+ };
+
+ // Unsupported Processing File and I/O operations.
+ (function(Processing) {
+ var unsupportedP5 = ("open() createOutput() createInput() BufferedReader selectFolder() " +
+ "dataPath() createWriter() selectOutput() beginRecord() " +
+ "saveStream() endRecord() selectInput() saveBytes() createReader() " +
+ "beginRaw() endRaw() PrintWriter delay()").split(" "),
+ count = unsupportedP5.length,
+ prettyName,
+ p5Name;
+
+ function createUnsupportedFunc(n) {
+ return function() {
+ throw "Processing.js does not support " + n + ".";
+ };
+ }
+
+ while (count--) {
+ prettyName = unsupportedP5[count];
+ p5Name = prettyName.replace("()", "");
+ Processing[p5Name] = createUnsupportedFunc(prettyName);
+ }
+ }(defaultScope));
+
+ // we're done. Return our object.
+ return Processing;
+};
+
+},{}],28:[function(require,module,exports){
+// Base source files
+var source = {
+ virtEquals: require("./Helpers/virtEquals"),
+ virtHashCode: require("./Helpers/virtHashCode"),
+ ObjectIterator: require("./Helpers/ObjectIterator"),
+ PConstants: require("./Helpers/PConstants"),
+ ArrayList: require("./Objects/ArrayList"),
+ HashMap: require("./Objects/HashMap"),
+ PVector: require("./Objects/PVector"),
+ PFont: require("./Objects/PFont"),
+ Char: require("./Objects/Char"),
+ XMLAttribute: require("./Objects/XMLAttribute"),
+ XMLElement: require("./Objects/XMLElement"),
+ PMatrix2D: require("./Objects/PMatrix2D"),
+ PMatrix3D: require("./Objects/PMatrix3D"),
+ PShape: require("./Objects/PShape"),
+ colors: require("./Objects/webcolors"),
+ PShapeSVG: require("./Objects/PShapeSVG"),
+ CommonFunctions: require("./P5Functions/commonFunctions"),
+ defaultScope: require("./Helpers/defaultScope"),
+ Processing: require("./Processing"),
+ setupParser: require("./Parser/Parser"),
+ finalize: require("./Helpers/finalizeProcessing")
+};
+
+// Additional code that gets tacked onto "p" during
+// instantiation of a Processing sketch.
+source.extend = {
+ withMath: require("./P5Functions/Math.js"),
+ withProxyFunctions: require("./P5Functions/JavaProxyFunctions")(source.virtHashCode, source.virtEquals),
+ withTouch: require("./P5Functions/touchmouse"),
+ withCommonFunctions: source.CommonFunctions.withCommonFunctions
+};
+
+/**
+ * Processing.js building function
+ */
+module.exports = function buildProcessingJS(Browser, testHarness) {
+ var noop = function(){},
+ virtEquals = source.virtEquals,
+ virtHashCode = source.virtHashCode,
+ PConstants = source.PConstants,
+ CommonFunctions = source.CommonFunctions,
+ ObjectIterator = source.ObjectIterator,
+ Char = source.Char,
+ XMLAttribute = source.XMLAttribute(),
+
+ ArrayList = source.ArrayList({
+ virtHashCode: virtHashCode,
+ virtEquals: virtEquals
+ }),
+
+ HashMap = source.HashMap({
+ virtHashCode: virtHashCode,
+ virtEquals: virtEquals
+ }),
+
+ PVector = source.PVector({
+ PConstants: PConstants
+ }),
+
+ PFont = source.PFont({
+ Browser: Browser,
+ noop: noop
+ }),
+
+ XMLElement = source.XMLElement({
+ Browser: Browser,
+ XMLAttribute: XMLAttribute
+ }),
+
+ PMatrix2D = source.PMatrix2D({
+ p:CommonFunctions
+ }),
+
+ PMatrix3D = source.PMatrix3D({
+ p:CommonFunctions
+ }),
+
+ PShape = source.PShape({
+ PConstants: PConstants,
+ PMatrix2D: PMatrix2D,
+ PMatrix3D: PMatrix3D
+ }),
+
+ PShapeSVG = source.PShapeSVG({
+ CommonFunctions: CommonFunctions,
+ PConstants: PConstants,
+ PShape: PShape,
+ XMLElement: XMLElement,
+ colors: source.colors
+ }),
+
+ defaultScope = source.defaultScope({
+ ArrayList: ArrayList,
+ HashMap: HashMap,
+ PVector: PVector,
+ PFont: PFont,
+ PShapeSVG: PShapeSVG,
+ ObjectIterator: ObjectIterator,
+ PConstants: PConstants,
+ Char: Char,
+ XMLElement: XMLElement,
+ XML: XMLElement
+ }),
+
+ Processing = source.Processing({
+ defaultScope: defaultScope,
+ Browser: Browser,
+ extend: source.extend,
+ noop: noop
+ });
+
+ // set up the Processing syntax parser
+ Processing = source.setupParser(Processing, {
+ Browser: Browser,
+ aFunctions: testHarness,
+ defaultScope: defaultScope
+ });
+
+ // finalise the Processing object
+ Processing = source.finalize(Processing, {
+ version: require('../package.json').version,
+ isDomPresent: false || Browser.isDomPresent,
+ window: Browser.window,
+ document: Browser.document,
+ noop: noop
+ });
+
+ // done.
+ return Processing;
+};
+
+},{"../package.json":2,"./Helpers/ObjectIterator":3,"./Helpers/PConstants":4,"./Helpers/defaultScope":6,"./Helpers/finalizeProcessing":7,"./Helpers/virtEquals":8,"./Helpers/virtHashCode":9,"./Objects/ArrayList":10,"./Objects/Char":11,"./Objects/HashMap":12,"./Objects/PFont":13,"./Objects/PMatrix2D":14,"./Objects/PMatrix3D":15,"./Objects/PShape":16,"./Objects/PShapeSVG":17,"./Objects/PVector":18,"./Objects/XMLAttribute":19,"./Objects/XMLElement":20,"./Objects/webcolors":21,"./P5Functions/JavaProxyFunctions":22,"./P5Functions/Math.js":23,"./P5Functions/commonFunctions":24,"./P5Functions/touchmouse":25,"./Parser/Parser":26,"./Processing":27}]},{},[1]);
diff --git a/js/riffwave.js b/js/riffwave.js
new file mode 100644
index 0000000..3c0b5e4
--- /dev/null
+++ b/js/riffwave.js
@@ -0,0 +1,130 @@
+/*
+ * RIFFWAVE.js v0.03 - Audio encoder for HTML5 <audio> elements.
+ * Copyleft 2011 by Pedro Ladaria <pedro.ladaria at Gmail dot com>
+ *
+ * Public Domain
+ *
+ * Changelog:
+ *
+ * 0.01 - First release
+ * 0.02 - New faster base64 encoding
+ * 0.03 - Support for 16bit samples
+ *
+ * Notes:
+ *
+ * 8 bit data is unsigned: 0..255
+ * 16 bit data is signed: −32,768..32,767
+ *
+ */
+
+var FastBase64 = {
+
+ chars: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
+ encLookup: [],
+
+ Init: function() {
+ for (var i=0; i<4096; i++) {
+ this.encLookup[i] = this.chars[i >> 6] + this.chars[i & 0x3F];
+ }
+ },
+
+ Encode: function(src) {
+ var len = src.length;
+ var dst = '';
+ var i = 0;
+ while (len > 2) {
+ n = (src[i] << 16) | (src[i+1]<<8) | src[i+2];
+ dst+= this.encLookup[n >> 12] + this.encLookup[n & 0xFFF];
+ len-= 3;
+ i+= 3;
+ }
+ if (len > 0) {
+ var n1= (src[i] & 0xFC) >> 2;
+ var n2= (src[i] & 0x03) << 4;
+ if (len > 1) n2 |= (src[++i] & 0xF0) >> 4;
+ dst+= this.chars[n1];
+ dst+= this.chars[n2];
+ if (len == 2) {
+ var n3= (src[i++] & 0x0F) << 2;
+ n3 |= (src[i] & 0xC0) >> 6;
+ dst+= this.chars[n3];
+ }
+ if (len == 1) dst+= '=';
+ dst+= '=';
+ }
+ return dst;
+ } // end Encode
+
+}
+
+FastBase64.Init();
+
+var RIFFWAVE = function(data) {
+
+ this.data = []; // Array containing audio samples
+ this.wav = []; // Array containing the generated wave file
+ this.dataURI = ''; // http://en.wikipedia.org/wiki/Data_URI_scheme
+
+ this.header = { // OFFS SIZE NOTES
+ chunkId : [0x52,0x49,0x46,0x46], // 0 4 "RIFF" = 0x52494646
+ chunkSize : 0, // 4 4 36+SubChunk2Size = 4+(8+SubChunk1Size)+(8+SubChunk2Size)
+ format : [0x57,0x41,0x56,0x45], // 8 4 "WAVE" = 0x57415645
+ subChunk1Id : [0x66,0x6d,0x74,0x20], // 12 4 "fmt " = 0x666d7420
+ subChunk1Size: 16, // 16 4 16 for PCM
+ audioFormat : 1, // 20 2 PCM = 1
+ numChannels : 1, // 22 2 Mono = 1, Stereo = 2...
+ sampleRate : 8000, // 24 4 8000, 44100...
+ byteRate : 0, // 28 4 SampleRate*NumChannels*BitsPerSample/8
+ blockAlign : 0, // 32 2 NumChannels*BitsPerSample/8
+ bitsPerSample: 8, // 34 2 8 bits = 8, 16 bits = 16
+ subChunk2Id : [0x64,0x61,0x74,0x61], // 36 4 "data" = 0x64617461
+ subChunk2Size: 0 // 40 4 data size = NumSamples*NumChannels*BitsPerSample/8
+ };
+
+ function u32ToArray(i) {
+ return [i&0xFF, (i>>8)&0xFF, (i>>16)&0xFF, (i>>24)&0xFF];
+ }
+
+ function u16ToArray(i) {
+ return [i&0xFF, (i>>8)&0xFF];
+ }
+
+ function split16bitArray(data) {
+ var r = [];
+ var j = 0;
+ var len = data.length;
+ for (var i=0; i<len; i++) {
+ r[j++] = data[i] & 0xFF;
+ r[j++] = (data[i]>>8) & 0xFF;
+ }
+ return r;
+ }
+
+ this.Make = function(data) {
+ if (data instanceof Array) this.data = data;
+ this.header.blockAlign = (this.header.numChannels * this.header.bitsPerSample) >> 3;
+ this.header.byteRate = this.header.blockAlign * this.sampleRate;
+ this.header.subChunk2Size = this.data.length * (this.header.bitsPerSample >> 3);
+ this.header.chunkSize = 36 + this.header.subChunk2Size;
+
+ this.wav = this.header.chunkId.concat(
+ u32ToArray(this.header.chunkSize),
+ this.header.format,
+ this.header.subChunk1Id,
+ u32ToArray(this.header.subChunk1Size),
+ u16ToArray(this.header.audioFormat),
+ u16ToArray(this.header.numChannels),
+ u32ToArray(this.header.sampleRate),
+ u32ToArray(this.header.byteRate),
+ u16ToArray(this.header.blockAlign),
+ u16ToArray(this.header.bitsPerSample),
+ this.header.subChunk2Id,
+ u32ToArray(this.header.subChunk2Size),
+ (this.header.bitsPerSample == 16) ? split16bitArray(this.data) : this.data
+ );
+ this.dataURI = 'data:audio/wav;base64,'+FastBase64.Encode(this.wav);
+ };
+
+ if (data instanceof Array) this.Make(data);
+
+}; // end RIFFWAVE
diff --git a/js/shaperoller.js b/js/shaperoller.js
new file mode 100644
index 0000000..f0360d6
--- /dev/null
+++ b/js/shaperoller.js
@@ -0,0 +1,172 @@
+var CIRCLE = 0;
+var SQUARE_SHAPE = 1;
+var points = [];
+var currentAngle = 0;
+var drawing = false;
+var shapes = [CIRCLE, CIRCLE];
+var radii = [107, 17];
+var shapeNameList = ['circle', 'square'];
+var speed = 0.5;
+
+function removeElementById(id)
+{
+ (function(x){x.parentNode.removeChild(x);})(document.getElementById(id));
+}
+
+function getNumShapes()
+{
+ var i = 0;
+ do
+ {
+ var x = document.getElementById("rt"+i);
+ i++;
+ }
+ while (x != null);
+ return i-1;
+}
+
+function deleteShape(num)
+{
+ removeElementById("rt"+num);
+ removeElementById("st"+num);
+ removeElementById("shape"+num);
+ removeElementById("radius"+num);
+ removeElementById("del"+num);
+ removeElementById("br"+num);
+
+}
+
+function updateShapes()
+{
+ shapes = [];
+ radii = [];
+ var nShapes = getNumShapes();
+ for (var i = 0; i < nShapes; i++)
+ {
+ var sel = document.getElementById("shape" + i);
+ var val = sel.options[sel.selectedIndex].value;
+ shapes.push(shapeNameList.indexOf(val));
+ radii.push(document.getElementById("radius" + i).value);
+ }
+ speed = parseFloat(document.getElementById("speed").value);
+
+}
+
+function loadShapes()
+{
+ var nShapes = getNumShapes();
+ for (var i = 0; i < nShapes; i++)
+ {
+ var sel = document.getElementById("shape" + i);
+ sel.selectedIndex = shapes[i];
+ document.getElementById("radius" + i).value = radii[i];
+ }
+ document.getElementById("speed").value = speed;
+}
+
+function addShape()
+{
+ updateShapes();
+ var nShapes = getNumShapes();
+ var shapesDiv = document.getElementById("shapes");
+ shapesDiv.innerHTML += '<span id="rt' + nShapes + '">Radius:</span> <input type="number" value="50" id="radius' + nShapes + '"> <span id="st' + nShapes + '">Shape:</span> ' +
+ '<select id="shape' + nShapes + '"><option value="circle">Circle</option><option value="square">Square</option></select> <button id="del' + nShapes + '" onclick="deleteShape(' + nShapes + ');">Delete</button><br id="br' + nShapes + '">';
+ shapes.push(0);
+ radii.push(50);
+ loadShapes();
+}
+
+function startDrawing()
+{
+ drawing = true;
+ points = [];
+ updateShapes();
+}
+
+function squine(angle)
+{
+ angle %= TWO_PI;
+ if (angle >= QUARTER_PI && angle <= 3*QUARTER_PI)
+ return 1;
+ if (angle >= 5*QUARTER_PI && angle <= 7*QUARTER_PI)
+ return -1;
+ if (angle > 7*QUARTER_PI || angle < QUARTER_PI)
+ return map((angle+QUARTER_PI)%TWO_PI-QUARTER_PI, -QUARTER_PI, QUARTER_PI, -1, 1);
+ return -map(angle, 3*QUARTER_PI, 5*QUARTER_PI, -1, 1);
+}
+
+function squos(angle)
+{
+ if (angle >= 7*QUARTER_PI || angle <= QUARTER_PI)
+ return 1;
+ if (angle >= 3*QUARTER_PI && angle <= 5*QUARTER_PI)
+ return -1;
+ if (angle > QUARTER_PI && angle < 3*QUARTER_PI)
+ return -map(angle, QUARTER_PI, 3*QUARTER_PI, -1, 1);
+ return map(angle, 5*QUARTER_PI, 7*QUARTER_PI, -1, 1);
+
+}
+
+function getPointOnShape(shape, radius, centerX, centerY, angle)
+{
+ if (shape == SQUARE_SHAPE)
+ return [radius*squos(angle)+centerX, radius*squine(angle)+centerY];
+ else if (shape == CIRCLE)
+ return [radius*cos(angle)+centerX, radius*sin(angle)+centerY];
+}
+
+
+function setup()
+{
+ createCanvas(700, 700).parent("canvasSpot");
+ ellipseMode(CENTER);
+}
+
+function draw()
+{
+ updateShapes();
+ try{
+ if (drawing)
+ {
+ background(255);
+ stroke(0);
+ noFill();
+
+ var centerX = width/2;
+ var centerY = height/2;
+ var nextPoint;
+
+ for (var i = 0; i < shapes.length; i++)
+ {
+
+ var angle = map(currentAngle%radii[i], 0, radii[i], 0, TWO_PI);
+ nextPoint = getPointOnShape(shapes[i], radii[i], centerX, centerY, angle);
+ centerX = nextPoint[0];
+ centerY = nextPoint[1];
+
+ }
+
+
+ points.push([centerX, centerY]);
+
+
+
+ if (points.length != 1 && dist(points[0][0], points[0][1], points[points.length-1][0], points[points.length-1][1]) < speed)
+ drawing = false;
+
+
+ for (var i = 1; i < points.length; i++)
+ {
+ line(points[i-1][0], points[i-1][1], points[i][0], points[i][1]);
+ }
+
+
+ currentAngle += speed;
+ if (drawing)
+ {
+ noStroke();
+ fill(255, 0, 0);
+ ellipse(centerX, centerY, 7, 7);
+ }
+ }}catch(e){document.write(e + "<br>");}
+} \ No newline at end of file
diff --git a/js/tree.js b/js/tree.js
new file mode 100644
index 0000000..64cd2f3
--- /dev/null
+++ b/js/tree.js
@@ -0,0 +1,24 @@
+
+function draw_branch(x, y, t)
+{
+ if (t > 8)
+ return;
+ var sz = (400 * pow(0.5, t)) * map(255-frameCount%256, 0, 255, 0, 1);
+ var angle = map(mouseX, 0, width, 0, HALF_PI) + t * map(mouseY, 0, height, -HALF_PI, HALF_PI);
+ line(x, y, x+cos(angle)*sz, y-sin(angle)*sz);
+ line(x, y, x-cos(angle)*sz, y-sin(angle)*sz);
+ draw_branch((x+cos(angle)*sz), (y-sin(angle)*sz), t+1);
+ draw_branch((x-cos(angle)*sz), (y-sin(angle)*sz), t+1);
+}
+function setup()
+{
+
+ createCanvas(700, 700);
+ background(255);
+ draw_branch(width/2, height, 0);
+}
+function draw()
+{
+ background(255);
+ draw_branch(width/2, height, 0);
+}
diff --git a/js/treegenerator.js b/js/treegenerator.js
new file mode 100644
index 0000000..225c639
--- /dev/null
+++ b/js/treegenerator.js
@@ -0,0 +1,37 @@
+
+var size;
+var start_angle;
+var angle_decay;
+function draw_branch(x, y, t)
+{
+ if (t > 8)
+ return;
+ var sz = size * pow(0.5, t);
+ var angle = start_angle + angle_decay * t;
+ line(x, y, x+cos(angle)*sz, y-sin(angle)*sz);
+ line(x, y, x-cos(angle)*sz, y-sin(angle)*sz);
+ draw_branch(x+cos(angle)*sz, y-sin(angle)*sz, t+1);
+ draw_branch(x-cos(angle)*sz, y-sin(angle)*sz, t+1);
+
+}
+function setup()
+{
+
+ createCanvas(700, 700);
+ stroke(0);
+}
+
+function draw()
+{
+ size = parseFloat(document.getElementById("size").value);
+ start_angle = parseFloat(document.getElementById("angle").value);
+ angle_decay = parseFloat(document.getElementById("angledecay").value);
+ background(255);
+ draw_branch(width/2, height, 0);
+}
+
+function saveTree()
+{
+ save("tree.png");
+
+} \ No newline at end of file
diff --git a/magnets.html b/magnets.html
new file mode 100644
index 0000000..2fb7553
--- /dev/null
+++ b/magnets.html
@@ -0,0 +1,17 @@
+<html>
+<head>
+<title>Magnets</title>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+</head>
+<body>
+<h2>Magnets</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+
+<p>You can get the Android app for Magnets <a href="https://play.google.com/store/apps/details?id=org.neocities.autoart.magnets">here</a>.</p>
+<script src="js/p5.js"></script>
+<script src="js/magnets.js"></script>
+</body>
+</html>
diff --git a/magnets_old_version.html b/magnets_old_version.html
new file mode 100644
index 0000000..ad12ce1
--- /dev/null
+++ b/magnets_old_version.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+<title>Magnets</title>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+</head>
+<body>
+<h2>Magnets</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+<script src="js/p5.js"></script>
+<script src="js/magnets_old_version.js"></script>
+</body>
+</html>
diff --git a/mandelbrot.html b/mandelbrot.html
new file mode 100644
index 0000000..caab5a7
--- /dev/null
+++ b/mandelbrot.html
@@ -0,0 +1,20 @@
+<html>
+<head>
+<title>The Mandelbrot Set</title>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+
+</head>
+<body>
+<h2>Mandelbrot Set</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+
+<p>The Mandelbrot Set is a <a target="_blank" href="https://en.wikipedia.org/wiki/Fractal">fractal</a>. An explanation of how it works can be found <a href="mandelbrot_explanation.html">here</a>. Click on a spot to zoom in on that spot or press A to zoom in and
+Q to zoom out. Press P to increase the power, and L to decrease it. Press I to increase the maximum number of iterations and K to decrease it.<br>
+<b>Every time you zoom in or out, change the power, or change the maximum number of iterations it will take a bit of time to load.</b></p>
+<canvas width=500 height=500 id="canvas"></canvas>
+<script src="js/mandelbrot.js"></script>
+</body>
+</html>
diff --git a/mandelbrot_explanation.html b/mandelbrot_explanation.html
new file mode 100644
index 0000000..3a59639
--- /dev/null
+++ b/mandelbrot_explanation.html
@@ -0,0 +1,72 @@
+<html>
+<head>
+<script src="js/latexit.js"></script>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+
+<title>Mandelbrot Set Explanation</title>
+</head>
+
+<body>
+
+<h2>Explanation of the Mandelbrot Set</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+
+Consider the function
+<div lang="latex">
+f_c(z) = z^2+c\\
+</div><br>
+Where z and c are complex numbers. Complex numbers are numbers in the form of
+<div lang="latex">
+\\
+ai+b\\
+$Where $i=\sqrt{-1}
+</div>
+<br>
+Now let's check if 0.5 is in the Mandelbrot Set. To do so, start at 0
+<div lang="latex">
+\\
+f_{0.5}(0) = 0^2 + 0.5 = 0.5\\
+f_{0.5}(0.5) = 0.5^2 + 0.5 = 0.75\\
+f_{0.5}(0.75) = 1.0625\\
+f_{0.5}(1.0625) = 1.62890625\\
+f_{0.5}(1.62890625) = 3.15333557\\
+</div>
+<br>
+It can be proven that if the function passes 2, it will go to infinity if you continually apply the function.
+Since this function has passed 2, 0.5 is not in the Mandelbrot Set. Compare this to 0.25.
+
+<div lang="latex">
+\\
+f_{0.25}(0) = 0^2+0.25 = 0.25\\
+f_{0.25}(0.25) = 0.3125\\
+f_{0.25}(0.3125) = 0.34765625\\
+f_{0.25}(0.34765625) = 0.370864868\\
+f_{0.25}(0.370864868) = 0.38754075\\
+f_{0.25}(0.38754075) = 0.400187833\\
+f_{0.25}(0.400187833) = 0.410150302\\
+f_{0.25}(0.410150) = 0.418223\\
+f_{0.25}(0.418223) = 0.424911\\
+f_{0.25}(0.424911) = 0.430549\\
+f_{0.25}(0.430549) = 0.435373\\
+f_{0.25}(0.435373) = 0.439549\\
+f_{0.25}(0.439549) = 0.443204\\
+f_{0.25}(0.443204) = 0.446429\\
+f_{0.25}(0.446429) = 0.449299\\
+</div>
+<br>
+This will never pass 2, so 0.25 is in the Mandelbrot Set.
+<br>
+This process can also be done to complex numbers.<br>
+<br>
+<div lang="latex">
+M(x) =$ the number of iterations required for $f_x$ to pass 2.$
+</div>
+
+The website is just a 2d plot of M(x).
+
+</body>
+
+</html>
diff --git a/mathematical.html b/mathematical.html
new file mode 100644
index 0000000..640d3ac
--- /dev/null
+++ b/mathematical.html
@@ -0,0 +1,120 @@
+<html>
+<head>
+<title>Mathematical Demonstrations</title>
+<meta charset="utf-8">
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+<link rel="shortcut icon" type="image/png" href="favicon.png">
+</head>
+
+<body>
+<h2>Mathematical Demonstrations</h2>
+<a class="header_link" href="index.html">Home</a>
+<a class="header_link" href="all.html">All</a>
+<a class="header_link" href="games.html">Games</a>
+<a class="header_link" href="apps.html">Android Apps</a>
+<hr>
+
+<img src="screenshots/modularpascal.png" width=256 height=256><br>
+<a href="modularpascal.html">Modular Pascal's Triangle</a><br>
+<p>
+A picture of Pascal's triangle mod x. Set x and<br>
+Click update to see the triangle. </p>
+<hr>
+
+<img src="screenshots/modularcircles.png" width=250 height=250><br>
+<a href="modularcircles.html">Modular Circles</a><br>
+<p>
+There are a circle of points, each one corresponding<br>
+to a number. Lines are drawn between points according<br>
+to rules. Create interesting shapes like cardioids<br>
+</p>
+<hr>
+
+<img src="screenshots/shaperoller.png" width=250 height=250><br>
+<a href="shaperoller.html">Shape Roller</a><br>
+<p>
+This program rolls (invisible) shapes around other <br>
+(invisible) shapes and traces a point on the shape as<br>
+it moves along. Create interesting shapes like cardioids.<br>
+</p>
+<hr>
+
+<img src="screenshots/mandelbrot.png" width=250 height=250><br>
+<a href="mandelbrot.html">The Mandelbrot Set</a><br>
+<p>
+This program draws the Mandelbrot Set. You can<br>
+zoom in and change the power.<br>
+</p>
+<hr>
+
+<img src="screenshots/ant.png" width=250 height=250><br>
+<a href="ant.html">Langton's Ant</a><br>
+<p>
+This is the cellular automaton known as Langton's<br>
+Ant. You can find out more about it <a href="https://en.wikipedia.org/wiki/Langton%27s_ant">here</a>.
+</p>
+<hr>
+
+<img src="screenshots/mazesolver.png" width=200 height=200><br>
+<a href="mazesolver.html">Maze Solver</a><br>
+<p>
+Create a maze, choose a starting point and an<br>
+ending point, and your computer will solve the<br>
+maze.
+</p>
+<hr>
+
+
+<img src="screenshots/AutoImages.png" width=200 height=200><br>
+<a href="AutoImages.html">AutoImages</a><br>
+<p>
+A computer program that creates art. It uses<br>
+random functions to generate images. An explanation<br>
+can be found <a href="explanation.html">here</a>.<br>
+There is also AutoArtEvolve, which improves its<br>
+images based on the ones you prefer:<br>
+<iframe frameborder="0" src="https://itch.io/embed/43294?linkback=true&amp;bg_color=fafafa&amp;fg_color=000000&amp;link_color=5ca4fa&amp;border_color=9f9f9f" width="552" height="167"></iframe>
+</p>
+<hr>
+
+<video width="250" height="250" controls>
+ <source src="autovideo.mp4" type="video/mp4">
+</video><br>
+<a href="AutoVideos.html">AutoVideos</a><br>
+<p>
+A computer program that creates videos. It uses<br>
+random functions to generate videos. An explanation<br>
+can be found <a href="explanation.html">here</a>.<br>
+</p>
+<hr>
+
+<audio controls>
+ <source src="autoaudio.wav" type="audio/wav">
+</audio><br>
+<a href="AutoAudio.html">AutoAudio</a><br>
+<p>
+A computer program that creates audio. It uses<br>
+random functions to generate audio. An explanation<br>
+can be found <a href="explanation.html">here</a>.<br>
+</p>
+<hr>
+
+
+<img src="screenshots/NameGenerator.png" width=200 height=200><br>
+<a href="NameGenerator.html">NameGenerator</a><br>
+<p>
+A computer program that generates names! It uses<br>
+the frequencies of trigrams.<br>
+</p>
+<hr>
+
+<img src="screenshots/picalculator.png" width=250 height=250><br>
+<a href="2pi.html">π Calculator</a><br>
+<p>
+Draw a circle, then find out how well your circle<br>
+approximates π!<br>
+</p>
+
+
+</body> \ No newline at end of file
diff --git a/mazesolver.html b/mazesolver.html
new file mode 100644
index 0000000..e62fcbb
--- /dev/null
+++ b/mazesolver.html
@@ -0,0 +1,39 @@
+<html>
+
+<head>
+<link href="css/bootstrap.min.css" rel="stylesheet">
+<link href="css/style.css" rel="stylesheet">
+<title>Maze Solver</title>
+</head>
+
+<body>
+
+<h2>Maze Solver</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+
+<p>Maze Solver is a computer program that can solve mazes.<br>
+It works using <a href='https://www.khanacademy.org/computing/computer-science/algorithms/intro-to-algorithms/a/route-finding'>this algorithm</a>. To create a
+maze, just left-click and drag on the screen below to create walls, middle-click to clear walls, and right-click to place start and ending locations. Then,
+click the button saying "Solve Maze" and it will draw red circles connecting your start location to your end location.</p>
+
+<button onclick='startCreation();' id='StartButton'>Create Maze</button><br><br>
+
+<p id='DoesItWork' style="color:#ff0000;"></p>
+
+<form id='Form'>
+Size: <input type='number' value=50>
+</form>
+
+<canvas height=0 width=0 id='Canvas' oncontextmenu='return false;'></canvas>
+
+<script src="js/mazesolver.js"></script>
+
+
+</body>
+<footer>
+<p>MazeSolver is licensed under the <a href='LICENSE.txt'>GNU General public license</a>.</p>
+</footer>
+
+</html>
diff --git a/modularcircles.html b/modularcircles.html
new file mode 100644
index 0000000..0267b03
--- /dev/null
+++ b/modularcircles.html
@@ -0,0 +1,22 @@
+<html>
+<head>
+<title>Modular circles</title>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+<script src="js/p5.js"></script>
+</head>
+<body>
+<h2>Modular circles</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+
+<p>Each point corresponds to a number. The points are connected based on rules.</p>
+
+<script src="js/modularcircles.js"></script>
+Number of points: <input min="1" value="300" type="number" id="npoints"><br>
+Add/Multiply: <input value="2" step="0.2" type="number" id="amount"><br>
+Multiply? <input id="should_mul" type="radio" value="multiply" name="muloradd" checked="true"><br>
+Add? <input id="should_add" type="radio" value="add" name="muloradd"><br>
+</body>
+</html> \ No newline at end of file
diff --git a/modularpascal.html b/modularpascal.html
new file mode 100644
index 0000000..62c05bc
--- /dev/null
+++ b/modularpascal.html
@@ -0,0 +1,22 @@
+<html>
+<head>
+<title>Modular pascal</title>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+
+<script src="js/p5.js"></script>
+
+</head>
+<body>
+<h2>Modular Pascal's Triangle</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+<p>A picture of Pascal's triangle mod x. This program might be a bit slow.</p>
+
+
+<script src="js/modularpascal.js"></script>
+x: <input min="1" value="2" type="number" id="mod"><br>
+<button onclick="updateTriangle();">Update</button><br>
+</body>
+</html> \ No newline at end of file
diff --git a/screenshots/2d3d.png b/screenshots/2d3d.png
new file mode 100644
index 0000000..12acc3f
--- /dev/null
+++ b/screenshots/2d3d.png
Binary files differ
diff --git a/screenshots/2d3dcustom.png b/screenshots/2d3dcustom.png
new file mode 100644
index 0000000..b461bf4
--- /dev/null
+++ b/screenshots/2d3dcustom.png
Binary files differ
diff --git a/screenshots/AutoHarmonograph.png b/screenshots/AutoHarmonograph.png
new file mode 100644
index 0000000..1847cbf
--- /dev/null
+++ b/screenshots/AutoHarmonograph.png
Binary files differ
diff --git a/screenshots/AutoImages.png b/screenshots/AutoImages.png
new file mode 100644
index 0000000..922706d
--- /dev/null
+++ b/screenshots/AutoImages.png
Binary files differ
diff --git a/screenshots/NameGenerator.png b/screenshots/NameGenerator.png
new file mode 100644
index 0000000..d658125
--- /dev/null
+++ b/screenshots/NameGenerator.png
Binary files differ
diff --git a/screenshots/ant.png b/screenshots/ant.png
new file mode 100644
index 0000000..1c387fe
--- /dev/null
+++ b/screenshots/ant.png
Binary files differ
diff --git a/screenshots/autoartapp.png b/screenshots/autoartapp.png
new file mode 100644
index 0000000..257baae
--- /dev/null
+++ b/screenshots/autoartapp.png
Binary files differ
diff --git a/screenshots/ballbounce.png b/screenshots/ballbounce.png
new file mode 100644
index 0000000..7351619
--- /dev/null
+++ b/screenshots/ballbounce.png
Binary files differ
diff --git a/screenshots/ballbounceapp.png b/screenshots/ballbounceapp.png
new file mode 100644
index 0000000..8173929
--- /dev/null
+++ b/screenshots/ballbounceapp.png
Binary files differ
diff --git a/screenshots/clock.png b/screenshots/clock.png
new file mode 100644
index 0000000..aca13b9
--- /dev/null
+++ b/screenshots/clock.png
Binary files differ
diff --git a/screenshots/h.png b/screenshots/h.png
new file mode 100644
index 0000000..3667103
--- /dev/null
+++ b/screenshots/h.png
Binary files differ
diff --git a/screenshots/magnets.png b/screenshots/magnets.png
new file mode 100644
index 0000000..a6562e6
--- /dev/null
+++ b/screenshots/magnets.png
Binary files differ
diff --git a/screenshots/magnetsapp.png b/screenshots/magnetsapp.png
new file mode 100644
index 0000000..a6e77e8
--- /dev/null
+++ b/screenshots/magnetsapp.png
Binary files differ
diff --git a/screenshots/mandelbrot.png b/screenshots/mandelbrot.png
new file mode 100644
index 0000000..1b943b4
--- /dev/null
+++ b/screenshots/mandelbrot.png
Binary files differ
diff --git a/screenshots/mazesolver.png b/screenshots/mazesolver.png
new file mode 100644
index 0000000..453e184
--- /dev/null
+++ b/screenshots/mazesolver.png
Binary files differ
diff --git a/screenshots/modularcircles.png b/screenshots/modularcircles.png
new file mode 100644
index 0000000..61b7638
--- /dev/null
+++ b/screenshots/modularcircles.png
Binary files differ
diff --git a/screenshots/modularpascal.png b/screenshots/modularpascal.png
new file mode 100644
index 0000000..c53c65c
--- /dev/null
+++ b/screenshots/modularpascal.png
Binary files differ
diff --git a/screenshots/picalculator.png b/screenshots/picalculator.png
new file mode 100644
index 0000000..c5b122c
--- /dev/null
+++ b/screenshots/picalculator.png
Binary files differ
diff --git a/screenshots/shaperoller.png b/screenshots/shaperoller.png
new file mode 100644
index 0000000..1a96c91
--- /dev/null
+++ b/screenshots/shaperoller.png
Binary files differ
diff --git a/screenshots/tree.png b/screenshots/tree.png
new file mode 100644
index 0000000..cc4a93e
--- /dev/null
+++ b/screenshots/tree.png
Binary files differ
diff --git a/shaperoller.html b/shaperoller.html
new file mode 100644
index 0000000..3b8332c
--- /dev/null
+++ b/shaperoller.html
@@ -0,0 +1,55 @@
+<html>
+<head>
+<title>Shape roller</title>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+
+</head>
+<body>
+<h2>Shape Roller</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+<p>This program rolls (invisible) shapes around other (invisible) shapes and traces a point on the shape as it moves along.<br>
+Here's an animation of how it works (from Wikimedia):<br> <img width=100 height=100 src="https://upload.wikimedia.org/wikipedia/commons/d/d0/Cardiod_animation.gif" target="_blank"></p>
+
+
+<script src="js/p5.js"></script>
+<script src="js/shaperoller.js"></script>
+
+Speed: <input type="number" id="speed" value="0.5"><br>
+<p>Quality will be lost at higher speeds</p>
+
+<div id="shapes">
+
+<span id="rt0">Radius:</span>
+<input type="number" value="100" id="radius0">
+<span id="st0">Shape:</span>
+<select id="shape0">
+<option value="circle">Circle</option>
+<option value="square">Square</option>
+</select>
+<button id="del0" onclick="deleteShape(0);">Delete</button><br id="br0">
+
+<span id="rt1">Radius:</span>
+<input type="number" value="50" id="radius1">
+<span id="st1">Shape:</span>
+<select id="shape1">
+<option value="circle">Circle</option>
+<option value="square">Square</option>
+</select>
+<button id="del1" onclick="deleteShape(1);">Delete</button><br id="br1">
+
+
+</div>
+
+<button onclick="addShape();">Add shape</button><br>
+<button onclick="startDrawing();">Start!</button>
+
+
+<div id="canvasSpot"></div>
+
+</body>
+
+
+</html> \ No newline at end of file
diff --git a/tree.html b/tree.html
new file mode 100644
index 0000000..a8301ee
--- /dev/null
+++ b/tree.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+<title>Tree</title>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+</head>
+<body>
+<h2>Tree</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+<script src="js/p5.js"></script>
+<script src="js/tree.js"></script>
+</body>
+</html> \ No newline at end of file
diff --git a/treegenerator.html b/treegenerator.html
new file mode 100644
index 0000000..8ef3a01
--- /dev/null
+++ b/treegenerator.html
@@ -0,0 +1,25 @@
+<html>
+<head>
+<title>Tree Generator</title>
+<link rel="stylesheet" href="css/bootstrap.min.css">
+<link rel="stylesheet" href="css/style.css">
+</head>
+<body>
+<h2>Tree Generator</h2>
+<div id="header_links_div"></div>
+<script src="js/header_links.js"></script>
+<hr>
+
+Angle:
+<input type="number" id="angle" min="0" step="0.01" value="1">
+<br>
+Angle decay:
+<input type="number" id="angledecay" min="0" step="0.01" value="0.1">
+<br>
+Size:
+<input type="number" id="size" min="0" max="700" value="400"><br>
+<button onclick="saveTree();">Save image</button><br><br>
+<script src="js/p5.js"></script>
+<script src="js/treegenerator.js"></script>
+</body>
+</html> \ No newline at end of file