diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1 @@ +{} diff --git a/error/403.html b/error/403.html index 38a15fa..f1e2843 100755 --- a/error/403.html +++ b/error/403.html @@ -1,22 +1,22 @@ + + + + + 403 Error + + + - - - - - 403 Error - - - + + + + diff --git a/error/404.html b/error/404.html index 8712ccf..4e8d33e 100755 --- a/error/404.html +++ b/error/404.html @@ -1,22 +1,20 @@ + + + + + 404 Error + + + - - - - - 404 Error - - - + + + + diff --git a/error/413.html b/error/413.html index 293f796..c6bb92b 100755 --- a/error/413.html +++ b/error/413.html @@ -1,22 +1,20 @@ + + + + + 413 Error + + + - - - - - 413 Error - - - + + + + diff --git a/error/5xx.html b/error/5xx.html index 5676d25..bb706a9 100755 --- a/error/5xx.html +++ b/error/5xx.html @@ -1,22 +1,22 @@ + + + + + 5XX Error + + + - - - - - 5XX Error - - - - - - - + + + diff --git a/index.html b/index.html index 971c6a6..dcd08d9 100755 --- a/index.html +++ b/index.html @@ -1,44 +1,46 @@ + + + + + Array in a Matrix + + + + - - - - - Array in a Matrix - - - - + + +
+ + +
- - -
- - -
- - - - - - \ No newline at end of file + + + diff --git a/index/about/index.html b/index/about/index.html index 2f92f26..73ae495 100755 --- a/index/about/index.html +++ b/index/about/index.html @@ -1,95 +1,115 @@ + + + + + About Me + + + + - - - - - About Me - - - + + +
+

About Me

+ My name is Array. My hobbies include skateboarding, biking, surfing the + web, play video games (including emulation) and programming. I tinker a + lot with electronics and tend to jailbreak and homebrew them whenever + possible. I use + Arch Linux btw . I created + this website + and constantly update it with new content (come back later!). I set up, + configured, host, and operate this website, and all of its related + services. -
+
+
-

About Me

- My name is Array. My hobbies include skateboarding, biking, surfing the web, play - video games (including emulation) and programming. I tinker a lot - with electronics and tend to jailbreak and homebrew them whenever possible. I use Arch Linux btw . I created this website and constantly update it with new content (come back later!). I set up, configured, host, and operate this website, and all of its related services. + My website is split into a couple sections: -
-
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SectionSection topic
About MeOverview of this domain and its owner.
BannersA list of banners.
Blog + My personal blog where I info dump and take forever to post. +
ChatChat and social services are I host.
GamesGames that I programmed or implemented.
RepositoryPackaged applications I coded.
SocialsMy social media profiles.
Services + Applications that I host but not necessary coded (Most useful + imo). +
ProjectsThings I created or contribute to.
Webrings + Links to other people's websites which may or may not be similar. +
- My website is split into a couple sections: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SectionSection topic
About MeOverview of this domain and its owner.
BannersA list of banners.
BlogMy personal blog where I info dump and take forever to post.
ChatChat and social services are I host.
GamesGames that I programmed or implemented.
RepositoryPackaged applications I coded.
SocialsMy social media profiles.
ServicesApplications that I host but not necessary coded (Most useful imo).
ProjectsThings I created or contribute to.
WebringsLinks to other people's websites which may or may not be similar.
+
+
+ You can view my desktop rice here. -
-
- - You can view my desktop rice here. - - -
- You have reached the end of the page. (ノ◕ヮ◕)ノ*:・゚✧ -
-
- - - \ No newline at end of file + +
+ + diff --git a/index/about/rice/index.html b/index/about/rice/index.html index 3d381a6..bfdfb9e 100755 --- a/index/about/rice/index.html +++ b/index/about/rice/index.html @@ -1,51 +1,62 @@ - - + - - + + About Me - - + + - +
+

+ What is Ricing? +

+ Ricing is just customizing your operating system to look a certain way or + follow a theme. You can see other people's desktop rices + here. My dotfiles are + here. -

What is Ricing?

- Ricing is just customizing your operating system to look a certain way or follow a theme. You can see other - people's desktop rices here. My dotfiles are here. +

My system looks like this:

+
+ KDE desktop on Arch Linux. +
+

And looks like this:

+
+ 3 images of the Sway window manager (using pywal) on Arch Linux. +
-

My system looks like this:

-
- KDE desktop on Arch Linux. -
- -

And looks like this:

-
- 3 images of the Sway window manager (using pywal) on Arch Linux. -
- - - This is a two monitor setup. - - + This is a two monitor setup. +
- - + diff --git a/index/apps/admin/index.html b/index/apps/admin/index.html index c22a40b..c91e623 100755 --- a/index/apps/admin/index.html +++ b/index/apps/admin/index.html @@ -1,31 +1,37 @@ - - + - - + + Administration - - - + + + - +
- - + +
- - - \ No newline at end of file + + diff --git a/index/apps/cbox/index.html b/index/apps/cbox/index.html index 252bb9e..e5cce43 100755 --- a/index/apps/cbox/index.html +++ b/index/apps/cbox/index.html @@ -1,35 +1,44 @@ + + + + + Cbox + + + - - - - - Cbox - - - + + +
+ -
- - - -
- You have reached the end of the page. (ノ◕ヮ◕)ノ*:・゚✧ -
-
- - - \ No newline at end of file + +
+ + diff --git a/index/apps/index.html b/index/apps/index.html index 63dc1df..9e27021 100755 --- a/index/apps/index.html +++ b/index/apps/index.html @@ -1,120 +1,137 @@ + + + + + Apps and Services + + + - - - - - Apps and Services - - - + + +
+

+ Note: Chat and social applications have been moved to + chatinamatrix.xyz. +

-
+
+

Matrix homeserver

+ + A decentralized End-to-End encrypted and federated chat server. Matrix + web clients can be found here. + +
-

Note: Chat and social applications have been moved to chatinamatrix.xyz.

+
-
-

Matrix homeserver

- - A decentralized End-to-End encrypted and federated chat server. Matrix web clients can be found here. - -
+
+

+ Cbox +

+ + Anonymous real-time live chat app. Come leave a quick message! + +
-
+
-
-

Cbox

- - Anonymous real-time live chat app. Come leave a quick message! - -
+
+

Gemini capsule

+ + This is my Gemini capsule, you can learn more about it + here. + (Requires Gemini compatible browser.) + +
-
+
-
-

Gemini capsule

- - This is my Gemini capsule, you can learn more about it here. (Requires Gemini compatible browser.) - -
+
+

+ Wiki archives +

+ + Wiki mirrors hosted using Kiwix. Come + browse Wikipedia, the Arch wiki and more! + +
-
+
-
-

Wiki archives

- - Wiki mirrors hosted using Kiwix. Come browse Wikipedia, the Arch wiki and more! - -
+
+

Tor hidden services

+ + List of all of my Tor hidden services. Use Tor to bypass censorship! + (Requires Tor compatible browser.) + +
-
+
-
-

Tor hidden services

- - List of all of my Tor hidden services. Use Tor to bypass censorship! (Requires Tor compatible browser.) - -
+
+

+ Gitea +

+ + Lightweight open sourced code hosting. (GitHub replacement!) + +
-
+
-
-

Gitea

- - Lightweight open sourced code hosting. (GitHub replacement!) - -
+
+

+ Share +

+ + Lightweight and minimal temporary file sharing. You can share files + using the browser or directly from the terminal! + +
-
+
+
+
-
-

Share

- - Lightweight and minimal temporary file sharing. You can share files using the browser or directly from the terminal! - -
+
+

Privacy Policy

+ Privacy policy of this website and its related services. +
-
-
-
+
-
-

Privacy Policy

- - Privacy policy of this website and its related services. - -
+
+

Administration tools

+ Server statistics and management tools. +
-
- -
-

Administration tools

- - Server statistics and management tools. - -
- - -
- You have reached the end of the page. (ノ◕ヮ◕)ノ*:・゚✧ -
-
- - + +
+ diff --git a/index/apps/privacy/index.html b/index/apps/privacy/index.html index 4e74092..1fed3b3 100755 --- a/index/apps/privacy/index.html +++ b/index/apps/privacy/index.html @@ -1,67 +1,97 @@ - - + Privacy Policy - - - + + + - +
-

Privacy Policy

-
-

arrayinamatrix.xyz & bloginamatrix.xyz

-
last updated: 22/11/2022
- - We do not collect any personal data or any information that can be used to identify you. IP addresses are logged for a maximum of one day. If you want your IP address removed from the IP Grabber, just contact us and we will purge the list. We record server analytics and statistics such as generic system information (CPU, memory usage, etc.). Pages that require some client side JavaScript code are denoted by the smash product symbol "⨳", external websites are not included. +

Privacy Policy

+
+

+ arrayinamatrix.xyz & + bloginamatrix.xyz +

+
last updated: 22/11/2022
+ + We do not collect any personal data or any information that can be + used to identify you. IP addresses are logged for a maximum of one + day. If you want your IP address removed from the + IP Grabber, just + contact us and we will purge the list. We + record server analytics and statistics such as generic system + information (CPU, memory usage, etc.). Pages that require some client + side JavaScript code are denoted by the smash product symbol "⨳", + external websites are not included. -
-
-
-
-

chatinamatrix.xyz

-
last updated: 22/11/2022
- - Server analytics and statistics are only used to improve and monitor the health of the Dendrite server. We use Google's ReCAPTCHA to reduce automated spam bots. Access logs (include Matrix federation) are purged daily. We only require a password and username to register on our Dendrite chat server, all other data (email address, phone number, etc.) are optional. Messages deleted from this homeserver will not, and can not, guarantee their removal from federated homeservers. Anyone, including server admins, can see all unencrypted data! -
-
- Rules for our matrix homeserver can be found here. +
+
+
+
+

chatinamatrix.xyz

+
last updated: 22/11/2022
+ + Server analytics and statistics are only used to improve and monitor + the health of the Dendrite server. We use Google's ReCAPTCHA to reduce + automated spam bots. Access logs (include Matrix federation) are + purged daily. We only require a password and username to register on + our Dendrite chat server, all other data (email address, phone number, + etc.) are optional. Messages deleted from this homeserver will not, + and can not, guarantee their removal from federated homeservers. + Anyone, including server admins, can see all unencrypted + data! +
+
+ Rules for our matrix homeserver can be found + here.
-
-
-
-
-

Note:

-
last updated: 22/11/2022
- - We may update or change our privacy policy without warning. All of our services are provided on a As-Is basis without any warranties. We will NOT notify you of any changes to the privacy policy. - -
-
-
-
- For additional information or support please email us at - tensor@arrayinamatrix.xyz - or message us on one of these platforms. -
-
- You have reached the end of the page. (ノ◕ヮ◕)ノ*:・゚✧ -
+
+
+
+
+

Note:

+
last updated: 22/11/2022
+ + We may update or change our privacy policy without warning. All of our + services are provided on a As-Is basis without any + warranties. We will NOT notify you of any changes to + the privacy policy. + +
+
+
+
+ For additional information or support please email us at + tensor@arrayinamatrix.xyz + or message us on one of these platforms. +
+
- - - \ No newline at end of file + + diff --git a/index/apps/tor/index.html b/index/apps/tor/index.html index 89ffb7c..d52e6d5 100644 --- a/index/apps/tor/index.html +++ b/index/apps/tor/index.html @@ -1,88 +1,105 @@ - - + - - + + Tor - - + + - +
-
-

arrayinamatrix.xyz -

- - 7o6hlu5tkjsi6z7thygpgqpc7pvb2qamjtaxkvceezcpj236stq25vqd.onion - -
+
+

+ arrayinamatrix.xyz +

+ + 7o6hlu5tkjsi6z7thygpgqpc7pvb2qamjtaxkvceezcpj236stq25vqd.onion + +
-
+
-
-

- git.arrayinamatrix.xyz -

- - srr5udc7cotv4qbwiwcbatj3yciq2doorz4llmgoie7nfnwsgxy2ivid.onion - -
+
+

+ git.arrayinamatrix.xyz +

+ + srr5udc7cotv4qbwiwcbatj3yciq2doorz4llmgoie7nfnwsgxy2ivid.onion + +
-
+
-
-

- wiki.arrayinamatrix.xyz -

- - wqram5hvegtb7ib72y73paou2jkzn7iqqrb2tp5eadckbjm6ekrwucqd.onion - -
+
+

+ wiki.arrayinamatrix.xyz +

+ + wqram5hvegtb7ib72y73paou2jkzn7iqqrb2tp5eadckbjm6ekrwucqd.onion + +
-
+
-
-

- bloginamatrix.xyz -

- - xyfrg77bbix6ksxs62rausbyvjluwqhh5o3defg5hz4uketxpfqcneyd.onion - -
+
+

+ bloginamatrix.xyz +

+ + xyfrg77bbix6ksxs62rausbyvjluwqhh5o3defg5hz4uketxpfqcneyd.onion + +
-
+
-
-

- chatinamatrix.xyz -

- - rw3lqcaf4so66ecbpgreedaes7t2eyh2legwvjggtdnlw2fb2n3svfyd.onion - -
+
+

+ chatinamatrix.xyz +

+ + rw3lqcaf4so66ecbpgreedaes7t2eyh2legwvjggtdnlw2fb2n3svfyd.onion + +
- - -
- You have reached the end of the page. (ノ◕ヮ◕)ノ*:・゚✧ -
+
+ You have reached the end of the page. (ノ◕ヮ◕)ノ*:・゚✧ +
- - - \ No newline at end of file + + diff --git a/index/banners/index.html b/index/banners/index.html index ff6fdf2..c7d7fce 100755 --- a/index/banners/index.html +++ b/index/banners/index.html @@ -1,141 +1,284 @@ - - + - - + + Banners - + - - + + - +
-

What is a Banner?

- +

What is a Banner?

+ -
+
-
-

Banners:

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + -
-
+ + -
+
-
-

My banner:

- You can use these banners to link back to my site: +
+

My banner:

+ You can use these banners to link back to my site: -
+
-
- - - -
-
<a href="https://arrayinamatrix.xyz"><img src="https://arrayinamatrix.xyz/res/site/banners/custom/white_88x31.png"></a>
+        
+ + + +
+
<a href="https://arrayinamatrix.xyz"><img src="https://arrayinamatrix.xyz/res/site/banners/custom/white_88x31.png"></a>
 <a href="https://arrayinamatrix.xyz"><img src="https://arrayinamatrix.xyz/res/site/banners/custom/white_176x62.png"></a>
 <a href="https://arrayinamatrix.xyz"><img src="https://arrayinamatrix.xyz/res/site/banners/custom/white_264x93.png"></a>
-
-
- - - -
-
<a href="https://arrayinamatrix.xyz"><img src="https://arrayinamatrix.xyz/res/site/banners/custom/black_88x31.gif"></a>
+        
+
+ + + +
+
<a href="https://arrayinamatrix.xyz"><img src="https://arrayinamatrix.xyz/res/site/banners/custom/black_88x31.gif"></a>
 <a href="https://arrayinamatrix.xyz"><img src="https://arrayinamatrix.xyz/res/site/banners/custom/black_176x62.gif"></a>
 <a href="https://arrayinamatrix.xyz"><img src="https://arrayinamatrix.xyz/res/site/banners/custom/black_264x93.gif"></a>
-
+
- Using MathJax: - -
- \begin{bmatrix} - \\ - & Array & in & a & Matrix & \\ - \\ - \end{bmatrix} -
-
-
-
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
+        Using MathJax:
+        
+          
+ \begin{bmatrix} \\ & Array & in & a & Matrix & \\ \\ \end{bmatrix} +
+
+
+
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
 <script id="MathJax-script" async="" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
 <a href="https://arrayinamatrix.xyz/">
     <div>
@@ -146,12 +289,11 @@
         \end{bmatrix}
     </div>
 </a>
-
+
-
- You have reached the end of the page. (ノ◕ヮ◕)ノ*:・゚✧ -
+
+ You have reached the end of the page. (ノ◕ヮ◕)ノ*:・゚✧ +
- - - \ No newline at end of file + + diff --git a/index/games/fractal/script/starter-template.js b/index/games/fractal/script/starter-template.js index 746c56e..1c740d9 100755 --- a/index/games/fractal/script/starter-template.js +++ b/index/games/fractal/script/starter-template.js @@ -1,13 +1,12 @@ function saveCanvas() { saveCanvasButton.download = "image.png"; - saveCanvasButton.href = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream"); + saveCanvasButton.href = canvas + .toDataURL("image/png") + .replace("image/png", "image/octet-stream"); } function goFullScreen() { - if (canvas.requestFullScreen) - canvas.requestFullScreen(); - else if (canvas.webkitRequestFullScreen) - canvas.webkitRequestFullScreen(); - else if (canvas.mozRequestFullScreen) - canvas.mozRequestFullScreen(); -} \ No newline at end of file + if (canvas.requestFullScreen) canvas.requestFullScreen(); + else if (canvas.webkitRequestFullScreen) canvas.webkitRequestFullScreen(); + else if (canvas.mozRequestFullScreen) canvas.mozRequestFullScreen(); +} diff --git a/index/games/fractal/script/xaos.js b/index/games/fractal/script/xaos.js index 78a1403..b5135f6 100755 --- a/index/games/fractal/script/xaos.js +++ b/index/games/fractal/script/xaos.js @@ -1,1410 +1,1518 @@ -/* - * XaoS.js - * https://github.com/jblang/XaoS.js - * - * Copyright (C)2011 John B. Langston III - * Copyright (C)2001, 2010 Andrea Medeghini - * Copyright (C)1996, 1997 Jan Hubicka and Thomas Marsh - * - * Based on code from XaoS by Jan Hubicka (http://xaos.sf.net) - * and from JAME by Andrea Medeghini (http://www.fractalwalk.net) - * - * This file is part of XaoS.js. - * - * XaoS.js 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. - * - * XaoS.js 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 XaoS.js. If not, see . - * - */ -var xaos = xaos || {}; - -xaos.zoom = (function() { - "use strict"; - - const USE_XAOS = true; // Whether to use zooming or recalculate every frame - const USE_SYMMETRY = true; // Whether to use symmetry when possible - const USE_SOLIDGUESS = true; // Whether to use solid guessing to avoid calculations - const RANGES = 2; // Number of ranges to use for sizing approximation data - const RANGE = 4; // Maximum distance to use for approximation - const MASK = 0x7; // Mask value for maximum potential source lines - const DSIZE = (RANGES + 1); // Shift value for target lines - const FPMUL = 64; // Multiplication factor for fixed-point representation - const FPRANGE = FPMUL * RANGE; // Fixed point range of approximation - const MAX_PRICE = Number.MAX_VALUE; // Maximum price of uninitialized approximation - const NEW_PRICE = FPRANGE * FPRANGE; // Price of calculating a new line - const GUESS_RANGE = 4; // Range to use for solid guessing - - /** A price entry in the approximation table - * @constructor - */ - function Price() { - this.previous = null; // Previous price calculated for the same line - this.index = 0; // Index of the source for this approximation (-1 means new calculation) - this.price = MAX_PRICE; // Price calculated for this line - } - - /** A group of pixels to be moved - * @constructor - */ - function Move() { - this.length = 0; // number of pixels to move - this.from = 0; // starting offset of pixel source - this.to = 0; // starting offset of pixel destination - } - - /** A single row or column of pixels in the image - * @constructor - */ - function Line() { - this.recalculate = false; // whether to recalculate this line - this.dirty = false; // whether this line needs to be redrawn - this.isRow = false; // whether this is a row (true) or column (false) - this.index = 0; // index of row or column within the image - this.symIndex = 0; // index of pixels to use for symmetry - this.symTo = 0; // position of pixels this is symmetrical to - this.symRef = 0; // position of pixels referring to this one - this.oldPosition = 0.0; // line's old position in the fractal's complex plane - this.newPosition = 0.0; // line's new position in the fractal's complex plane - this.priority = 0.0; // calculation priority for this row/column - } - - /** An image derived from an HTML5 canvas - * @param canvas - the canvas used to display the image - * @constructor - */ - function CanvasImage(canvas) { - let width = canvas.clientWidth; - let height = canvas.clientHeight; - if (canvas.width !== width || canvas.height !== height) { - canvas.width = width; - canvas.height = height; - } else { - ctx.clearRect(0, 0, width, height); - } - - this.canvas = canvas; - this.context = canvas.getContext("2d"); - this.width = canvas.width; - this.height = canvas.height; - this.newImageData = this.context.createImageData(this.width, this.height); - this.oldImageData = this.context.createImageData(this.width, this.height); - this.newBuffer = new Uint32Array(this.newImageData.data.buffer); - this.oldBuffer = new Uint32Array(this.oldImageData.data.buffer); - } - - /** Swap new and old buffers */ - CanvasImage.prototype.swapBuffers = function() { - var tmp = this.oldBuffer; - this.oldBuffer = this.newBuffer; - this.newBuffer = tmp; - tmp = this.oldImageData; - this.newImageData = this.oldImageData; - this.oldImageData = tmp; - }; - - /** Draw the current image */ - CanvasImage.prototype.paint = function() { - this.context.putImageData(this.newImageData, 0, 0); - }; - - /** Utility function to make an array of the specified size - * with the specified initial value. It will do the right thing - * to create unique items, whether you pass in a prototype, a - * constructor, or a primitive. - * @param {number} size - the size of the array. - * @param initial - the initial value for each entry. - */ - function makeArray(size, initial) { - var i, data = []; - for (i = 0; i < size; i++) { - if (typeof initial === "object") { - // prototype object - data[i] = Object.create(initial); - } else if (typeof initial === "function") { - // constructor - data[i] = new initial(); - } else { - // primitive - data[i] = initial; - } - } - return data; - } - - /** Container for all zoom context data for a particular canvas. - * - * @param image {CanvasImage} Image on which to draw the fractal. - * @param fractal {FractalContext} Fractal parameters. - * @constructor - */ - function ZoomContext(image, fractal) { - var size = Math.max(image.width, image.height); - this.image = image; // the image to draw the fractal on - this.fractal = fractal; // the fractal formula used for the image - this.columns = makeArray(image.width, Line); // columns in the fractal image - this.rows = makeArray(image.height, Line); // rows in the fractal image - this.sourcePos = makeArray(size + 1, 0); // fixed-point positions for source lines - this.oldBest = makeArray(size, null); // best prices for previous line - this.newBest = makeArray(size, null); // best prices for current line - this.calcPrices = makeArray(size, Price); // prices for calculating new lines - this.movePrices = makeArray(size << DSIZE, Price); // prices for approximating new lines from exsiting ones - this.moveTable = makeArray(image.width + 1, Move); // table of pixels to be moved - this.fillTable = makeArray(image.width + 1, Move); // table of pixels to be filled - this.queue = makeArray(image.width + image.height, null); // queue of lines to calculate - this.queueLength = 0; // length of the calculation queue - this.startTime = 0; // time that the current frame was started - this.minFPS = 60; // target FPS to maintain - this.fudgeFactor = 0; // fudge factor used to achieve target FPS - this.incomplete = false; // flag indicates incomplete calculation - this.zooming = false; // flag indicates image is currently zooming - } - - /** Swaps the old and new best prices in the this container. */ - ZoomContext.prototype.swapBest = function() { - var tmpBest = this.oldBest; - this.oldBest = this.newBest; - this.newBest = tmpBest; - }; - - /** Convert fractal viewport from radius and center to x and y start to end ranges */ - ZoomContext.prototype.convertArea = function() { - var radius = this.fractal.region.radius; - var center = this.fractal.region.center; - var aspect = this.image.width / this.image.height; - var size = Math.max(radius.x, radius.y * aspect); - return { - begin: { - x: center.x - size / 2, - y: (center.y - size / 2) / aspect - }, - end: { - x: center.x + size / 2, - y: (center.y + size / 2) / aspect - } - }; - }; - - /** Resets line of pixels for fresh calculation - * - * @param line - row or column of pixels - * @param begin - starting fractal cooridnate - * @param end - ending coordinate - * @param isRow - whether this is a row or column - * @returns {number} - */ - ZoomContext.prototype.initialize = function(lines, begin, end, isRow) { - var i; - var p; - var step = (end - begin) / lines.length; - var line = null; - - for (i = 0, p = begin; i < lines.length; i++, p += step) { - line = lines[i] - line.recalculate = true; - line.dirty = true; - line.isRow = isRow; - line.index = i; - line.oldPosition = p; - line.newPosition = p; - line.symIndex = i; - line.symTo = -1; - line.symRef = -1; - } - return step; - } - - /** Calculate price of approximating one line from another - * - * @param p1 - position of first line - * @param p2 - position of second line - * @returns {number} - price of approximation - */ - function calcPrice(p1, p2) { - return (p1 - p2) * (p1 - p2); - } - - /** Calculate fixed-point representation of each line's old position - * @param lines - lines to use for calculation - * @param begin - beginning of floating point range - * @param end - end of floating point range - */ - ZoomContext.prototype.calcFixedpoint = function(lines, begin, end) { - var tofix = (lines.length * FPMUL) / (end - begin); - var i; - this.sourcePos[lines.length] = Number.MAX_VALUE; - for (i = lines.length - 1; i >= 0; i--) { - this.sourcePos[i] = ((lines[i].oldPosition - begin) * tofix) | 0; - if (this.sourcePos[i] > this.sourcePos[i + 1]) { - this.sourcePos[i] = this.sourcePos[i + 1]; - } - } - } - - /** Choose the best approximation for lines based on previous frame - * - * @param lines - relocation table for rows or columns - * @param begin - beginning coordinate (x or y) - * @param end - ending coordinate (x or y) - * @param newPosition - array of newPosition coordinates on the complex plane - * @returns {number} - */ - ZoomContext.prototype.approximate = function(lines, begin, end) { - var previous = null; // pointer to previous approximation - var best = null; // pointer to best approximation - var line = null; // pointer to current line - var price = 0; // price of current approximation - var dest; // index of the current destination line - var idealPos = 0; // ideal position for the current destination - var maxPos = 0; // maximum valid source position of the current destination - var source = 0; // index of current source line - var prevBegin = 0; // index of first potential source for current destination - var prevEnd = 0; // index of last potential source for current destination - var currBegin = 0; // index of first potential source for next destination - var flag = 0; - var size = lines.length; - var step = (end - begin) / size; - var sourcePos = this.sourcePos; - - // Calculate fixed-point positions of all source lines - this.calcFixedpoint(lines, begin, end); - - for (dest = 0, idealPos = 0; dest < size; dest++, idealPos += FPMUL) { - this.swapBest(); - maxPos = idealPos - FPRANGE; - if (maxPos < -FPMUL) { - maxPos = -FPMUL; - } - source = prevBegin; - while (sourcePos[source] < maxPos) { - source++; - } - currBegin = source; - maxPos = idealPos + FPRANGE; - - // Find the previous approximation - if ((prevBegin !== prevEnd) && (source > prevBegin)) { - // Previous line had approximations; use them - if (source < prevEnd) { - previous = this.oldBest[source - 1]; - } else { - previous = this.oldBest[prevEnd - 1]; - } - price = previous.price; - } else if (dest > 0) { - // Previous line had no approximations - // Use the price of calculating the previous line - previous = this.calcPrices[dest - 1]; - price = previous.price; - } else { - // We're on the first line; no previous prices exists - previous = null; - price = 0; - } - - // Add the price for calculating this line - price += NEW_PRICE; - best = this.calcPrices[dest]; - best.price = price; - best.index = -1; - best.previous = previous; - - // Try all possible approximations for this line and calculate the best one - if (prevBegin !== prevEnd) { - if (source === prevBegin) { - // We're on the first line so there is no previous line - if (sourcePos[source] !== sourcePos[source + 1]) { - previous = this.calcPrices[dest - 1]; - price = previous.price + calcPrice(sourcePos[source], idealPos); - if (price < best.price) { - best = this.movePrices[(source << DSIZE) + (dest & MASK)]; - best.price = price; - best.index = source; - best.previous = previous; - } - } - this.newBest[source++] = best; - } - previous = null; - - // Potential sources for the previous and current line overlap within - // this range, so we have to calculate every possibility and find the best - while (source < prevEnd) { - if (sourcePos[source] !== sourcePos[source + 1]) { - previous = this.oldBest[source - 1]; - price = previous.price + NEW_PRICE; - if (price < best.price) { - best = this.movePrices[((source - 1) << DSIZE) + (dest & MASK)]; - best.price = price; - best.index = -1; - best.previous = previous; - this.newBest[source - 1] = best; - } - price = previous.price + calcPrice(sourcePos[source], idealPos); - if (price < best.price) { - best = this.movePrices[(source << DSIZE) + (dest & MASK)]; - best.price = price; - best.index = source; - best.previous = previous; - } else if (sourcePos[source] > idealPos) { - this.newBest[source++] = best; - break; - } - } - this.newBest[source++] = best; - } - - // We are past the overlapping area - if (source > prevBegin) { - previous = this.oldBest[source - 1]; - } else { - previous = this.calcPrices[dest - 1]; - } - price = previous.price + NEW_PRICE; - if ((price < best.price) && (source > currBegin)) { - best = this.movePrices[((source - 1) << DSIZE) + (dest & MASK)]; - best.price = price; - best.index = -1; - best.previous = previous; - this.newBest[source - 1] = best; - } - while (sourcePos[source] < maxPos) { - if (sourcePos[source] !== sourcePos[source + 1]) { - price = previous.price + calcPrice(sourcePos[source], idealPos); - if (price < best.price) { - best = this.movePrices[(source << DSIZE) + (dest & MASK)]; - best.price = price; - best.index = source; - best.previous = previous; - } else if (sourcePos[source] > idealPos) { - break; - } - } - this.newBest[source++] = best; - } - while (sourcePos[source] < maxPos) { - this.newBest[source++] = best; - } - } else if (sourcePos[source] < maxPos) { - if (dest > 0) { - previous = this.calcPrices[dest - 1]; - price = previous.price; - } else { - previous = null; - price = 0; - } - while (sourcePos[source] < maxPos) { - if (sourcePos[source] !== sourcePos[source + 1]) { - price += calcPrice(sourcePos[source], idealPos); - if (price < best.price) { - best = this.movePrices[(source << DSIZE) + (dest & MASK)]; - best.price = price; - best.index = source; - best.previous = previous; - } else if (sourcePos[source] > idealPos) { - break; - } - } - this.newBest[source++] = best; - } - while (sourcePos[source] < maxPos) { - this.newBest[source++] = best; - } - - } - prevBegin = currBegin; - currBegin = prevEnd; - prevEnd = source; - } - if ((begin > lines[0].oldPosition) && (end < lines[size - 1].oldPosition)) { - flag = 1; - } - if ((sourcePos[0] > 0) && (sourcePos[size - 1] < (size * FPMUL))) { - flag = 2; - } - for (dest = size - 1; dest >= 0; dest--) { - line = lines[dest] - line.symTo = -1; - line.symRef = -1; - if (best.index < 0) { - line.recalculate = true; - line.dirty = true; - line.symIndex = line.index; - } else { - line.symIndex = best.index; - line.newPosition = lines[best.index].oldPosition; - line.recalculate = false; - line.dirty = false; - } - best = best.previous; - } - newPositions(lines, begin, end, step, flag); - return step; - } - - /** Choose new positions for lines based on calculated prices - * - * @param lines - * @param size - * @param begin1 - * @param end1 - * @param step - * @param newPosition - * @param flag - */ - function newPositions(lines, begin1, end1, step, flag) { - var delta = 0; - var size = lines.length; - var begin = 0; - var end = 0; - var s = -1; - var e = -1; - if (begin1 > end1) { - begin1 = end1; - } - while (s < (size - 1)) { - e = s + 1; - if (lines[e].recalculate) { - while (e < size) { - if (!lines[e].recalculate) { - break; - } - e++; - } - if (e < size) { - end = lines[e].newPosition; - } else { - end = end1; - } - if (s < 0) { - begin = begin1; - } else { - begin = lines[s].newPosition; - } - if ((e === size) && (begin > end)) { - end = begin; - } - if ((e - s) === 2) { - delta = (end - begin) * 0.5; - } else { - delta = (end - begin) / (e - s); - } - switch (flag) { - case 1: - for (s++; s < e; s++) { - begin += delta; - lines[s].newPosition = begin; - lines[s].priority = 1 / (1 + (Math.abs((lines[s].oldPosition - begin)) * step)); - } - break; - case 2: - for (s++; s < e; s++) { - begin += delta; - lines[s].newPosition = begin; - lines[s].priority = Math.abs((lines[s].oldPosition - begin)) * step; - } - break; - default: - for (s++; s < e; s++) { - begin += delta; - lines[s].newPosition = begin; - lines[s].priority = 1.0; - } - break; - } - } - s = e; - } - } - - /** Populate symmetry data into relocation table - * - * @param lines - * @param symi - * @param symPosition - * @param step - */ - function prepareSymmetry(lines, symi, symPosition, step) { - var i; - var j = 0; - var tmp; - var abs; - var distance; - var newPosition; - var size = lines.length; - var max = size - RANGE - 1; - var min = RANGE; - var istart = 0; - var line = null; - var otherLine = null; - var symj = (2 * symi) - size; - symPosition *= 2; - if (symj < 0) { - symj = 0; - } - distance = step * RANGE; - for (i = symj; i < symi; i++) { - line = lines[i]; - if (line.symTo !== -1) { - continue; - } - newPosition = line.newPosition; - line.symTo = (2 * symi) - i; - if (line.symTo > max) { - line.symTo = max; - } - j = ((line.symTo - istart) > RANGE) ? (-RANGE) : (-line.symTo + istart); - if (line.recalculate) { - while ((j < RANGE) && ((line.symTo + j) < (size - 1))) { - tmp = symPosition - lines[line.symTo + j].newPosition; - abs = Math.abs(tmp - newPosition); - if (abs < distance) { - if (((i === 0) || (tmp > lines[i - 1].newPosition)) && (tmp < lines[i + 1].newPosition)) { - distance = abs; - min = j; - } - } else if (tmp < newPosition) { - break; - } - j++; - } - } else { - while ((j < RANGE) && ((line.symTo + j) < (size - 1))) { - if (line.recalculate) { - tmp = symPosition - lines[line.symTo + j].newPosition; - abs = Math.abs(tmp - newPosition); - if (abs < distance) { - if (((i === 0) || (tmp > lines[i - 1].newPosition)) && (tmp < lines[i + 1].newPosition)) { - distance = abs; - min = j; - } - } else if (tmp < newPosition) { - break; - } - } - j++; - } - } - line.symTo += min; - otherLine = lines[line.symTo]; - if ((min === RANGE) || (line.symTo <= symi) || (otherLine.symTo !== -1) || (otherLine.symRef !== -1)) { - line.symTo = -1; - continue; - } - if (!line.recalculate) { - line.symTo = -1; - if ((otherLine.symTo !== -1) || !otherLine.recalculate) { - continue; - } - otherLine.symIndex = line.symIndex; - otherLine.symTo = i; - istart = line.symTo - 1; - otherLine.recalculate = false; - otherLine.dirty = true; - line.symRef = line.symTo; - otherLine.newPosition = symPosition - line.newPosition; - } else { - if (otherLine.symTo !== -1) { - line.symTo = -1; - continue; - } - line.symIndex = otherLine.symIndex; - istart = line.symTo - 1; - line.recalculate = false; - line.dirty = true; - otherLine.symRef = i; - line.newPosition = symPosition - otherLine.newPosition; - } - } - } - - /** Optimized array copy using Duff's Device. - * - * @param from {Array} source array - * @param fromOffset {number} offset into source array - * @param to {Array} idealPos array - * @param toOffset {number} offset into idealPos array - * @param length {number} elements to copy - */ - function arrayCopy(from, fromOffset, to, toOffset, length) { - var n = length % 8; - while (n--) { - to[toOffset++] = from[fromOffset++]; - } - n = (length / 8) | 0; - while (n--) { - to[toOffset++] = from[fromOffset++]; - to[toOffset++] = from[fromOffset++]; - to[toOffset++] = from[fromOffset++]; - to[toOffset++] = from[fromOffset++]; - to[toOffset++] = from[fromOffset++]; - to[toOffset++] = from[fromOffset++]; - to[toOffset++] = from[fromOffset++]; - to[toOffset++] = from[fromOffset++]; - } - } - - /** Apply previously calculated symmetry to image */ - ZoomContext.prototype.doSymmetry = function() { - var from_offset = 0; - var to_offset = 0; - var i; - var j = 0; - var buffer = this.image.newBuffer; - var bufferWidth = this.image.width; - for (i = 0; i < this.rows.length; i++) { - if ((this.rows[i].symTo >= 0) && (!this.rows[this.rows[i].symTo].dirty)) { - from_offset = this.rows[i].symTo * bufferWidth; - arrayCopy(buffer, from_offset, buffer, to_offset, bufferWidth); - this.rows[i].dirty = false; - } - to_offset += bufferWidth; - } - for (i = 0; i < this.columns.length; i++) { - if ((this.columns[i].symTo >= 0) && (!this.columns[this.columns[i].symTo].dirty)) { - to_offset = i; - from_offset = this.columns[i].symTo; - for (j = 0; j < this.rows.length; j++) { - buffer[to_offset] = buffer[from_offset]; - to_offset += bufferWidth; - from_offset += bufferWidth; - } - this.columns[i].dirty = false; - } - } - } - - /** Build an optimized move table based on relocation table */ - ZoomContext.prototype.prepareMove = function() { - var move = null; - var i = 0; - var j = 0; - var s = 0; - while (i < this.columns.length) { - if (!this.columns[i].dirty) { - move = this.moveTable[s]; - move.to = i; - move.length = 1; - move.from = this.columns[i].symIndex; - for (j = i + 1; j < this.columns.length; j++) { - if (this.columns[j].dirty || ((j - this.columns[j].symIndex) !== (move.to - move.from))) { - break; - } - move.length++; - } - i = j; - s++; - } else { - i++; - } - } - move = this.moveTable[s]; - move.length = 0; - } - - /** Execute moves defined in move table */ - ZoomContext.prototype.doMove = function() { - var move = null; - var newOffset = 0; - var oldOffset = 0; - var from = 0; - var to = 0; - var i; - var s = 0; - var length = 0; - var newBuffer = this.image.newBuffer; - var oldBuffer = this.image.oldBuffer; - var bufferWidth = this.image.width; - for (i = 0; i < this.rows.length; i++) { - if (!this.rows[i].dirty) { - s = 0; - oldOffset = this.rows[i].symIndex * bufferWidth; - while ((move = this.moveTable[s]).length > 0) { - from = oldOffset + move.from; - to = newOffset + move.to; - length = move.length; - arrayCopy(oldBuffer, from, newBuffer, to, length); - s++; - } - } - newOffset += bufferWidth; - } - } - - /** Shortcut for prepare and execute move */ - ZoomContext.prototype.movePixels = function() { - this.prepareMove(); - this.doMove(); - } - - /** Prepare fill table based on relocation table */ - ZoomContext.prototype.prepareFill = function() { - var fill = null; - var i; - var j = 0; - var k = 0; - var s = 0; - var n = 0; - for (i = 0; i < this.columns.length; i++) { - if (this.columns[i].dirty) { - j = i - 1; - for (k = i + 1; (k < this.columns.length) && this.columns[k].dirty; k++) {} - while ((i < this.columns.length) && this.columns[i].dirty) { - if ((k < this.columns.length) && ((j < i) || ((this.columns[i].newPosition - this.columns[j].newPosition) > (this.columns[k].newPosition - this.columns[i].newPosition)))) { - j = k; - } else if (j < 0) { - break; - } - n = k - i; - fill = this.fillTable[s]; - fill.length = n; - fill.from = j; - fill.to = i; - while (n > 0) { - this.columns[i].newPosition = this.columns[j].newPosition; - this.columns[i].dirty = false; - n--; - i++; - } - s++; - } - } - } - fill = this.fillTable[s]; - fill.length = 0; - } - - /** Apply fill table */ - ZoomContext.prototype.doFill = function() { - var fill = null; - var from_offset = 0; - var to_offset = 0; - var from = 0; - var to = 0; - var i; - var j = 0; - var k = 0; - var t = 0; - var s = 0; - var d = 0; - var buffer = this.image.newBuffer; - var bufferWidth = this.image.width; - for (i = 0; i < this.rows.length; i++) { - if (this.rows[i].dirty) { - j = i - 1; - for (k = i + 1; (k < this.rows.length) && this.rows[k].dirty; k++) {} - while ((i < this.rows.length) && this.rows[i].dirty) { - if ((k < this.rows.length) && ((j < i) || ((this.rows[i].newPosition - this.rows[j].newPosition) > (this.rows[k].newPosition - this.rows[i].newPosition)))) { - j = k; - } else if (j < 0) { - break; - } - to_offset = i * bufferWidth; - from_offset = j * bufferWidth; - if (!this.rows[j].dirty) { - s = 0; - while ((fill = this.fillTable[s]).length > 0) { - from = from_offset + fill.from; - to = from_offset + fill.to; - for (t = 0; t < fill.length; t++) { - d = to + t; - buffer[d] = buffer[from]; - } - s++; - } - } - arrayCopy(buffer, from_offset, buffer, to_offset, bufferWidth); - this.rows[i].newPosition = this.rows[j].newPosition; - this.rows[i].dirty = true; - i++; - } - } else { - s = 0; - from_offset = i * bufferWidth; - while ((fill = this.fillTable[s]).length > 0) { - from = from_offset + fill.from; - to = from_offset + fill.to; - for (t = 0; t < fill.length; t++) { - d = to + t; - buffer[d] = buffer[from]; - } - s++; - } - this.rows[i].dirty = true; - } - } - } - - /** Shortcut to prepare and apply fill table */ - ZoomContext.prototype.fill = function() { - this.prepareFill(); - this.doFill(); - } - - /** Render line using solid guessing - * - * @param row - */ - ZoomContext.prototype.renderRow = function(row) { - var buffer = this.image.newBuffer; - var bufferWidth = this.image.width; - var newPosition = row.newPosition; - var r = row.index; - var offset = r * bufferWidth; - var i; - var j; - var k; - var n; - var distl; - var distr; - var distu; - var distd; - var offsetu; - var offsetd; - var offsetl; - var offsetul; - var offsetur; - var offsetdl; - var offsetdr; - var rend = r - GUESS_RANGE; - var length; - var current; - if (rend < 0) { - rend = 0; - } - for (i = r - 1; (i >= rend) && this.rows[i].dirty; i--) {} - distu = r - i; - rend = r + GUESS_RANGE; - if (rend >= this.rows.length) { - rend = this.rows.length - 1; - } - for (j = r + 1; (j < rend) && this.rows[j].dirty; j++) {} - distd = j - r; - if (!USE_SOLIDGUESS || (i < 0) || (j >= this.rows.length) || this.rows[i].dirty || this.rows[j].dirty) { - for (k = 0, length = this.columns.length; k < length; k++) { - current = this.columns[k]; - if (!this.columns[k].dirty) { - buffer[offset] = this.fractal.formula(current.newPosition, newPosition); - } - offset++; - } - } else { - distr = 0; - distl = Number.MAX_VALUE / 2; - offsetu = offset - (distu * bufferWidth); - offsetd = offset + (distd * bufferWidth); - for (k = 0, length = this.columns.length; k < length; k++) { - current = this.columns[k]; - if (!this.columns[k].dirty) { - if (distr <= 0) { - rend = k + GUESS_RANGE; - if (rend >= this.columns.length) { - rend = this.columns.length - 1; - } - for (j = k + 1; (j < rend) && this.columns[j].dirty; j++) { - distr = j - k; - } - if (j >= rend) { - distr = Number.MAX_VALUE / 2; - } - } - if ((distr < (Number.MAX_VALUE / 4)) && (distl < (Number.MAX_VALUE / 4))) { - offsetl = offset - distl; - offsetul = offsetu - distl; - offsetdl = offsetd - distl; - offsetur = offsetu + distr; - offsetdr = offsetd + distr; - n = buffer[offsetl]; - if ((n == buffer[offsetu]) && (n == buffer[offsetd]) && (n == buffer[offsetul]) && (n == buffer[offsetur]) && (n == buffer[offsetdl]) && (n == buffer[offsetdr])) { - buffer[offset] = n; - } else { - buffer[offset] = this.fractal.formula(current.newPosition, newPosition); - } - } else { - buffer[offset] = this.fractal.formula(current.newPosition, newPosition); - } - distl = 0; - } - offset++; - offsetu++; - offsetd++; - distr--; - distl++; - } - } - row.recalculate = false; - row.dirty = false; - } - - /** Render column using solid guessing - * - * @param column - */ - ZoomContext.prototype.renderColumn = function(column) { - var buffer = this.image.newBuffer; - var bufferWidth = this.image.width; - var newPosition = column.newPosition; - var r = column.index; - var offset = r; - var rend = r - GUESS_RANGE; - var i; - var j; - var k; - var n; - var distl; - var distr; - var distu; - var distd; - var offsetl; - var offsetr; - var offsetu; - var offsetlu; - var offsetru; - var offsetld; - var offsetrd; - var sumu; - var sumd; - var length; - var current; - if (rend < 0) { - rend = 0; - } - for (i = r - 1; (i >= rend) && this.columns[i].dirty; i--) {} - distl = r - i; - rend = r + GUESS_RANGE; - if (rend >= this.columns.length) { - rend = this.columns.length - 1; - } - for (j = r + 1; (j < rend) && this.columns[j].dirty; j++) {} - distr = j - r; - if (!USE_SOLIDGUESS || (i < 0) || (j >= this.columns.length) || this.columns[i].dirty || this.columns[j].dirty) { - for (k = 0, length = this.rows.length; k < length; k++) { - current = this.rows[k]; - if (!this.rows[k].dirty) { - buffer[offset] = this.fractal.formula(newPosition, current.newPosition); - } - offset += bufferWidth; - } - } else { - distd = 0; - distu = Number.MAX_VALUE / 2; - offsetl = offset - distl; - offsetr = offset + distr; - for (k = 0, length = this.rows.length; k < length; k++) { - current = this.rows[k]; - if (!this.rows[k].dirty) { - if (distd <= 0) { - rend = k + GUESS_RANGE; - if (rend >= this.rows.length) { - rend = this.rows.length - 1; - } - for (j = k + 1; (j < rend) && this.rows[j].dirty; j++) { - distd = j - k; - } - if (j >= rend) { - distd = Number.MAX_VALUE / 2; - } - } - if ((distd < (Number.MAX_VALUE / 4)) && (distu < (Number.MAX_VALUE / 4))) { - sumu = distu * bufferWidth; - sumd = distd * bufferWidth; - offsetu = offset - sumu; - offsetlu = offsetl - sumu; - offsetru = offsetr - sumu; - offsetld = offsetl + sumd; - offsetrd = offsetr + sumd; - n = buffer[offsetu]; - if ((n == buffer[offsetl]) && (n == buffer[offsetr]) && (n == buffer[offsetlu]) && (n == buffer[offsetru]) && (n == buffer[offsetld]) && (n == buffer[offsetrd])) { - buffer[offset] = n; - } else { - buffer[offset] = this.fractal.formula(newPosition, current.newPosition); - } - } else { - buffer[offset] = this.fractal.formula(newPosition, current.newPosition); - } - distu = 0; - } - offset += bufferWidth; - offsetl += bufferWidth; - offsetr += bufferWidth; - distd--; - distu++; - } - } - column.recalculate = false; - column.dirty = false; - } - - /** Calculate whether we're taking too long to render the fractal to meet the idealPos FPS */ - ZoomContext.prototype.tooSlow = function() { - var newTime = new Date().getTime(), - minFPS = this.zooming ? this.minFPS : 10; - return 1000 / (newTime - this.startTime + this.fudgeFactor) < minFPS; - } - - /** Prioritize calculation of lines between begin and end - * - * @param lines - rows or columns to prioritize - * @param begin - index of first line to prioritize - * @param end - index of last line to prioritize - */ - function calcPriority(lines, begin, end) { - var middle; - while (begin < end) { - middle = begin + ((end - begin) >> 1); - lines[middle].priority = (lines[end].newPosition - lines[middle].newPosition) * lines[middle].priority; - if (lines[middle].symRef !== -1) { - lines[middle].priority /= 2.0; - } - calcPriority(lines, begin, middle); - begin = middle + 1; - } - } - - /** Enqueue all the lines to be recalculated and set their priority - * - * @param lines - lines to enqueue for calculation - */ - ZoomContext.prototype.enqueueCalculations = function(lines) { - var i; - var j = 0; - for (i = 0; i < lines.length; i++) { - if (lines[i].recalculate) { - for (j = i; (j < lines.length) && lines[j].recalculate; j++) { - this.queue[this.queueLength++] = lines[j]; - } - if (j === lines.length) { - j -= 1; - } - calcPriority(lines, i, j); - i = j; - } - } - } - - /** Sort calculation queue according to priority (using quicksort) - * - * @param queue - * @param l - * @param r - */ - function sortQueue(queue, l, r) { - var m = (queue[l].priority + queue[r].priority) / 2.0; - var tmp = null; - var i = l; - var j = r; - do { - while (queue[i].priority > m) { - i++; - } - while (queue[j].priority < m) { - j--; - } - if (i <= j) { - tmp = queue[i]; - queue[i] = queue[j]; - queue[j] = tmp; - i++; - j--; - } - } - while (j >= i); - if (l < j) { - sortQueue(queue, l, j); - } - if (r > i) { - sortQueue(queue, i, r); - } - } - - /** Process the relocation table */ - ZoomContext.prototype.calculate = function() { - var i, newTime; - this.incomplete = false; - this.queueLength = 0; - this.enqueueCalculations(this.columns); - this.enqueueCalculations(this.rows); - if (this.queueLength > 0) { - if (this.queueLength > 1) { - sortQueue(this.queue, 0, this.queueLength - 1); - } - for (i = 0; i < this.queueLength; i++) { - if (this.queue[i].isRow) { - this.renderRow(this.queue[i]); - } else { - this.renderColumn(this.queue[i]); - } - if (!this.recalculate && this.tooSlow() && (i < this.queueLength)) { - this.incomplete = true; - this.fill(); - break; - } - } - } - }; - - /** Update newPosition array with newly calculated positions */ - ZoomContext.prototype.updatePosition = function() { - var k; - var len; - for (k = 0,len = this.columns.length; k < len; k++) { - this.columns[k].oldPosition = this.columns[k].newPosition; - } - for (k = 0,len = this.rows.length; k < len; k++) { - this.rows[k].oldPosition = this.rows[k].newPosition; - } - }; - - /** Calculate FPS achieved and determine if fudge factor needs adjustment for next frame */ - ZoomContext.prototype.updateFPS = function() { - var fps = 1000 / (new Date().getTime() - this.startTime); - if (fps < this.minFPS) { - this.fudgeFactor++; - } else if (fps > this.minFPS + 10 && this.fudgeFactor > 0) { - this.fudgeFactor--; - } - console.log(fps + " fps"); - }; - - /** Overall fractal drawing workflow, calls other functions */ - ZoomContext.prototype.drawFractal = function(recalculate) { - var area = this.convertArea(); - var symx = this.fractal.symmetry && this.fractal.symmetry.x; - var symy = this.fractal.symmetry && this.fractal.symmetry.y; - var stepx, stepy; - this.startTime = new Date().getTime(); - this.recalculate = recalculate; - if (recalculate || !USE_XAOS) { - stepx = this.initialize(this.columns, area.begin.x, area.end.x, false); - stepy = this.initialize(this.rows, area.begin.y, area.end.y, true); - } else { - stepx = this.approximate(this.columns, area.begin.x, area.end.x); - stepy = this.approximate(this.rows, area.begin.y, area.end.y); - } - if (USE_SYMMETRY && typeof symy === "number" && !(area.begin.y > symy || symy > area.end.y)) { - prepareSymmetry(this.rows, Math.floor((symy - area.begin.y) / stepy), symy, stepy); - } - if (USE_SYMMETRY && typeof symx === "number" && !(area.begin.x > symx || symx > area.end.x)) { - prepareSymmetry(this.columns, Math.floor((symx - area.begin.x) / stepx), symx, stepx); - } - this.image.swapBuffers(); - this.movePixels(); - this.calculate(); - if (USE_SYMMETRY && typeof symx === "number" || typeof symy === "number") { - this.doSymmetry(); - } - this.image.paint(); - this.updatePosition(); - this.updateFPS(); - }; - - /** Adjust display region to zoom based on mouse buttons */ - ZoomContext.prototype.updateRegion = function(mouse) { - var MAXSTEP = 0.008 * 3; - var MUL = 0.3; - var area = this.convertArea(); - var x = area.begin.x + mouse.x * ((area.end.x - area.begin.x) / this.image.width); - var y = area.begin.y + mouse.y * ((area.end.y - area.begin.y) / this.image.height); - var deltax = (mouse.oldx - mouse.x) * ((area.end.x - area.begin.x) / this.image.width); - var deltay = (mouse.oldy - mouse.y) * ((area.end.y - area.begin.y) / this.image.height); - var step; - var mmul; - if (mouse.button[1] || (mouse.button[0] && mouse.button[2])) { - // Pan when middle or left+right buttons are pressed - step = 0; - } else if (mouse.button[0]) { - // Zoom in when left button is pressed - step = MAXSTEP * 2; - } else if (mouse.button[2]) { - // Zoom out when right button is pressed - step = -MAXSTEP * 2; - } else { - this.zooming = false; - return; - } - mmul = Math.pow((1 - step), MUL); - area.begin.x = x + (area.begin.x - x) * mmul; - area.end.x = x + (area.end.x - x) * mmul; - area.begin.y = y + (area.begin.y - y) * mmul; - area.end.y = y + (area.end.y - y) * mmul; - this.fractal.region.radius.x = area.end.x - area.begin.x; - this.fractal.region.radius.y = area.end.y - area.begin.y; - this.fractal.region.center.x = (area.begin.x + area.end.x) / 2; - this.fractal.region.center.y = ((area.begin.y + area.end.y) / 2) * (this.image.width / this.image.height); - this.zooming = true; - }; - - /** Attaches zoomer to specified canvas */ - return function(canvas, fractal) { - var image = new CanvasImage(canvas); - var zoomer = new ZoomContext(image, fractal); - var mouse = { x: 0, y: 0, button: [false, false, false] }; - - function doZoom() { - zoomer.updateRegion(mouse); - if (zoomer.zooming || zoomer.incomplete) { - requestAnimationFrame(doZoom); - zoomer.drawFractal(false); - } - } - - canvas.ontouchstart = function(e) { - if(e.touches.length < 3){ - var touch = e.touches[0]; - (e.touches.length == 2)?mouse.button[2]=true:mouse.button[2]=false; - var mouseEvent = new MouseEvent("mousedown", { - clientX: touch.clientX, - clientY: touch.clientY - }); - canvas.dispatchEvent(mouseEvent); - } - }; - - canvas.ontouchend = function(e) { - var mouseEvent = new MouseEvent("mouseup", {}); - canvas.dispatchEvent(mouseEvent); - }; - - canvas.ontouchmove = function(e) { - var touch = e.touches[0]; - var mouseEvent = new MouseEvent("mousemove", { - clientX: touch.clientX, - clientY: touch.clientY - }); - canvas.dispatchEvent(mouseEvent); - }; - - canvas.onmousedown = function(e) { - mouse.button[e.button] = true; - mouse.x = e.offsetX || (e.clientX - canvas.offsetLeft); - mouse.y = e.offsetY || (e.clientY - canvas.offsetTop); - mouse.oldx = e.offsetX || (e.clientX - canvas.offsetLeft); - mouse.oldy = e.offsetY || (e.clientY - canvas.offsetTop); - doZoom(); - }; - - canvas.onmouseup = function(e) { - mouse.button[e.button] = false; - }; - - canvas.onmousemove = function(e) { - mouse.x = e.offsetX || (e.clientX - canvas.offsetLeft); - mouse.y = e.offsetY || (e.clientY - canvas.offsetTop); - }; - - canvas.oncontextmenu = function() { - return false; - }; - - canvas.onmouseout = function() { - mouse.button = [false, false, false]; - }; - - zoomer.drawFractal(true); - } -}()); - -/** Create the default XaoS color palette */ -xaos.defaultPalette = function() { - var MAXENTRIES = 65536; - var segmentsize = 8; - var setsegments = Math.floor((MAXENTRIES + 3) / segmentsize); - var nsegments = Math.floor(255 / segmentsize); - var segments = [ - [0, 0, 0], - [120, 119, 238], - [24, 7, 25], - [197, 66, 28], - [29, 18, 11], - [135, 46, 71], - [24, 27, 13], - [241, 230, 128], - [17, 31, 24], - [240, 162, 139], - [11, 4, 30], - [106, 87, 189], - [29, 21, 14], - [12, 140, 118], - [10, 6, 29], - [50, 144, 77], - [22, 0, 24], - [148, 188, 243], - [4, 32, 7], - [231, 146, 14], - [10, 13, 20], - [184, 147, 68], - [13, 28, 3], - [169, 248, 152], - [4, 0, 34], - [62, 83, 48], - [7, 21, 22], - [152, 97, 184], - [8, 3, 12], - [247, 92, 235], - [31, 32, 16] - ]; - var i, y; - var r, g, b; - var rs, gs, bs; - var palette = []; - - for (i = 0; i < setsegments; i++) { - r = segments[i % nsegments][0]; - g = segments[i % nsegments][1]; - b = segments[i % nsegments][2]; - rs = (segments[(i + 1) % setsegments % nsegments][0] - r) / segmentsize; - gs = (segments[(i + 1) % setsegments % nsegments][1] - g) / segmentsize; - bs = (segments[(i + 1) % setsegments % nsegments][2] - b) / segmentsize; - for (y = 0; y < segmentsize; y++) { - palette.push(255<<24 | b << 16 | g << 8 | r); - r += rs; - g += gs; - b += bs; - } - } - return new Uint32Array(palette); -}; - -xaos.mandelbrot = { - symmetry: {x: null, y: 0 }, - region: { - center: { x: -0.75, y: 0.0 }, - radius: { x: 2.5, y : 2.5 }, - angle: 0 - }, - z0: { x: 0, y: 0 }, - maxiter: 512, - bailout: 4, - formula: function(cr, ci) { - var maxiter = this.maxiter, - bailout = this.bailout, - zr = this.z0.x, - zi = this.z0.y, - i = maxiter; - - while (i--) { - var zr2 = zr * zr; - var zi2 = zi * zi; - - if (zr2 + zi2 > bailout) { - return this.palette[(maxiter - i) % this.palette.length]; - } - - zi = ci + (2 * zr * zi); - zr = cr + zr2 - zi2; - } - - return this.palette[0]; - }, - palette: xaos.defaultPalette() -}; - -xaos.zoom(document.getElementById("canvas"), xaos.mandelbrot); \ No newline at end of file +/* + * XaoS.js + * https://github.com/jblang/XaoS.js + * + * Copyright (C)2011 John B. Langston III + * Copyright (C)2001, 2010 Andrea Medeghini + * Copyright (C)1996, 1997 Jan Hubicka and Thomas Marsh + * + * Based on code from XaoS by Jan Hubicka (http://xaos.sf.net) + * and from JAME by Andrea Medeghini (http://www.fractalwalk.net) + * + * This file is part of XaoS.js. + * + * XaoS.js 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. + * + * XaoS.js 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 XaoS.js. If not, see . + * + */ +var xaos = xaos || {}; + +xaos.zoom = (function () { + "use strict"; + + const USE_XAOS = true; // Whether to use zooming or recalculate every frame + const USE_SYMMETRY = true; // Whether to use symmetry when possible + const USE_SOLIDGUESS = true; // Whether to use solid guessing to avoid calculations + const RANGES = 2; // Number of ranges to use for sizing approximation data + const RANGE = 4; // Maximum distance to use for approximation + const MASK = 0x7; // Mask value for maximum potential source lines + const DSIZE = RANGES + 1; // Shift value for target lines + const FPMUL = 64; // Multiplication factor for fixed-point representation + const FPRANGE = FPMUL * RANGE; // Fixed point range of approximation + const MAX_PRICE = Number.MAX_VALUE; // Maximum price of uninitialized approximation + const NEW_PRICE = FPRANGE * FPRANGE; // Price of calculating a new line + const GUESS_RANGE = 4; // Range to use for solid guessing + + /** A price entry in the approximation table + * @constructor + */ + function Price() { + this.previous = null; // Previous price calculated for the same line + this.index = 0; // Index of the source for this approximation (-1 means new calculation) + this.price = MAX_PRICE; // Price calculated for this line + } + + /** A group of pixels to be moved + * @constructor + */ + function Move() { + this.length = 0; // number of pixels to move + this.from = 0; // starting offset of pixel source + this.to = 0; // starting offset of pixel destination + } + + /** A single row or column of pixels in the image + * @constructor + */ + function Line() { + this.recalculate = false; // whether to recalculate this line + this.dirty = false; // whether this line needs to be redrawn + this.isRow = false; // whether this is a row (true) or column (false) + this.index = 0; // index of row or column within the image + this.symIndex = 0; // index of pixels to use for symmetry + this.symTo = 0; // position of pixels this is symmetrical to + this.symRef = 0; // position of pixels referring to this one + this.oldPosition = 0.0; // line's old position in the fractal's complex plane + this.newPosition = 0.0; // line's new position in the fractal's complex plane + this.priority = 0.0; // calculation priority for this row/column + } + + /** An image derived from an HTML5 canvas + * @param canvas - the canvas used to display the image + * @constructor + */ + function CanvasImage(canvas) { + let width = canvas.clientWidth; + let height = canvas.clientHeight; + if (canvas.width !== width || canvas.height !== height) { + canvas.width = width; + canvas.height = height; + } else { + ctx.clearRect(0, 0, width, height); + } + + this.canvas = canvas; + this.context = canvas.getContext("2d"); + this.width = canvas.width; + this.height = canvas.height; + this.newImageData = this.context.createImageData(this.width, this.height); + this.oldImageData = this.context.createImageData(this.width, this.height); + this.newBuffer = new Uint32Array(this.newImageData.data.buffer); + this.oldBuffer = new Uint32Array(this.oldImageData.data.buffer); + } + + /** Swap new and old buffers */ + CanvasImage.prototype.swapBuffers = function () { + var tmp = this.oldBuffer; + this.oldBuffer = this.newBuffer; + this.newBuffer = tmp; + tmp = this.oldImageData; + this.newImageData = this.oldImageData; + this.oldImageData = tmp; + }; + + /** Draw the current image */ + CanvasImage.prototype.paint = function () { + this.context.putImageData(this.newImageData, 0, 0); + }; + + /** Utility function to make an array of the specified size + * with the specified initial value. It will do the right thing + * to create unique items, whether you pass in a prototype, a + * constructor, or a primitive. + * @param {number} size - the size of the array. + * @param initial - the initial value for each entry. + */ + function makeArray(size, initial) { + var i, + data = []; + for (i = 0; i < size; i++) { + if (typeof initial === "object") { + // prototype object + data[i] = Object.create(initial); + } else if (typeof initial === "function") { + // constructor + data[i] = new initial(); + } else { + // primitive + data[i] = initial; + } + } + return data; + } + + /** Container for all zoom context data for a particular canvas. + * + * @param image {CanvasImage} Image on which to draw the fractal. + * @param fractal {FractalContext} Fractal parameters. + * @constructor + */ + function ZoomContext(image, fractal) { + var size = Math.max(image.width, image.height); + this.image = image; // the image to draw the fractal on + this.fractal = fractal; // the fractal formula used for the image + this.columns = makeArray(image.width, Line); // columns in the fractal image + this.rows = makeArray(image.height, Line); // rows in the fractal image + this.sourcePos = makeArray(size + 1, 0); // fixed-point positions for source lines + this.oldBest = makeArray(size, null); // best prices for previous line + this.newBest = makeArray(size, null); // best prices for current line + this.calcPrices = makeArray(size, Price); // prices for calculating new lines + this.movePrices = makeArray(size << DSIZE, Price); // prices for approximating new lines from exsiting ones + this.moveTable = makeArray(image.width + 1, Move); // table of pixels to be moved + this.fillTable = makeArray(image.width + 1, Move); // table of pixels to be filled + this.queue = makeArray(image.width + image.height, null); // queue of lines to calculate + this.queueLength = 0; // length of the calculation queue + this.startTime = 0; // time that the current frame was started + this.minFPS = 60; // target FPS to maintain + this.fudgeFactor = 0; // fudge factor used to achieve target FPS + this.incomplete = false; // flag indicates incomplete calculation + this.zooming = false; // flag indicates image is currently zooming + } + + /** Swaps the old and new best prices in the this container. */ + ZoomContext.prototype.swapBest = function () { + var tmpBest = this.oldBest; + this.oldBest = this.newBest; + this.newBest = tmpBest; + }; + + /** Convert fractal viewport from radius and center to x and y start to end ranges */ + ZoomContext.prototype.convertArea = function () { + var radius = this.fractal.region.radius; + var center = this.fractal.region.center; + var aspect = this.image.width / this.image.height; + var size = Math.max(radius.x, radius.y * aspect); + return { + begin: { + x: center.x - size / 2, + y: (center.y - size / 2) / aspect, + }, + end: { + x: center.x + size / 2, + y: (center.y + size / 2) / aspect, + }, + }; + }; + + /** Resets line of pixels for fresh calculation + * + * @param line - row or column of pixels + * @param begin - starting fractal cooridnate + * @param end - ending coordinate + * @param isRow - whether this is a row or column + * @returns {number} + */ + ZoomContext.prototype.initialize = function (lines, begin, end, isRow) { + var i; + var p; + var step = (end - begin) / lines.length; + var line = null; + + for (i = 0, p = begin; i < lines.length; i++, p += step) { + line = lines[i]; + line.recalculate = true; + line.dirty = true; + line.isRow = isRow; + line.index = i; + line.oldPosition = p; + line.newPosition = p; + line.symIndex = i; + line.symTo = -1; + line.symRef = -1; + } + return step; + }; + + /** Calculate price of approximating one line from another + * + * @param p1 - position of first line + * @param p2 - position of second line + * @returns {number} - price of approximation + */ + function calcPrice(p1, p2) { + return (p1 - p2) * (p1 - p2); + } + + /** Calculate fixed-point representation of each line's old position + * @param lines - lines to use for calculation + * @param begin - beginning of floating point range + * @param end - end of floating point range + */ + ZoomContext.prototype.calcFixedpoint = function (lines, begin, end) { + var tofix = (lines.length * FPMUL) / (end - begin); + var i; + this.sourcePos[lines.length] = Number.MAX_VALUE; + for (i = lines.length - 1; i >= 0; i--) { + this.sourcePos[i] = ((lines[i].oldPosition - begin) * tofix) | 0; + if (this.sourcePos[i] > this.sourcePos[i + 1]) { + this.sourcePos[i] = this.sourcePos[i + 1]; + } + } + }; + + /** Choose the best approximation for lines based on previous frame + * + * @param lines - relocation table for rows or columns + * @param begin - beginning coordinate (x or y) + * @param end - ending coordinate (x or y) + * @param newPosition - array of newPosition coordinates on the complex plane + * @returns {number} + */ + ZoomContext.prototype.approximate = function (lines, begin, end) { + var previous = null; // pointer to previous approximation + var best = null; // pointer to best approximation + var line = null; // pointer to current line + var price = 0; // price of current approximation + var dest; // index of the current destination line + var idealPos = 0; // ideal position for the current destination + var maxPos = 0; // maximum valid source position of the current destination + var source = 0; // index of current source line + var prevBegin = 0; // index of first potential source for current destination + var prevEnd = 0; // index of last potential source for current destination + var currBegin = 0; // index of first potential source for next destination + var flag = 0; + var size = lines.length; + var step = (end - begin) / size; + var sourcePos = this.sourcePos; + + // Calculate fixed-point positions of all source lines + this.calcFixedpoint(lines, begin, end); + + for (dest = 0, idealPos = 0; dest < size; dest++, idealPos += FPMUL) { + this.swapBest(); + maxPos = idealPos - FPRANGE; + if (maxPos < -FPMUL) { + maxPos = -FPMUL; + } + source = prevBegin; + while (sourcePos[source] < maxPos) { + source++; + } + currBegin = source; + maxPos = idealPos + FPRANGE; + + // Find the previous approximation + if (prevBegin !== prevEnd && source > prevBegin) { + // Previous line had approximations; use them + if (source < prevEnd) { + previous = this.oldBest[source - 1]; + } else { + previous = this.oldBest[prevEnd - 1]; + } + price = previous.price; + } else if (dest > 0) { + // Previous line had no approximations + // Use the price of calculating the previous line + previous = this.calcPrices[dest - 1]; + price = previous.price; + } else { + // We're on the first line; no previous prices exists + previous = null; + price = 0; + } + + // Add the price for calculating this line + price += NEW_PRICE; + best = this.calcPrices[dest]; + best.price = price; + best.index = -1; + best.previous = previous; + + // Try all possible approximations for this line and calculate the best one + if (prevBegin !== prevEnd) { + if (source === prevBegin) { + // We're on the first line so there is no previous line + if (sourcePos[source] !== sourcePos[source + 1]) { + previous = this.calcPrices[dest - 1]; + price = previous.price + calcPrice(sourcePos[source], idealPos); + if (price < best.price) { + best = this.movePrices[(source << DSIZE) + (dest & MASK)]; + best.price = price; + best.index = source; + best.previous = previous; + } + } + this.newBest[source++] = best; + } + previous = null; + + // Potential sources for the previous and current line overlap within + // this range, so we have to calculate every possibility and find the best + while (source < prevEnd) { + if (sourcePos[source] !== sourcePos[source + 1]) { + previous = this.oldBest[source - 1]; + price = previous.price + NEW_PRICE; + if (price < best.price) { + best = this.movePrices[((source - 1) << DSIZE) + (dest & MASK)]; + best.price = price; + best.index = -1; + best.previous = previous; + this.newBest[source - 1] = best; + } + price = previous.price + calcPrice(sourcePos[source], idealPos); + if (price < best.price) { + best = this.movePrices[(source << DSIZE) + (dest & MASK)]; + best.price = price; + best.index = source; + best.previous = previous; + } else if (sourcePos[source] > idealPos) { + this.newBest[source++] = best; + break; + } + } + this.newBest[source++] = best; + } + + // We are past the overlapping area + if (source > prevBegin) { + previous = this.oldBest[source - 1]; + } else { + previous = this.calcPrices[dest - 1]; + } + price = previous.price + NEW_PRICE; + if (price < best.price && source > currBegin) { + best = this.movePrices[((source - 1) << DSIZE) + (dest & MASK)]; + best.price = price; + best.index = -1; + best.previous = previous; + this.newBest[source - 1] = best; + } + while (sourcePos[source] < maxPos) { + if (sourcePos[source] !== sourcePos[source + 1]) { + price = previous.price + calcPrice(sourcePos[source], idealPos); + if (price < best.price) { + best = this.movePrices[(source << DSIZE) + (dest & MASK)]; + best.price = price; + best.index = source; + best.previous = previous; + } else if (sourcePos[source] > idealPos) { + break; + } + } + this.newBest[source++] = best; + } + while (sourcePos[source] < maxPos) { + this.newBest[source++] = best; + } + } else if (sourcePos[source] < maxPos) { + if (dest > 0) { + previous = this.calcPrices[dest - 1]; + price = previous.price; + } else { + previous = null; + price = 0; + } + while (sourcePos[source] < maxPos) { + if (sourcePos[source] !== sourcePos[source + 1]) { + price += calcPrice(sourcePos[source], idealPos); + if (price < best.price) { + best = this.movePrices[(source << DSIZE) + (dest & MASK)]; + best.price = price; + best.index = source; + best.previous = previous; + } else if (sourcePos[source] > idealPos) { + break; + } + } + this.newBest[source++] = best; + } + while (sourcePos[source] < maxPos) { + this.newBest[source++] = best; + } + } + prevBegin = currBegin; + currBegin = prevEnd; + prevEnd = source; + } + if (begin > lines[0].oldPosition && end < lines[size - 1].oldPosition) { + flag = 1; + } + if (sourcePos[0] > 0 && sourcePos[size - 1] < size * FPMUL) { + flag = 2; + } + for (dest = size - 1; dest >= 0; dest--) { + line = lines[dest]; + line.symTo = -1; + line.symRef = -1; + if (best.index < 0) { + line.recalculate = true; + line.dirty = true; + line.symIndex = line.index; + } else { + line.symIndex = best.index; + line.newPosition = lines[best.index].oldPosition; + line.recalculate = false; + line.dirty = false; + } + best = best.previous; + } + newPositions(lines, begin, end, step, flag); + return step; + }; + + /** Choose new positions for lines based on calculated prices + * + * @param lines + * @param size + * @param begin1 + * @param end1 + * @param step + * @param newPosition + * @param flag + */ + function newPositions(lines, begin1, end1, step, flag) { + var delta = 0; + var size = lines.length; + var begin = 0; + var end = 0; + var s = -1; + var e = -1; + if (begin1 > end1) { + begin1 = end1; + } + while (s < size - 1) { + e = s + 1; + if (lines[e].recalculate) { + while (e < size) { + if (!lines[e].recalculate) { + break; + } + e++; + } + if (e < size) { + end = lines[e].newPosition; + } else { + end = end1; + } + if (s < 0) { + begin = begin1; + } else { + begin = lines[s].newPosition; + } + if (e === size && begin > end) { + end = begin; + } + if (e - s === 2) { + delta = (end - begin) * 0.5; + } else { + delta = (end - begin) / (e - s); + } + switch (flag) { + case 1: + for (s++; s < e; s++) { + begin += delta; + lines[s].newPosition = begin; + lines[s].priority = + 1 / (1 + Math.abs(lines[s].oldPosition - begin) * step); + } + break; + case 2: + for (s++; s < e; s++) { + begin += delta; + lines[s].newPosition = begin; + lines[s].priority = Math.abs(lines[s].oldPosition - begin) * step; + } + break; + default: + for (s++; s < e; s++) { + begin += delta; + lines[s].newPosition = begin; + lines[s].priority = 1.0; + } + break; + } + } + s = e; + } + } + + /** Populate symmetry data into relocation table + * + * @param lines + * @param symi + * @param symPosition + * @param step + */ + function prepareSymmetry(lines, symi, symPosition, step) { + var i; + var j = 0; + var tmp; + var abs; + var distance; + var newPosition; + var size = lines.length; + var max = size - RANGE - 1; + var min = RANGE; + var istart = 0; + var line = null; + var otherLine = null; + var symj = 2 * symi - size; + symPosition *= 2; + if (symj < 0) { + symj = 0; + } + distance = step * RANGE; + for (i = symj; i < symi; i++) { + line = lines[i]; + if (line.symTo !== -1) { + continue; + } + newPosition = line.newPosition; + line.symTo = 2 * symi - i; + if (line.symTo > max) { + line.symTo = max; + } + j = line.symTo - istart > RANGE ? -RANGE : -line.symTo + istart; + if (line.recalculate) { + while (j < RANGE && line.symTo + j < size - 1) { + tmp = symPosition - lines[line.symTo + j].newPosition; + abs = Math.abs(tmp - newPosition); + if (abs < distance) { + if ( + (i === 0 || tmp > lines[i - 1].newPosition) && + tmp < lines[i + 1].newPosition + ) { + distance = abs; + min = j; + } + } else if (tmp < newPosition) { + break; + } + j++; + } + } else { + while (j < RANGE && line.symTo + j < size - 1) { + if (line.recalculate) { + tmp = symPosition - lines[line.symTo + j].newPosition; + abs = Math.abs(tmp - newPosition); + if (abs < distance) { + if ( + (i === 0 || tmp > lines[i - 1].newPosition) && + tmp < lines[i + 1].newPosition + ) { + distance = abs; + min = j; + } + } else if (tmp < newPosition) { + break; + } + } + j++; + } + } + line.symTo += min; + otherLine = lines[line.symTo]; + if ( + min === RANGE || + line.symTo <= symi || + otherLine.symTo !== -1 || + otherLine.symRef !== -1 + ) { + line.symTo = -1; + continue; + } + if (!line.recalculate) { + line.symTo = -1; + if (otherLine.symTo !== -1 || !otherLine.recalculate) { + continue; + } + otherLine.symIndex = line.symIndex; + otherLine.symTo = i; + istart = line.symTo - 1; + otherLine.recalculate = false; + otherLine.dirty = true; + line.symRef = line.symTo; + otherLine.newPosition = symPosition - line.newPosition; + } else { + if (otherLine.symTo !== -1) { + line.symTo = -1; + continue; + } + line.symIndex = otherLine.symIndex; + istart = line.symTo - 1; + line.recalculate = false; + line.dirty = true; + otherLine.symRef = i; + line.newPosition = symPosition - otherLine.newPosition; + } + } + } + + /** Optimized array copy using Duff's Device. + * + * @param from {Array} source array + * @param fromOffset {number} offset into source array + * @param to {Array} idealPos array + * @param toOffset {number} offset into idealPos array + * @param length {number} elements to copy + */ + function arrayCopy(from, fromOffset, to, toOffset, length) { + var n = length % 8; + while (n--) { + to[toOffset++] = from[fromOffset++]; + } + n = (length / 8) | 0; + while (n--) { + to[toOffset++] = from[fromOffset++]; + to[toOffset++] = from[fromOffset++]; + to[toOffset++] = from[fromOffset++]; + to[toOffset++] = from[fromOffset++]; + to[toOffset++] = from[fromOffset++]; + to[toOffset++] = from[fromOffset++]; + to[toOffset++] = from[fromOffset++]; + to[toOffset++] = from[fromOffset++]; + } + } + + /** Apply previously calculated symmetry to image */ + ZoomContext.prototype.doSymmetry = function () { + var from_offset = 0; + var to_offset = 0; + var i; + var j = 0; + var buffer = this.image.newBuffer; + var bufferWidth = this.image.width; + for (i = 0; i < this.rows.length; i++) { + if (this.rows[i].symTo >= 0 && !this.rows[this.rows[i].symTo].dirty) { + from_offset = this.rows[i].symTo * bufferWidth; + arrayCopy(buffer, from_offset, buffer, to_offset, bufferWidth); + this.rows[i].dirty = false; + } + to_offset += bufferWidth; + } + for (i = 0; i < this.columns.length; i++) { + if ( + this.columns[i].symTo >= 0 && + !this.columns[this.columns[i].symTo].dirty + ) { + to_offset = i; + from_offset = this.columns[i].symTo; + for (j = 0; j < this.rows.length; j++) { + buffer[to_offset] = buffer[from_offset]; + to_offset += bufferWidth; + from_offset += bufferWidth; + } + this.columns[i].dirty = false; + } + } + }; + + /** Build an optimized move table based on relocation table */ + ZoomContext.prototype.prepareMove = function () { + var move = null; + var i = 0; + var j = 0; + var s = 0; + while (i < this.columns.length) { + if (!this.columns[i].dirty) { + move = this.moveTable[s]; + move.to = i; + move.length = 1; + move.from = this.columns[i].symIndex; + for (j = i + 1; j < this.columns.length; j++) { + if ( + this.columns[j].dirty || + j - this.columns[j].symIndex !== move.to - move.from + ) { + break; + } + move.length++; + } + i = j; + s++; + } else { + i++; + } + } + move = this.moveTable[s]; + move.length = 0; + }; + + /** Execute moves defined in move table */ + ZoomContext.prototype.doMove = function () { + var move = null; + var newOffset = 0; + var oldOffset = 0; + var from = 0; + var to = 0; + var i; + var s = 0; + var length = 0; + var newBuffer = this.image.newBuffer; + var oldBuffer = this.image.oldBuffer; + var bufferWidth = this.image.width; + for (i = 0; i < this.rows.length; i++) { + if (!this.rows[i].dirty) { + s = 0; + oldOffset = this.rows[i].symIndex * bufferWidth; + while ((move = this.moveTable[s]).length > 0) { + from = oldOffset + move.from; + to = newOffset + move.to; + length = move.length; + arrayCopy(oldBuffer, from, newBuffer, to, length); + s++; + } + } + newOffset += bufferWidth; + } + }; + + /** Shortcut for prepare and execute move */ + ZoomContext.prototype.movePixels = function () { + this.prepareMove(); + this.doMove(); + }; + + /** Prepare fill table based on relocation table */ + ZoomContext.prototype.prepareFill = function () { + var fill = null; + var i; + var j = 0; + var k = 0; + var s = 0; + var n = 0; + for (i = 0; i < this.columns.length; i++) { + if (this.columns[i].dirty) { + j = i - 1; + for ( + k = i + 1; + k < this.columns.length && this.columns[k].dirty; + k++ + ) {} + while (i < this.columns.length && this.columns[i].dirty) { + if ( + k < this.columns.length && + (j < i || + this.columns[i].newPosition - this.columns[j].newPosition > + this.columns[k].newPosition - this.columns[i].newPosition) + ) { + j = k; + } else if (j < 0) { + break; + } + n = k - i; + fill = this.fillTable[s]; + fill.length = n; + fill.from = j; + fill.to = i; + while (n > 0) { + this.columns[i].newPosition = this.columns[j].newPosition; + this.columns[i].dirty = false; + n--; + i++; + } + s++; + } + } + } + fill = this.fillTable[s]; + fill.length = 0; + }; + + /** Apply fill table */ + ZoomContext.prototype.doFill = function () { + var fill = null; + var from_offset = 0; + var to_offset = 0; + var from = 0; + var to = 0; + var i; + var j = 0; + var k = 0; + var t = 0; + var s = 0; + var d = 0; + var buffer = this.image.newBuffer; + var bufferWidth = this.image.width; + for (i = 0; i < this.rows.length; i++) { + if (this.rows[i].dirty) { + j = i - 1; + for (k = i + 1; k < this.rows.length && this.rows[k].dirty; k++) {} + while (i < this.rows.length && this.rows[i].dirty) { + if ( + k < this.rows.length && + (j < i || + this.rows[i].newPosition - this.rows[j].newPosition > + this.rows[k].newPosition - this.rows[i].newPosition) + ) { + j = k; + } else if (j < 0) { + break; + } + to_offset = i * bufferWidth; + from_offset = j * bufferWidth; + if (!this.rows[j].dirty) { + s = 0; + while ((fill = this.fillTable[s]).length > 0) { + from = from_offset + fill.from; + to = from_offset + fill.to; + for (t = 0; t < fill.length; t++) { + d = to + t; + buffer[d] = buffer[from]; + } + s++; + } + } + arrayCopy(buffer, from_offset, buffer, to_offset, bufferWidth); + this.rows[i].newPosition = this.rows[j].newPosition; + this.rows[i].dirty = true; + i++; + } + } else { + s = 0; + from_offset = i * bufferWidth; + while ((fill = this.fillTable[s]).length > 0) { + from = from_offset + fill.from; + to = from_offset + fill.to; + for (t = 0; t < fill.length; t++) { + d = to + t; + buffer[d] = buffer[from]; + } + s++; + } + this.rows[i].dirty = true; + } + } + }; + + /** Shortcut to prepare and apply fill table */ + ZoomContext.prototype.fill = function () { + this.prepareFill(); + this.doFill(); + }; + + /** Render line using solid guessing + * + * @param row + */ + ZoomContext.prototype.renderRow = function (row) { + var buffer = this.image.newBuffer; + var bufferWidth = this.image.width; + var newPosition = row.newPosition; + var r = row.index; + var offset = r * bufferWidth; + var i; + var j; + var k; + var n; + var distl; + var distr; + var distu; + var distd; + var offsetu; + var offsetd; + var offsetl; + var offsetul; + var offsetur; + var offsetdl; + var offsetdr; + var rend = r - GUESS_RANGE; + var length; + var current; + if (rend < 0) { + rend = 0; + } + for (i = r - 1; i >= rend && this.rows[i].dirty; i--) {} + distu = r - i; + rend = r + GUESS_RANGE; + if (rend >= this.rows.length) { + rend = this.rows.length - 1; + } + for (j = r + 1; j < rend && this.rows[j].dirty; j++) {} + distd = j - r; + if ( + !USE_SOLIDGUESS || + i < 0 || + j >= this.rows.length || + this.rows[i].dirty || + this.rows[j].dirty + ) { + for (k = 0, length = this.columns.length; k < length; k++) { + current = this.columns[k]; + if (!this.columns[k].dirty) { + buffer[offset] = this.fractal.formula( + current.newPosition, + newPosition + ); + } + offset++; + } + } else { + distr = 0; + distl = Number.MAX_VALUE / 2; + offsetu = offset - distu * bufferWidth; + offsetd = offset + distd * bufferWidth; + for (k = 0, length = this.columns.length; k < length; k++) { + current = this.columns[k]; + if (!this.columns[k].dirty) { + if (distr <= 0) { + rend = k + GUESS_RANGE; + if (rend >= this.columns.length) { + rend = this.columns.length - 1; + } + for (j = k + 1; j < rend && this.columns[j].dirty; j++) { + distr = j - k; + } + if (j >= rend) { + distr = Number.MAX_VALUE / 2; + } + } + if (distr < Number.MAX_VALUE / 4 && distl < Number.MAX_VALUE / 4) { + offsetl = offset - distl; + offsetul = offsetu - distl; + offsetdl = offsetd - distl; + offsetur = offsetu + distr; + offsetdr = offsetd + distr; + n = buffer[offsetl]; + if ( + n == buffer[offsetu] && + n == buffer[offsetd] && + n == buffer[offsetul] && + n == buffer[offsetur] && + n == buffer[offsetdl] && + n == buffer[offsetdr] + ) { + buffer[offset] = n; + } else { + buffer[offset] = this.fractal.formula( + current.newPosition, + newPosition + ); + } + } else { + buffer[offset] = this.fractal.formula( + current.newPosition, + newPosition + ); + } + distl = 0; + } + offset++; + offsetu++; + offsetd++; + distr--; + distl++; + } + } + row.recalculate = false; + row.dirty = false; + }; + + /** Render column using solid guessing + * + * @param column + */ + ZoomContext.prototype.renderColumn = function (column) { + var buffer = this.image.newBuffer; + var bufferWidth = this.image.width; + var newPosition = column.newPosition; + var r = column.index; + var offset = r; + var rend = r - GUESS_RANGE; + var i; + var j; + var k; + var n; + var distl; + var distr; + var distu; + var distd; + var offsetl; + var offsetr; + var offsetu; + var offsetlu; + var offsetru; + var offsetld; + var offsetrd; + var sumu; + var sumd; + var length; + var current; + if (rend < 0) { + rend = 0; + } + for (i = r - 1; i >= rend && this.columns[i].dirty; i--) {} + distl = r - i; + rend = r + GUESS_RANGE; + if (rend >= this.columns.length) { + rend = this.columns.length - 1; + } + for (j = r + 1; j < rend && this.columns[j].dirty; j++) {} + distr = j - r; + if ( + !USE_SOLIDGUESS || + i < 0 || + j >= this.columns.length || + this.columns[i].dirty || + this.columns[j].dirty + ) { + for (k = 0, length = this.rows.length; k < length; k++) { + current = this.rows[k]; + if (!this.rows[k].dirty) { + buffer[offset] = this.fractal.formula( + newPosition, + current.newPosition + ); + } + offset += bufferWidth; + } + } else { + distd = 0; + distu = Number.MAX_VALUE / 2; + offsetl = offset - distl; + offsetr = offset + distr; + for (k = 0, length = this.rows.length; k < length; k++) { + current = this.rows[k]; + if (!this.rows[k].dirty) { + if (distd <= 0) { + rend = k + GUESS_RANGE; + if (rend >= this.rows.length) { + rend = this.rows.length - 1; + } + for (j = k + 1; j < rend && this.rows[j].dirty; j++) { + distd = j - k; + } + if (j >= rend) { + distd = Number.MAX_VALUE / 2; + } + } + if (distd < Number.MAX_VALUE / 4 && distu < Number.MAX_VALUE / 4) { + sumu = distu * bufferWidth; + sumd = distd * bufferWidth; + offsetu = offset - sumu; + offsetlu = offsetl - sumu; + offsetru = offsetr - sumu; + offsetld = offsetl + sumd; + offsetrd = offsetr + sumd; + n = buffer[offsetu]; + if ( + n == buffer[offsetl] && + n == buffer[offsetr] && + n == buffer[offsetlu] && + n == buffer[offsetru] && + n == buffer[offsetld] && + n == buffer[offsetrd] + ) { + buffer[offset] = n; + } else { + buffer[offset] = this.fractal.formula( + newPosition, + current.newPosition + ); + } + } else { + buffer[offset] = this.fractal.formula( + newPosition, + current.newPosition + ); + } + distu = 0; + } + offset += bufferWidth; + offsetl += bufferWidth; + offsetr += bufferWidth; + distd--; + distu++; + } + } + column.recalculate = false; + column.dirty = false; + }; + + /** Calculate whether we're taking too long to render the fractal to meet the idealPos FPS */ + ZoomContext.prototype.tooSlow = function () { + var newTime = new Date().getTime(), + minFPS = this.zooming ? this.minFPS : 10; + return 1000 / (newTime - this.startTime + this.fudgeFactor) < minFPS; + }; + + /** Prioritize calculation of lines between begin and end + * + * @param lines - rows or columns to prioritize + * @param begin - index of first line to prioritize + * @param end - index of last line to prioritize + */ + function calcPriority(lines, begin, end) { + var middle; + while (begin < end) { + middle = begin + ((end - begin) >> 1); + lines[middle].priority = + (lines[end].newPosition - lines[middle].newPosition) * + lines[middle].priority; + if (lines[middle].symRef !== -1) { + lines[middle].priority /= 2.0; + } + calcPriority(lines, begin, middle); + begin = middle + 1; + } + } + + /** Enqueue all the lines to be recalculated and set their priority + * + * @param lines - lines to enqueue for calculation + */ + ZoomContext.prototype.enqueueCalculations = function (lines) { + var i; + var j = 0; + for (i = 0; i < lines.length; i++) { + if (lines[i].recalculate) { + for (j = i; j < lines.length && lines[j].recalculate; j++) { + this.queue[this.queueLength++] = lines[j]; + } + if (j === lines.length) { + j -= 1; + } + calcPriority(lines, i, j); + i = j; + } + } + }; + + /** Sort calculation queue according to priority (using quicksort) + * + * @param queue + * @param l + * @param r + */ + function sortQueue(queue, l, r) { + var m = (queue[l].priority + queue[r].priority) / 2.0; + var tmp = null; + var i = l; + var j = r; + do { + while (queue[i].priority > m) { + i++; + } + while (queue[j].priority < m) { + j--; + } + if (i <= j) { + tmp = queue[i]; + queue[i] = queue[j]; + queue[j] = tmp; + i++; + j--; + } + } while (j >= i); + if (l < j) { + sortQueue(queue, l, j); + } + if (r > i) { + sortQueue(queue, i, r); + } + } + + /** Process the relocation table */ + ZoomContext.prototype.calculate = function () { + var i, newTime; + this.incomplete = false; + this.queueLength = 0; + this.enqueueCalculations(this.columns); + this.enqueueCalculations(this.rows); + if (this.queueLength > 0) { + if (this.queueLength > 1) { + sortQueue(this.queue, 0, this.queueLength - 1); + } + for (i = 0; i < this.queueLength; i++) { + if (this.queue[i].isRow) { + this.renderRow(this.queue[i]); + } else { + this.renderColumn(this.queue[i]); + } + if (!this.recalculate && this.tooSlow() && i < this.queueLength) { + this.incomplete = true; + this.fill(); + break; + } + } + } + }; + + /** Update newPosition array with newly calculated positions */ + ZoomContext.prototype.updatePosition = function () { + var k; + var len; + for (k = 0, len = this.columns.length; k < len; k++) { + this.columns[k].oldPosition = this.columns[k].newPosition; + } + for (k = 0, len = this.rows.length; k < len; k++) { + this.rows[k].oldPosition = this.rows[k].newPosition; + } + }; + + /** Calculate FPS achieved and determine if fudge factor needs adjustment for next frame */ + ZoomContext.prototype.updateFPS = function () { + var fps = 1000 / (new Date().getTime() - this.startTime); + if (fps < this.minFPS) { + this.fudgeFactor++; + } else if (fps > this.minFPS + 10 && this.fudgeFactor > 0) { + this.fudgeFactor--; + } + console.log(fps + " fps"); + }; + + /** Overall fractal drawing workflow, calls other functions */ + ZoomContext.prototype.drawFractal = function (recalculate) { + var area = this.convertArea(); + var symx = this.fractal.symmetry && this.fractal.symmetry.x; + var symy = this.fractal.symmetry && this.fractal.symmetry.y; + var stepx, stepy; + this.startTime = new Date().getTime(); + this.recalculate = recalculate; + if (recalculate || !USE_XAOS) { + stepx = this.initialize(this.columns, area.begin.x, area.end.x, false); + stepy = this.initialize(this.rows, area.begin.y, area.end.y, true); + } else { + stepx = this.approximate(this.columns, area.begin.x, area.end.x); + stepy = this.approximate(this.rows, area.begin.y, area.end.y); + } + if ( + USE_SYMMETRY && + typeof symy === "number" && + !(area.begin.y > symy || symy > area.end.y) + ) { + prepareSymmetry( + this.rows, + Math.floor((symy - area.begin.y) / stepy), + symy, + stepy + ); + } + if ( + USE_SYMMETRY && + typeof symx === "number" && + !(area.begin.x > symx || symx > area.end.x) + ) { + prepareSymmetry( + this.columns, + Math.floor((symx - area.begin.x) / stepx), + symx, + stepx + ); + } + this.image.swapBuffers(); + this.movePixels(); + this.calculate(); + if ( + (USE_SYMMETRY && typeof symx === "number") || + typeof symy === "number" + ) { + this.doSymmetry(); + } + this.image.paint(); + this.updatePosition(); + this.updateFPS(); + }; + + /** Adjust display region to zoom based on mouse buttons */ + ZoomContext.prototype.updateRegion = function (mouse) { + var MAXSTEP = 0.008 * 3; + var MUL = 0.3; + var area = this.convertArea(); + var x = + area.begin.x + mouse.x * ((area.end.x - area.begin.x) / this.image.width); + var y = + area.begin.y + + mouse.y * ((area.end.y - area.begin.y) / this.image.height); + var deltax = + (mouse.oldx - mouse.x) * ((area.end.x - area.begin.x) / this.image.width); + var deltay = + (mouse.oldy - mouse.y) * + ((area.end.y - area.begin.y) / this.image.height); + var step; + var mmul; + if (mouse.button[1] || (mouse.button[0] && mouse.button[2])) { + // Pan when middle or left+right buttons are pressed + step = 0; + } else if (mouse.button[0]) { + // Zoom in when left button is pressed + step = MAXSTEP * 2; + } else if (mouse.button[2]) { + // Zoom out when right button is pressed + step = -MAXSTEP * 2; + } else { + this.zooming = false; + return; + } + mmul = Math.pow(1 - step, MUL); + area.begin.x = x + (area.begin.x - x) * mmul; + area.end.x = x + (area.end.x - x) * mmul; + area.begin.y = y + (area.begin.y - y) * mmul; + area.end.y = y + (area.end.y - y) * mmul; + this.fractal.region.radius.x = area.end.x - area.begin.x; + this.fractal.region.radius.y = area.end.y - area.begin.y; + this.fractal.region.center.x = (area.begin.x + area.end.x) / 2; + this.fractal.region.center.y = + ((area.begin.y + area.end.y) / 2) * + (this.image.width / this.image.height); + this.zooming = true; + }; + + /** Attaches zoomer to specified canvas */ + return function (canvas, fractal) { + var image = new CanvasImage(canvas); + var zoomer = new ZoomContext(image, fractal); + var mouse = { x: 0, y: 0, button: [false, false, false] }; + + function doZoom() { + zoomer.updateRegion(mouse); + if (zoomer.zooming || zoomer.incomplete) { + requestAnimationFrame(doZoom); + zoomer.drawFractal(false); + } + } + + canvas.ontouchstart = function (e) { + if (e.touches.length < 3) { + var touch = e.touches[0]; + e.touches.length == 2 + ? (mouse.button[2] = true) + : (mouse.button[2] = false); + var mouseEvent = new MouseEvent("mousedown", { + clientX: touch.clientX, + clientY: touch.clientY, + }); + canvas.dispatchEvent(mouseEvent); + } + }; + + canvas.ontouchend = function (e) { + var mouseEvent = new MouseEvent("mouseup", {}); + canvas.dispatchEvent(mouseEvent); + }; + + canvas.ontouchmove = function (e) { + var touch = e.touches[0]; + var mouseEvent = new MouseEvent("mousemove", { + clientX: touch.clientX, + clientY: touch.clientY, + }); + canvas.dispatchEvent(mouseEvent); + }; + + canvas.onmousedown = function (e) { + mouse.button[e.button] = true; + mouse.x = e.offsetX || e.clientX - canvas.offsetLeft; + mouse.y = e.offsetY || e.clientY - canvas.offsetTop; + mouse.oldx = e.offsetX || e.clientX - canvas.offsetLeft; + mouse.oldy = e.offsetY || e.clientY - canvas.offsetTop; + doZoom(); + }; + + canvas.onmouseup = function (e) { + mouse.button[e.button] = false; + }; + + canvas.onmousemove = function (e) { + mouse.x = e.offsetX || e.clientX - canvas.offsetLeft; + mouse.y = e.offsetY || e.clientY - canvas.offsetTop; + }; + + canvas.oncontextmenu = function () { + return false; + }; + + canvas.onmouseout = function () { + mouse.button = [false, false, false]; + }; + + zoomer.drawFractal(true); + }; +})(); + +/** Create the default XaoS color palette */ +xaos.defaultPalette = function () { + var MAXENTRIES = 65536; + var segmentsize = 8; + var setsegments = Math.floor((MAXENTRIES + 3) / segmentsize); + var nsegments = Math.floor(255 / segmentsize); + var segments = [ + [0, 0, 0], + [120, 119, 238], + [24, 7, 25], + [197, 66, 28], + [29, 18, 11], + [135, 46, 71], + [24, 27, 13], + [241, 230, 128], + [17, 31, 24], + [240, 162, 139], + [11, 4, 30], + [106, 87, 189], + [29, 21, 14], + [12, 140, 118], + [10, 6, 29], + [50, 144, 77], + [22, 0, 24], + [148, 188, 243], + [4, 32, 7], + [231, 146, 14], + [10, 13, 20], + [184, 147, 68], + [13, 28, 3], + [169, 248, 152], + [4, 0, 34], + [62, 83, 48], + [7, 21, 22], + [152, 97, 184], + [8, 3, 12], + [247, 92, 235], + [31, 32, 16], + ]; + var i, y; + var r, g, b; + var rs, gs, bs; + var palette = []; + + for (i = 0; i < setsegments; i++) { + r = segments[i % nsegments][0]; + g = segments[i % nsegments][1]; + b = segments[i % nsegments][2]; + rs = (segments[((i + 1) % setsegments) % nsegments][0] - r) / segmentsize; + gs = (segments[((i + 1) % setsegments) % nsegments][1] - g) / segmentsize; + bs = (segments[((i + 1) % setsegments) % nsegments][2] - b) / segmentsize; + for (y = 0; y < segmentsize; y++) { + palette.push((255 << 24) | (b << 16) | (g << 8) | r); + r += rs; + g += gs; + b += bs; + } + } + return new Uint32Array(palette); +}; + +xaos.mandelbrot = { + symmetry: { x: null, y: 0 }, + region: { + center: { x: -0.75, y: 0.0 }, + radius: { x: 2.5, y: 2.5 }, + angle: 0, + }, + z0: { x: 0, y: 0 }, + maxiter: 512, + bailout: 4, + formula: function (cr, ci) { + var maxiter = this.maxiter, + bailout = this.bailout, + zr = this.z0.x, + zi = this.z0.y, + i = maxiter; + + while (i--) { + var zr2 = zr * zr; + var zi2 = zi * zi; + + if (zr2 + zi2 > bailout) { + return this.palette[(maxiter - i) % this.palette.length]; + } + + zi = ci + 2 * zr * zi; + zr = cr + zr2 - zi2; + } + + return this.palette[0]; + }, + palette: xaos.defaultPalette(), +}; + +xaos.zoom(document.getElementById("canvas"), xaos.mandelbrot); diff --git a/index/games/fractal/style.css b/index/games/fractal/style.css index 645195c..9ada16e 100755 --- a/index/games/fractal/style.css +++ b/index/games/fractal/style.css @@ -1,67 +1,67 @@ #controls { - position: relative; - margin-bottom: 2.5em; + position: relative; + margin-bottom: 2.5em; } #canvas { - width: 100%; - height: 100vh; - margin-bottom: 0.5em; - display: inline-block; - vertical-align: baseline; + width: 100%; + height: 100vh; + margin-bottom: 0.5em; + display: inline-block; + vertical-align: baseline; } #fullScreenButton { - position: absolute; - height: 100vh; - width: 100%; - bottom: 3rem; - visibility: hidden; + position: absolute; + height: 100vh; + width: 100%; + bottom: 3rem; + visibility: hidden; } @media only screen and (max-width: 600px) { - #fullScreenButton { - visibility: visible; - opacity: 0; - } + #fullScreenButton { + visibility: visible; + opacity: 0; + } - #saveCanvasButton { - visibility: hidden; - } + #saveCanvasButton { + visibility: hidden; + } } #resetButton { - position: absolute; - left: 0em; + position: absolute; + left: 0em; } #saveCanvasButton { - position: absolute; - right: 0.1em; - visibility: visible; + position: absolute; + right: 0.1em; + visibility: visible; } .container { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; } @media (min-width: 768px) { - .container { - width: 750px; - } + .container { + width: 750px; + } } @media (min-width: 992px) { - .container { - width: 970px; - } + .container { + width: 970px; + } } @media (min-width: 1200px) { - .container { - width: 1170px; - } -} \ No newline at end of file + .container { + width: 1170px; + } +} diff --git a/index/games/index.html b/index/games/index.html index d61e05d..63f485e 100755 --- a/index/games/index.html +++ b/index/games/index.html @@ -1,67 +1,81 @@ + + + + + Games + + + - - - - - Games - - - + + +
+
+

+ Rock Paper Scissors 🪨📄✂️ +

+ + The first game I ever made! I wrote this when I first began learning + Javascript, to be specific I wrote the logic during that time. The + game was only playable through the terminal, now its fairly portable + and can be integrated into anything. + +
-
+
+
+

+ Eternal Space +

+ + Currently only playable on a pc, I haven't tried figuring out how to + scale down the game lol. I participated in the Opera GX + GameMaker + game jam and created this monstrosity. The game jam was alien themed, + however I never got that far. Before taking part in this game jam I + have never programmed an actual game nor have been in a game jam. I + learned how to program in GameMaker Language (basically Javascript) + during the event. + +
-
-

Rock Paper Scissors 🪨📄✂️

- - The first game I ever made! I wrote this when I first began learning Javascript, to be specific I wrote the logic - during that time. The game was only playable through the terminal, now its fairly portable and can be integrated - into anything. - -
+
-
+
+

+ Fractal +

+ + Interactive Javascript real-time fractal renderer using XaoS.js. Works + best on a PC. + +
-
-

Eternal Space

- - Currently only playable on a pc, I haven't tried figuring out how to scale down the game lol. I participated in - the Opera GX + GameMaker game jam and created this monstrosity. The game jam was alien themed, however I never got - that far. Before taking part in this game jam I have never programmed an actual game nor have been in a game jam. - I learned how to program in GameMaker Language (basically Javascript) during the event. - -
- -
- -
-

Fractal

- - Interactive Javascript real-time fractal renderer using XaoS.js. Works best on a PC. - -
- - -
- You have reached the end of the page. (ノ◕ヮ◕)ノ*:・゚✧ -
-
- - +
+ You have reached the end of the page. (ノ◕ヮ◕)ノ*:・゚✧ +
+
+ diff --git a/index/games/rps/index.html b/index/games/rps/index.html index ada772d..06c3bd1 100755 --- a/index/games/rps/index.html +++ b/index/games/rps/index.html @@ -1,49 +1,51 @@ + + + + + Rock Paper Scissors + + + - - - - - Rock Paper Scissors - - - + + +
+
+
+

+

+
-
-
-
-

-

-
+
+ + + + +
-
- - - - -
- -
-

- -

-

-
-
-
- You have reached the end of the page. (ノ◕ヮ◕)ノ*:・゚✧ -
-
- - - \ No newline at end of file +
+

+ +

+

+
+
+
+ You have reached the end of the page. (ノ◕ヮ◕)ノ*:・゚✧ +
+
+ + diff --git a/index/games/rps/script/script.js b/index/games/rps/script/script.js index 3935445..b555617 100755 --- a/index/games/rps/script/script.js +++ b/index/games/rps/script/script.js @@ -90,4 +90,4 @@ function playGame() { function play(input) { getUserChoice(input); playGame(); -} \ No newline at end of file +} diff --git a/index/projects/index.html b/index/projects/index.html index 3f874db..769dc28 100755 --- a/index/projects/index.html +++ b/index/projects/index.html @@ -1,116 +1,116 @@ + + + + + My Projects + + + - - - - - My Projects - - - + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ProjectDescriptionLink (local)
Dark New RomanA custom serif font based off of Times New Roman. + Download font (truetype 28.9 KiB) +
IP address gabberGrabs your IPv4 (Internet Protocol) Address. + ip-grabber +
Number System Converter (web)Converts decimal numbers into other bases! + nsc-web +
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
ProjectDescriptionLink (local)
Dark New Roman - A custom serif font based off of Times New Roman. - - Download font (truetype 28.9 KiB) -
IP address gabber - Grabs your IPv4 (Internet Protocol) Address. - - ip-grabber -
Number System Converter (web) - Converts decimal numbers into other bases! - - nsc-web -
+
+
-
-
+ + + + + + + + + + + + + + + + + + + + +
ProjectDescriptionLink (external)
Firefox black & red triangle themeA red and black theme for firefox tabs. + https://addons.mozilla.org/en-US/firefox/addon/black-red-triangle-theme/ +
RGB Progress BarMakes progress bars cycle through different RGB colors. + https://addons.mozilla.org/addon/rgb-progress-bar/ +
- - - - - - - - - - - - - - - - - - - - -
ProjectDescriptionLink (external)
Firefox black & red triangle theme - A red and black theme for firefox tabs. - - https://addons.mozilla.org/en-US/firefox/addon/black-red-triangle-theme/ -
RGB Progress Bar - Makes progress bars cycle through different RGB colors. - - https://addons.mozilla.org/addon/rgb-progress-bar/ -
+
+
-
-
+ + + + + + + + + + + + +
Github contributionDescriptionRepository linkLanguage
- - - - - - - - - - - - -
Github contributionDescriptionRepository linkLanguage
- - -
- You have reached the end of the page. (ノ◕ヮ◕)ノ*:・゚✧ -
-
- - +
+ You have reached the end of the page. (ノ◕ヮ◕)ノ*:・゚✧ +
+
+ diff --git a/index/projects/ip-grabber/index.html b/index/projects/ip-grabber/index.html index 4c252a5..dde7a5d 100755 --- a/index/projects/ip-grabber/index.html +++ b/index/projects/ip-grabber/index.html @@ -1,57 +1,72 @@ + + + + + IP Grabber + + + + - - - - - IP Grabber - - - - + + - -
- -
-

Recently Logged:

- - Embed not showing? Download here. - -
-
-

Why?

-

- I never created an app that uses both server-side and frontend code, so this is a learning experience. -

-
-

- You mad bro?, Problem? -

-
- You have reached the end of the page. (ノ◕ヮ◕)ノ*:・゚✧ -
-
- - - \ No newline at end of file +
+ You have reached the end of the page. (ノ◕ヮ◕)ノ*:・゚✧ +
+ + + diff --git a/index/projects/nsc-web/index.html b/index/projects/nsc-web/index.html index 4322730..70c1f43 100755 --- a/index/projects/nsc-web/index.html +++ b/index/projects/nsc-web/index.html @@ -1,54 +1,84 @@ - - + - - + + Number System Converter - + - + - +
-
-

Enter a decimal number to convert and a base.

-
-
- -
-
- -
-
- -
-
- -
-
+
+

Enter a decimal number to convert and a base.

+
+
+ +
+
+ +
+
+ +
+
+ +
+
-
- You have reached the end of the page. (ノ◕ヮ◕)ノ*:・゚✧ -
+
+ You have reached the end of the page. (ノ◕ヮ◕)ノ*:・゚✧ +
- - - \ No newline at end of file + + diff --git a/index/projects/nsc-web/script/script.js b/index/projects/nsc-web/script/script.js index 2def6e8..d05d4a8 100755 --- a/index/projects/nsc-web/script/script.js +++ b/index/projects/nsc-web/script/script.js @@ -1,22 +1,19 @@ numberSystemConverter(); function numberSystemConverter() { + let num = document.getElementById("number").value; + let base = document.getElementById("base").value; + let new_base = document.getElementById("new-base").value; - let num = document.getElementById('number').value; - let base = document.getElementById('base').value; - let new_base = document.getElementById('new-base').value; + base = parseInt(base); + new_base = parseInt(new_base); - base = parseInt(base); - new_base = parseInt(new_base); + try { + let dec_num = parseInt(num, base); + let new_num = dec_num.toString(new_base); - try { - - let dec_num = parseInt(num, base); - let new_num = dec_num.toString(new_base); - - document.getElementById('converted').value = new_num; - console.log(`BASE ${base}: ${num} ==> BASE ${new_base}: ${new_num}`); - - } catch (RangeError) { - console.log(`One or more bases is not in the range of 2 to 36.`); - } -}; \ No newline at end of file + document.getElementById("converted").value = new_num; + console.log(`BASE ${base}: ${num} ==> BASE ${new_base}: ${new_num}`); + } catch (RangeError) { + console.log(`One or more bases is not in the range of 2 to 36.`); + } +} diff --git a/index/projects/script/script.js b/index/projects/script/script.js index c997dba..861a71e 100755 --- a/index/projects/script/script.js +++ b/index/projects/script/script.js @@ -35,4 +35,4 @@ function requestUserRepos() { } }; xhr.send(); -} \ No newline at end of file +} diff --git a/index/socials/index.html b/index/socials/index.html index 7b0a4fe..7db02f3 100755 --- a/index/socials/index.html +++ b/index/socials/index.html @@ -1,43 +1,61 @@ - - + - - + + Socials - + - - + + - +
- - - + + +
- - - \ No newline at end of file + + diff --git a/index/webrings/index.html b/index/webrings/index.html index ab58840..e2a4522 100755 --- a/index/webrings/index.html +++ b/index/webrings/index.html @@ -1,104 +1,179 @@ - - + - - + + Webrings - - - + + + - +
-

What is a webring?

- A webring is a collection of sites that chained to each other where if you follow a link from one site and keep - going up the ring you will eventually find the site you started from. +

+ What is a webring? +

+ A webring is a collection of sites that chained to each other where if you + follow a link from one site and keep going up the ring you will eventually + find the site you started from. -
-
-
+
+
+
-
+
+
+
+
+ +
+
+ Retronaut +
+ Previous + Random + Next +
+
+ Hotline +
+ Previous + Next +
+
+ + Click for the [ + Random page + ] +
+ Want to join the ring? Click here for + info. + + + + + +
+
+ + + + + + +
+ Octo Ring logo
previousrandomnext
check out other GitHub profiles in the Octo Ring +
+
+
-
-
-
- -
-
- Retronaut -
- Previous - Random - Next -
-
- Hotline -
- Previous - Next -
-
- - Click for the [ - Random page ] -
- Want to join the ring? Click here for - info. - - - - - -
-
- - - - - - -
Octo Ring logo
previousrandomnext
check out other GitHub profiles in the Octo Ring
-
-
+
+
-
-
- -
-
- You have reached the end of the page. (ノ◕ヮ◕)ノ*:・゚✧ -
+
+
+ You have reached the end of the page. (ノ◕ヮ◕)ノ*:・゚✧ +
- - - \ No newline at end of file + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..b8da960 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "prettier": "2.8.4" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..a7c7592 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,17 @@ +lockfileVersion: 5.4 + +specifiers: + prettier: 2.8.4 + +devDependencies: + prettier: 2.8.4 + +packages: + /prettier/2.8.4: + resolution: + { + integrity: sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==, + } + engines: { node: ">=10.13.0" } + hasBin: true + dev: true diff --git a/style/alt.index.html b/style/alt.index.html index adf43fd..de771aa 100755 --- a/style/alt.index.html +++ b/style/alt.index.html @@ -1,30 +1,27 @@ - - + - - + + Array in a Matrix - - + + - +
- - + +
- - - \ No newline at end of file + + diff --git a/style/alt.style.css b/style/alt.style.css index ba2d66c..ae55ffe 100755 --- a/style/alt.style.css +++ b/style/alt.style.css @@ -61,7 +61,8 @@ body { font-family: Utopia, "Liberation Serif"; text-align: center; /* img size is 50x50 */ - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAABbmlDQ1BpY2MAACiRdZE7SwNBFIW/RCWikRRaBLHYQsVCJSiIpcbCJkiICr6aZPMSNuuyu0HEVrCxCFiINr4K/4G2gq2CICiCiI1/wFcjYb2TBBJEZ5m9H2fmXGbOgD9m6AWnOQIF07UT01FtYXFJC7wSIEwnw/iSumNNxuMx/h1f9/hUvRtSvf7f9+doT2ccHXytwmO6ZbvCE8KxdddSvCPcpeeTaeEj4UFbDih8rfRUlV8U56r8odieS0yBX/XUcg2camA9bxeEB4R7C0ZRr51H3SSYMednpXbL7MEhwTRRNFIUWcXAZUiqKZn97YtUfDOsiUeXv8UGtjhy5MU7KGpRumakZkXPyGewoXL/naeTHR2pdg9GoeXZ8977ILAL5ZLnfR97XvkEmp7g0qz71ySn8U/RS3Wt9xBCW3B+VddSe3CxDeFHK2knK1KTTH82C29n0LEInbfQtlzNqrbO6QPMbcoT3cD+AfTL/tDKD92VZ/plHO89AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAuElEQVRoBe3awQ2CQABEUbUEbJEEi5ISpQVETuY38LPkk5gwp9mdx9H7srz2dX3fpul5G/XZts959H2e5/14G/Z3nv+QGPYC/+M/Rv2ceO4uwkXsnIgtwP5EuIidE7EF2J8IF7FzIrYA+xPhInZOxBZgfyJcxM6J2ALsT4SL2DkRW4D9iXAROydiC7A/ES5i50RsAfYnwkXsnIgtwP5EuIidE7EF2J8IF7FzIrYA+y8j8rvYJf6v9QVf2KG69fWg3gAAAABJRU5ErkJggg==") repeat 0 0; + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAABbmlDQ1BpY2MAACiRdZE7SwNBFIW/RCWikRRaBLHYQsVCJSiIpcbCJkiICr6aZPMSNuuyu0HEVrCxCFiINr4K/4G2gq2CICiCiI1/wFcjYb2TBBJEZ5m9H2fmXGbOgD9m6AWnOQIF07UT01FtYXFJC7wSIEwnw/iSumNNxuMx/h1f9/hUvRtSvf7f9+doT2ccHXytwmO6ZbvCE8KxdddSvCPcpeeTaeEj4UFbDih8rfRUlV8U56r8odieS0yBX/XUcg2camA9bxeEB4R7C0ZRr51H3SSYMednpXbL7MEhwTRRNFIUWcXAZUiqKZn97YtUfDOsiUeXv8UGtjhy5MU7KGpRumakZkXPyGewoXL/naeTHR2pdg9GoeXZ8977ILAL5ZLnfR97XvkEmp7g0qz71ySn8U/RS3Wt9xBCW3B+VddSe3CxDeFHK2knK1KTTH82C29n0LEInbfQtlzNqrbO6QPMbcoT3cD+AfTL/tDKD92VZ/plHO89AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAuElEQVRoBe3awQ2CQABEUbUEbJEEi5ISpQVETuY38LPkk5gwp9mdx9H7srz2dX3fpul5G/XZts959H2e5/14G/Z3nv+QGPYC/+M/Rv2ceO4uwkXsnIgtwP5EuIidE7EF2J8IF7FzIrYA+xPhInZOxBZgfyJcxM6J2ALsT4SL2DkRW4D9iXAROydiC7A/ES5i50RsAfYnwkXsnIgtwP5EuIidE7EF2J8IF7FzIrYA+y8j8rvYJf6v9QVf2KG69fWg3gAAAABJRU5ErkJggg==") + repeat 0 0; -webkit-animation: bg-scrolling-reverse 0.92s infinite; /* Safari 4+ */ -moz-animation: bg-scrolling-reverse 0.92s infinite; @@ -88,8 +89,6 @@ body { font-family: "Montserrat", sans-serif; } -; - .project-name { color: --primary; font-family: Utopia, "Liberation Serif"; @@ -162,7 +161,7 @@ a:hover { border-color: rgba(255, 255, 255, 0.3); } -.btn+.btn { +.btn + .btn { margin-left: 1rem; } @@ -187,7 +186,7 @@ a:hover { font-size: 0.9rem; } - .btn+.btn { + .btn + .btn { margin-top: 1rem; margin-left: 0; } @@ -204,16 +203,16 @@ select, textarea { color: inherit; font: inherit; - margin: 0 + margin: 0; } button { - overflow: visible + overflow: visible; } button, select { - text-transform: none + text-transform: none; } button, @@ -221,21 +220,20 @@ html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; - cursor: pointer + cursor: pointer; } button[disabled], html input[disabled] { - cursor: default + cursor: default; } button::-moz-focus-inner, input::-moz-focus-inner { border: 0; - padding: 0 + padding: 0; } - .dropdown { position: relative; display: inline-block; @@ -259,23 +257,24 @@ input::-moz-focus-inner { @media screen and (min-width: 64em) { .project-tagline { - font-size: 1.25rem + font-size: 1.25rem; } } @media screen and (min-width: 42em) and (max-width: 64em) { .project-tagline { - font-size: 1.15rem + font-size: 1.15rem; } } @media screen and (max-width: 42em) { .project-tagline { - font-size: 1rem + font-size: 1rem; } } -footer, site-footer { +footer, +site-footer { font-family: "Montserrat", sans-serif; font-size: small; display: block; @@ -283,19 +282,19 @@ footer, site-footer { @media screen and (min-width: 64em) { .site-footer { - font-size: 1rem + font-size: 1rem; } } @media screen and (min-width: 42em) and (max-width: 64em) { .site-footer { - font-size: 1rem + font-size: 1rem; } } @media screen and (max-width: 42em) { .site-footer { - font-size: 0.9rem + font-size: 0.9rem; } } @@ -304,13 +303,13 @@ sup { font-size: 75%; line-height: 0; position: relative; - vertical-align: baseline + vertical-align: baseline; } sup { - top: -0.5em + top: -0.5em; } sub { - bottom: -0.25em -} \ No newline at end of file + bottom: -0.25em; +} diff --git a/style/index.html b/style/index.html index 1d7a774..6842a03 100755 --- a/style/index.html +++ b/style/index.html @@ -1,245 +1,260 @@ + + + + + Dark Matrix + + + - - - - - Dark Matrix - - - + + +
+

+ Text can be bold, italic, or + strikethrough. +

-
-

- Text can be bold, italic, or - strikethrough. -

+

Link to another page.

-

Link to another page.

- -

There should be whitespace between paragraphs.

- -

- There should be whitespace between paragraphs. We recommend including a - README, or a file with information about your project. -

- -

Header 1

- -

- This is a normal paragraph following a header. GitHub is a code hosting - platform for version control and collaboration. It lets you and others - work together on projects from anywhere. -

- -

Header 2

- -
-

This is a blockquote following a header.

+

There should be whitespace between paragraphs.

- When something is important enough, you do it even if the odds are not - in your favor. + There should be whitespace between paragraphs. We recommend including a + README, or a file with information about your project.

-
-

Header 3

+

Header 1

-
-
-
// Javascript code with syntax highlighting.
+      

+ This is a normal paragraph following a header. GitHub is a code hosting + platform for version control and collaboration. It lets you and others + work together on projects from anywhere. +

+ +

Header 2

+ +
+

This is a blockquote following a header.

+ +

+ When something is important enough, you do it even if the odds are not + in your favor. +

+
+ +

Header 3

+ +
+
+
// Javascript code with syntax highlighting.
 var fun = function lang(l) {
   dateformat.i18n = require('./lang/' + l)
   return true;
 }
 
+
-
-
-
-
# Ruby code with syntax highlighting
+      
+
+
# Ruby code with syntax highlighting
 GitHubPages::Dependencies.gems.each do |gem, version|
   s.add_dependency(gem, "= #{version}")
 end
 
+
-
-

Header 4

+

Header 4

-
    -
  • This is an unordered list following a header.
  • -
  • This is an unordered list following a header.
  • -
  • This is an unordered list following a header.
  • -
+
    +
  • This is an unordered list following a header.
  • +
  • This is an unordered list following a header.
  • +
  • This is an unordered list following a header.
  • +
-
Header 5
+
Header 5
-
    -
  1. This is an ordered list following a header.
  2. -
  3. This is an ordered list following a header.
  4. -
  5. This is an ordered list following a header.
  6. -
+
    +
  1. This is an ordered list following a header.
  2. +
  3. This is an ordered list following a header.
  4. +
  5. This is an ordered list following a header.
  6. +
-
Header 6
+
Header 6
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
head1head twothree
okgood swedish fishnice
out of stockgood and plentynice
ok - good - oreos - hmm
ok - good - zoute - drop - yumm
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
head1head twothree
okgood swedish fishnice
out of stockgood and plentynice
ok + good + oreos + hmm
ok + good + zoute + drop + yumm
-

- There’s a horizontal rule below this. -

+

+ There’s a horizontal rule below this. +

-
+
-

Here is an unordered list:

+

Here is an unordered list:

-
    -
  • Item foo
  • -
  • Item bar
  • -
  • Item baz
  • -
  • Item zip
  • -
+
    +
  • Item foo
  • +
  • Item bar
  • +
  • Item baz
  • +
  • Item zip
  • +
-

And an ordered list:

+

And an ordered list:

-
    -
  1. Item one
  2. -
  3. Item two
  4. -
  5. Item three
  6. -
  7. Item four
  8. -
+
    +
  1. Item one
  2. +
  3. Item two
  4. +
  5. Item three
  6. +
  7. Item four
  8. +
-

And a nested list:

+

And a nested list:

-
    -
  • - level 1 item -
      -
    • level 2 item
    • -
    • - level 2 item -
        -
      • level 3 item
      • -
      • level 3 item
      • -
      -
    • -
    -
  • -
  • - level 1 item -
      -
    • level 2 item
    • -
    • level 2 item
    • -
    • level 2 item
    • -
    -
  • -
  • - level 1 item -
      -
    • level 2 item
    • -
    • level 2 item
    • -
    -
  • -
  • level 1 item
  • -
+
    +
  • + level 1 item +
      +
    • level 2 item
    • +
    • + level 2 item +
        +
      • level 3 item
      • +
      • level 3 item
      • +
      +
    • +
    +
  • +
  • + level 1 item +
      +
    • level 2 item
    • +
    • level 2 item
    • +
    • level 2 item
    • +
    +
  • +
  • + level 1 item +
      +
    • level 2 item
    • +
    • level 2 item
    • +
    +
  • +
  • level 1 item
  • +
-

Small image

+

Small image

-

- Octocat -

+

+ Octocat +

-

Large image

+

Large image

-

- Branching -

+

+ Branching +

-

- Definition lists can be used with HTML syntax. -

+

+ Definition lists can be used with HTML syntax. +

-
-
Name
-
Godzilla
-
Born
-
1952
-
Birthplace
-
Japan
-
Color
-
Green
-
+
+
Name
+
Godzilla
+
Born
+
1952
+
Birthplace
+
Japan
+
Color
+
Green
+
-
-
-
Long, single-line code blocks should not wrap. They should horizontally scroll if they are too long. This line should be long enough to demonstrate this.
+      
+
+
Long, single-line code blocks should not wrap. They should horizontally scroll if they are too long. This line should be long enough to demonstrate this.
 
+
-
-
-
-
The final element.
+      
+
+
The final element.
 
+
-
-
- - - \ No newline at end of file + This page was generated by + Array in a Matrix. + +
+ + diff --git a/style/style.css b/style/style.css index f71a63b..b8f1dc8 100755 --- a/style/style.css +++ b/style/style.css @@ -4,11 +4,11 @@ html { font-family: "Montserrat", sans-serif; -ms-text-size-adjust: 100%; - -webkit-text-size-adjust: 100% + -webkit-text-size-adjust: 100%; } body { - margin: 0 + margin: 0; } article, @@ -24,7 +24,7 @@ menu, nav, section, summary { - display: block + display: block; } audio, @@ -32,53 +32,53 @@ canvas, progress, video { display: inline-block; - vertical-align: baseline + vertical-align: baseline; } audio:not([controls]) { display: none; - height: 0 + height: 0; } [hidden], template { - display: none + display: none; } a { - background-color: transparent + background-color: transparent; } a:active, a:hover { - outline: 0 + outline: 0; } abbr[title] { - border-bottom: 1px dotted + border-bottom: 1px dotted; } b, strong { - font-weight: bold + font-weight: bold; } dfn { - font-style: italic + font-style: italic; } h1 { font-size: 2em; - margin: 0.67em 0 + margin: 0.67em 0; } mark { background: #ff0; - color: #000 + color: #000; } small { - font-size: 80% + font-size: 80%; } sub, @@ -86,36 +86,36 @@ sup { font-size: 75%; line-height: 0; position: relative; - vertical-align: baseline + vertical-align: baseline; } sup { - top: -0.5em + top: -0.5em; } sub { - bottom: -0.25em + bottom: -0.25em; } img { - border: 0 + border: 0; } svg:not(:root) { - overflow: hidden + overflow: hidden; } figure { - margin: 1em 40px + margin: 1em 40px; } hr { box-sizing: content-box; - height: 0 + height: 0; } pre { - overflow: auto + overflow: auto; } code, @@ -123,7 +123,7 @@ kbd, pre, samp { font-family: monospace, monospace; - font-size: 1em + font-size: 1em; } button, @@ -133,16 +133,16 @@ select, textarea { color: inherit; font: inherit; - margin: 0 + margin: 0; } button { - overflow: visible + overflow: visible; } button, select { - text-transform: none + text-transform: none; } button, @@ -150,76 +150,76 @@ html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; - cursor: pointer + cursor: pointer; } button[disabled], html input[disabled] { - cursor: default + cursor: default; } button::-moz-focus-inner, input::-moz-focus-inner { border: 0; - padding: 0 + padding: 0; } input { - line-height: normal + line-height: normal; } input[type="checkbox"], input[type="radio"] { box-sizing: border-box; - padding: 0 + padding: 0; } input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { - height: auto + height: auto; } input[type="search"] { -webkit-appearance: textfield; - box-sizing: content-box + box-sizing: content-box; } input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none + -webkit-appearance: none; } fieldset { border: 1px solid #c0c0c0; margin: 0 2px; - padding: 0.35em 0.625em 0.75em + padding: 0.35em 0.625em 0.75em; } legend { border: 0; - padding: 0 + padding: 0; } textarea { - overflow: auto + overflow: auto; } optgroup { - font-weight: bold + font-weight: bold; } table { border-collapse: collapse; - border-spacing: 0 + border-spacing: 0; } td, th { - padding: 0 + padding: 0; } * { - box-sizing: border-box + box-sizing: border-box; } body { @@ -228,7 +228,7 @@ body { font-family: "Montserrat", sans-serif; font-size: 16px; line-height: 1.5; - color: #606c71 + color: #606c71; } #skip-to-content { @@ -236,7 +236,7 @@ body { width: 1px; position: absolute; overflow: hidden; - top: -10px + top: -10px; } #skip-to-content:focus { @@ -246,16 +246,16 @@ body { height: auto; width: auto; background: #e19447; - outline: thick solid #e19447 + outline: thick solid #e19447; } a { color: #1e6bb8; - text-decoration: none + text-decoration: none; } a:hover { - text-decoration: underline + text-decoration: underline; } .btn { @@ -267,30 +267,30 @@ a:hover { border-style: solid; border-width: 1px; border-radius: 0.3rem; - transition: color 0.2s, background-color 0.2s, border-color 0.2s + transition: color 0.2s, background-color 0.2s, border-color 0.2s; } .btn:hover { color: rgba(255, 255, 255, 0.8); text-decoration: none; background-color: rgba(255, 255, 255, 0.2); - border-color: rgba(255, 255, 255, 0.3) + border-color: rgba(255, 255, 255, 0.3); } -.btn+.btn { - margin-left: 1rem +.btn + .btn { + margin-left: 1rem; } @media screen and (min-width: 64em) { .btn { - padding: 0.75rem 1rem + padding: 0.75rem 1rem; } } @media screen and (min-width: 42em) and (max-width: 64em) { .btn { padding: 0.6rem 0.9rem; - font-size: 0.9rem + font-size: 0.9rem; } } @@ -299,12 +299,12 @@ a:hover { display: block; width: 100%; padding: 0.75rem; - font-size: 0.9rem + font-size: 0.9rem; } - .btn+.btn { + .btn + .btn { margin-top: 1rem; - margin-left: 0 + margin-left: 0; } } @@ -362,7 +362,8 @@ a:hover { } body { - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAABbmlDQ1BpY2MAACiRdZE7SwNBFIW/RCWikRRaBLHYQsVCJSiIpcbCJkiICr6aZPMSNuuyu0HEVrCxCFiINr4K/4G2gq2CICiCiI1/wFcjYb2TBBJEZ5m9H2fmXGbOgD9m6AWnOQIF07UT01FtYXFJC7wSIEwnw/iSumNNxuMx/h1f9/hUvRtSvf7f9+doT2ccHXytwmO6ZbvCE8KxdddSvCPcpeeTaeEj4UFbDih8rfRUlV8U56r8odieS0yBX/XUcg2camA9bxeEB4R7C0ZRr51H3SSYMednpXbL7MEhwTRRNFIUWcXAZUiqKZn97YtUfDOsiUeXv8UGtjhy5MU7KGpRumakZkXPyGewoXL/naeTHR2pdg9GoeXZ8977ILAL5ZLnfR97XvkEmp7g0qz71ySn8U/RS3Wt9xBCW3B+VddSe3CxDeFHK2knK1KTTH82C29n0LEInbfQtlzNqrbO6QPMbcoT3cD+AfTL/tDKD92VZ/plHO89AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAuElEQVRoBe3awQ2CQABEUbUEbJEEi5ISpQVETuY38LPkk5gwp9mdx9H7srz2dX3fpul5G/XZts959H2e5/14G/Z3nv+QGPYC/+M/Rv2ceO4uwkXsnIgtwP5EuIidE7EF2J8IF7FzIrYA+xPhInZOxBZgfyJcxM6J2ALsT4SL2DkRW4D9iXAROydiC7A/ES5i50RsAfYnwkXsnIgtwP5EuIidE7EF2J8IF7FzIrYA+y8j8rvYJf6v9QVf2KG69fWg3gAAAABJRU5ErkJggg==") repeat 0 0; + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAABbmlDQ1BpY2MAACiRdZE7SwNBFIW/RCWikRRaBLHYQsVCJSiIpcbCJkiICr6aZPMSNuuyu0HEVrCxCFiINr4K/4G2gq2CICiCiI1/wFcjYb2TBBJEZ5m9H2fmXGbOgD9m6AWnOQIF07UT01FtYXFJC7wSIEwnw/iSumNNxuMx/h1f9/hUvRtSvf7f9+doT2ccHXytwmO6ZbvCE8KxdddSvCPcpeeTaeEj4UFbDih8rfRUlV8U56r8odieS0yBX/XUcg2camA9bxeEB4R7C0ZRr51H3SSYMednpXbL7MEhwTRRNFIUWcXAZUiqKZn97YtUfDOsiUeXv8UGtjhy5MU7KGpRumakZkXPyGewoXL/naeTHR2pdg9GoeXZ8977ILAL5ZLnfR97XvkEmp7g0qz71ySn8U/RS3Wt9xBCW3B+VddSe3CxDeFHK2knK1KTTH82C29n0LEInbfQtlzNqrbO6QPMbcoT3cD+AfTL/tDKD92VZ/plHO89AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAuElEQVRoBe3awQ2CQABEUbUEbJEEi5ISpQVETuY38LPkk5gwp9mdx9H7srz2dX3fpul5G/XZts959H2e5/14G/Z3nv+QGPYC/+M/Rv2ceO4uwkXsnIgtwP5EuIidE7EF2J8IF7FzIrYA+xPhInZOxBZgfyJcxM6J2ALsT4SL2DkRW4D9iXAROydiC7A/ES5i50RsAfYnwkXsnIgtwP5EuIidE7EF2J8IF7FzIrYA+y8j8rvYJf6v9QVf2KG69fWg3gAAAABJRU5ErkJggg==") + repeat 0 0; -webkit-animation: bg-scrolling-reverse 0.92s infinite; /* Safari 4+ */ -moz-animation: bg-scrolling-reverse 0.92s infinite; @@ -379,66 +380,66 @@ body { @media screen and (min-width: 64em) { .page-header { - padding: 5rem 6rem + padding: 5rem 6rem; } } @media screen and (min-width: 42em) and (max-width: 64em) { .page-header { - padding: 3rem 4rem + padding: 3rem 4rem; } } @media screen and (max-width: 42em) { .page-header { - padding: 2rem 1rem + padding: 2rem 1rem; } } .project-name { margin-top: 0; - margin-bottom: 0.1rem + margin-bottom: 0.1rem; } @media screen and (min-width: 64em) { .project-name { - font-size: 3.25rem + font-size: 3.25rem; } } @media screen and (min-width: 42em) and (max-width: 64em) { .project-name { - font-size: 2.25rem + font-size: 2.25rem; } } @media screen and (max-width: 42em) { .project-name { - font-size: 1.75rem + font-size: 1.75rem; } } .project-tagline { margin-bottom: 2rem; font-weight: normal; - opacity: 0.7 + opacity: 0.7; } @media screen and (min-width: 64em) { .project-tagline { - font-size: 1.25rem + font-size: 1.25rem; } } @media screen and (min-width: 42em) and (max-width: 64em) { .project-tagline { - font-size: 1.15rem + font-size: 1.15rem; } } @media screen and (max-width: 42em) { .project-tagline { - font-size: 1rem + font-size: 1rem; } } @@ -448,7 +449,7 @@ body { } .main-content :first-child { - margin-top: 0 + margin-top: 0; } @media screen and (min-width: 64em) { @@ -456,21 +457,21 @@ body { max-width: 64rem; padding: 2rem 6rem; margin: 0 auto; - font-size: 1.1rem + font-size: 1.1rem; } } @media screen and (min-width: 42em) and (max-width: 64em) { .main-content { padding: 2rem 4rem; - font-size: 1.1rem + font-size: 1.1rem; } } @media screen and (max-width: 42em) { .main-content { padding: 2rem 1rem; - font-size: 1rem + font-size: 1rem; } } @@ -485,11 +486,11 @@ body { font-size: 11px; line-height: 10px; padding: 3px 5px; - vertical-align: middle + vertical-align: middle; } .main-content img { - max-width: 100% + max-width: 100%; } .main-content h1, @@ -505,7 +506,7 @@ body { } .main-content p { - margin-bottom: 1em + margin-bottom: 1em; } .main-content code { @@ -514,7 +515,7 @@ body { font-size: 0.9rem; color: #567482; background-color: #f3f6fa; - border-radius: 0.3rem + border-radius: 0.3rem; } .main-content pre { @@ -526,10 +527,10 @@ body { word-wrap: normal; background-color: #f3f6fa; border: solid 1px #dce6f0; - border-radius: 0.3rem + border-radius: 0.3rem; } -.main-content pre>code { +.main-content pre > code { padding: 0; margin: 0; font-size: 0.9rem; @@ -537,16 +538,16 @@ body { word-break: normal; white-space: pre; background: transparent; - border: 0 + border: 0; } .main-content .highlight { - margin-bottom: 1rem + margin-bottom: 1rem; } .main-content .highlight pre { margin-bottom: 0; - word-break: normal + word-break: normal; } .main-content .highlight pre, @@ -556,7 +557,7 @@ body { font-size: 0.9rem; line-height: 1.45; border-radius: 0.3rem; - -webkit-overflow-scrolling: touch + -webkit-overflow-scrolling: touch; } .main-content pre code, @@ -569,34 +570,34 @@ body { line-height: inherit; word-wrap: normal; background-color: transparent; - border: 0 + border: 0; } .main-content pre code:before, .main-content pre code:after, .main-content pre tt:before, .main-content pre tt:after { - content: normal + content: normal; } .main-content ul, .main-content ol { - margin-top: 0 + margin-top: 0; } .main-content blockquote { padding: 0 1rem; margin-left: 0; color: #819198; - border-left: 0.3rem solid #dce6f0 + border-left: 0.3rem solid #dce6f0; } -.main-content blockquote>:first-child { - margin-top: 0 +.main-content blockquote > :first-child { + margin-top: 0; } -.main-content blockquote>:last-child { - margin-bottom: 0 +.main-content blockquote > :last-child { + margin-bottom: 0; } .main-content table { @@ -605,33 +606,33 @@ body { overflow: auto; word-break: normal; word-break: keep-all; - -webkit-overflow-scrolling: touch + -webkit-overflow-scrolling: touch; } .main-content table th { - font-weight: bold + font-weight: bold; } .main-content table th, .main-content table td { padding: 0.5rem 1rem; - border: 1px solid #e9ebec + border: 1px solid #e9ebec; } .main-content dl { - padding: 0 + padding: 0; } .main-content dl dt { padding: 0; margin-top: 1rem; font-size: 1rem; - font-weight: bold + font-weight: bold; } .main-content dl dd { padding: 0; - margin-bottom: 1rem + margin-bottom: 1rem; } .main-content hr { @@ -639,30 +640,30 @@ body { padding: 0; margin: 1rem 0; background-color: #dce6f0; - border: 0 + border: 0; } .site-footer { padding-top: 2rem; margin-top: 2rem; - border-top: solid 1px #eff0f1 + border-top: solid 1px #eff0f1; } @media screen and (min-width: 64em) { .site-footer { - font-size: 1rem + font-size: 1rem; } } @media screen and (min-width: 42em) and (max-width: 64em) { .site-footer { - font-size: 1rem + font-size: 1rem; } } @media screen and (max-width: 42em) { .site-footer { - font-size: 0.9rem + font-size: 0.9rem; } } @@ -700,7 +701,7 @@ section { } .zoom { - transition: transform .2s; + transition: transform 0.2s; margin: 0 auto; } .zoom img { @@ -719,9 +720,9 @@ section { justify-content: space-evenly; } -main div { - box-sizing: unset +main div { + box-sizing: unset; } .center { text-align: center; -} \ No newline at end of file +}