Quote:
+ Try to keep your soul young and quivering right up to old age.
+
+
Author:
+ George Sand
+
+
+
+
About me
+
+
The Union of the indestructible republics of the free ones
+ The Great Russia has forever consolidated itself.
+ Long live the people created by the will of the people
+ A single, mighty Soviet Union!
+
+
Be glorious, our free Fatherland,
+ Friendship, peoples a reliable stronghold!
+ The Soviet banner, the banner of the people
+ Let it lead from victory to victory!
+
+
Through the thunderstorms the sun of freedom shone,
+ And Lenin, the great one, illumined our way.
+ We were brought up by Stalin - to be faithful to the people.
+ We were inspired by work and deeds.
+
+
Be glorious, Fatherland freer,
+ Happiness of peoples reliable stronghold!
+ The Soviet banner, the banner of the people
+ Let it lead from victory to victory!
+
+
We raised our army in the battles,
+ The invaders of the vile from the road are estimated!
+ We are in the battles to decide the fate of generations,
+ We will lead our glory to our Fatherland!
+
+
Be glorious, our free Fatherland,
+ the glory of peoples is a reliable stronghold!
+ The Soviet banner, the banner of the people
+ Let it lead from victory to victory!
This is havox - havox is a collection of my projects and tutorials that I've written over the years. The tutorials written are more to consolidate my knowledge of whatever I've been working on however if they help even one person, I'll consider that a win.
+
Feel free to check out any of my tutorials or one of my various js projects
+
Why V2?
+
+This is the second revision of Havox. After havox's initial conception in late 2017, it has suffered from a chronic case of spaghetti code. An update with: neater and better code, updated theming and overall cleaner UI was long overdue.
+
+
Why is Havox on umbra.cyou?
+
Havox has domain hopped a fair bit since 2017 (where it used to be hosted on jayomayo.host - lol) however since losing the havox.org domain, I will be using umbra.cyou for the foreseeable future
Thank you to @uixmat for inspiration and some code for this webpage, check out his twitter.
+
Also shoutout to Daniel Shiffman and the P5 team. P5 is used extensively in these tutorials & projects
+
+
diff --git a/archive/v2/index_style.css b/archive/v2/index_style.css
new file mode 100644
index 0000000..9712484
--- /dev/null
+++ b/archive/v2/index_style.css
@@ -0,0 +1,117 @@
+@import url("https://fonts.googleapis.com/css2?family=Roboto&display=swap");
+* {
+ box-sizing: border-box; }
+
+html, body {
+ font-family: "Roboto", sans-serif;
+ background: #eceff4;
+ padding: 80px 30px 30px;
+ color: #444;
+ height: 100%; }
+
+ol, ul {
+ margin: 0 0 50px 0; }
+
+ul {
+ position: relative;
+ width: 75%;
+ margin: 100px auto 0;
+ padding: 10px;
+ box-sizing: border-box; }
+
+ul li {
+ display: flex;
+ background: red; }
+
+h1 {
+ color: #2e3440;
+ font-size: 48px; }
+
+h2 {
+ color: #2e3440;
+ font-size: 28px; }
+
+p {
+ line-height: 1.6em;
+ margin-bottom: 10px;
+ font-size: 20px;
+ padding: 0; }
+ p:last-child {
+ margin-bottom: 40px; }
+
+code {
+ background: #f8f8f8;
+ color: #d64f9b;
+ border: 1px solid #eee;
+ padding: 2px;
+ border-radius: 3px; }
+
+a {
+ font-size: 22px;
+ font-weight: bold;
+ color: #88c0d0;
+ text-decoration: none;
+ transition: all 200ms; }
+ a:hover, a:focus {
+ color: #ebcb8b; }
+
+article {
+ margin-bottom: 50px; }
+ article .margin {
+ margin-bottom: 100vh; }
+
+nav {
+ display: flex;
+ width: 100%;
+ top: 0;
+ left: 0;
+ background: #3b4252;
+ position: fixed;
+ padding: 30px; }
+ nav a {
+ display: inline-flex;
+ padding: 20px 30px;
+ color: #eceff4;
+ text-decoration: none; }
+ nav a:hover, nav a:focus {
+ color: #ebcb8b; }
+ nav a.active {
+ color: #88c0d0; }
+
+.tut_box {
+ border: 2px solid #3b4252;
+ border-radius: 5px;
+ margin-bottom: 10px;
+ padding: 5px; }
+ .tut_box:hover {
+ background-color: #3b4252;
+ color: #eceff4; }
+ .tut_box:hover a {
+ color: #ebcb8b; }
+ .tut_box:hover .out_link {
+ color: #8fbcbb; }
+ .tut_box p {
+ padding: 0px;
+ margin: 0px; }
+ .tut_box .tut_date {
+ float: right;
+ position: relative;
+ top: 0px;
+ right: 0px;
+ text-align: right; }
+
+.tutName {
+ font-weight: bold;
+ color: #8fbcbb;
+ font-size: 22px; }
+ .tutName:hover {
+ color: #ebcb8b; }
+
+.out_link {
+ font-weight: bold;
+ color: #4c566a;
+ text-decoration: none;
+ padding: 5px, 5px;
+ font-size: 19px; }
+
+/*# sourceMappingURL=index_style.css.map */
diff --git a/archive/v2/index_style.css.map b/archive/v2/index_style.css.map
new file mode 100644
index 0000000..b7c8362
--- /dev/null
+++ b/archive/v2/index_style.css.map
@@ -0,0 +1,7 @@
+{
+"version": 3,
+"mappings": "AAAQ,2EAAmE;AAE3E,CAAE;EAAE,UAAU,EAAE,UAAU;;AAO1B,UAAW;EACT,WAAW,EAAE,oBAAoB;EACjC,UAAU,ECsIJ,OAAO;EDrIb,OAAO,EAAE,cAAc;EACvB,KAAK,EAAE,IAAI;EACX,MAAM,EAAE,IAAI;;AAGd,MAAO;EACL,MAAM,EAAE,UAAU;;AAGpB,EAAG;EACD,QAAQ,EAAE,QAAQ;EAClB,KAAK,EAAE,GAAG;EACV,MAAM,EAAE,YAAY;EACpB,OAAO,EAAE,IAAI;EACb,UAAU,EAAE,UAAU;;AAGxB,KAAM;EACJ,OAAO,EAAE,IAAI;EACb,UAAU,EAAE,GAAG;;AAGjB,EAAG;EACA,KAAK,ECEA,OAAO;EDDZ,SAAS,EAAE,IAAI;;AAGlB,EAAG;EACD,KAAK,ECHC,OAAO;EDIb,SAAS,EAAE,IAAI;;AAGjB,CAAE;EACA,WAAW,EAAE,KAAK;EAClB,aAAa,EAAE,IAAI;EACnB,SAAS,EAAE,IAAI;EACf,OAAO,EAAE,CAAC;EACV,YAAa;IACX,aAAa,EAAE,IAAI;;AAIvB,IAAK;EACH,UAAU,EAAE,OAAO;EACnB,KAAK,EAAE,OAAO;EACd,MAAM,EAAE,cAAc;EACtB,OAAO,EAAE,GAAG;EACZ,aAAa,EAAE,GAAG;;AAGpB,CAAE;EACA,SAAS,EAAE,IAAI;EACf,WAAW,EAAE,IAAI;EACjB,KAAK,ECuGC,OAAO;EDtGb,eAAe,EAAE,IAAI;EACrB,UAAU,EAAE,SAAS;EACrB,gBACO;IACL,KAAK,ECiJA,OAAO;;AD7IhB,OAAQ;EACN,aAAa,EAAE,IAAI;EACnB,eAAQ;IACN,aAAa,EAAE,KAAK;;AAIxB,GAAI;EACF,OAAO,EAAE,IAAI;EACb,KAAK,EAAE,IAAI;EACX,GAAG,EAAE,CAAC;EACN,IAAI,EAAE,CAAC;EACP,UAAU,ECvCJ,OAAO;EDwCb,QAAQ,EAAE,KAAK;EACf,OAAO,EAAE,IAAI;EACb,KAAE;IACA,OAAO,EAAE,WAAW;IACpB,OAAO,EAAE,SAAS;IAClB,KAAK,ECqDD,OAAO;IDpDX,eAAe,EAAE,IAAI;IACrB,wBACQ;MACN,KAAK,ECuHF,OAAO;IDrHZ,YAAS;MACP,KAAK,ECqEH,OAAO;;ADhEf,QAAS;EACP,MAAM,EAAE,iBAAgB;EACxB,aAAa,EAAE,GAAG;EAClB,aAAa,EAAE,IAAI;EACnB,OAAO,EAAE,GAAG;EAEZ,cAAQ;IACN,gBAAgB,EChEZ,OAAO;IDiEX,KAAK,ECiCD,OAAO;IDhCX,gBAAE;MACA,KAAK,ECqGF,OAAO;IDnGZ,wBAAS;MACP,KAAK,ECqCH,OAAO;EDlCb,UAAE;IACA,OAAO,EAAE,GAAG;IACZ,MAAM,EAAE,GAAG;EAGb,kBAAU;IACR,KAAK,EAAE,KAAK;IACZ,QAAQ,EAAE,QAAQ;IAClB,GAAG,EAAE,GAAG;IACR,KAAK,EAAE,GAAG;IACV,UAAU,EAAE,KAAK;;AAIrB,QAAS;EACP,WAAW,EAAE,IAAI;EACjB,KAAK,ECkBC,OAAO;EDjBb,SAAS,EAAE,IAAI;EACf,cAAQ;IACN,KAAK,EC4EA,OAAO;;ADxEhB,SAAU;EACR,WAAW,EAAE,IAAI;EACjB,KAAK,ECzCC,OAAO;ED0Cb,eAAe,EAAE,IAAI;EACrB,OAAO,EAAE,QAAQ;EACjB,SAAS,EAAE,IAAI",
+"sources": ["style.scss","nord.scss"],
+"names": [],
+"file": "index_style.css"
+}
\ No newline at end of file
diff --git a/archive/v2/nord.scss b/archive/v2/nord.scss
new file mode 100644
index 0000000..4b0be03
--- /dev/null
+++ b/archive/v2/nord.scss
@@ -0,0 +1,235 @@
+// Copyright (c) 2016-present Arctic Ice Studio
+// Copyright (c) 2016-present Sven Greb
+
+// Project: Nord
+// Version: 0.2.0
+// Repository: https://github.com/arcticicestudio/nord
+// License: MIT
+// References:
+// http://sass-lang.com
+// http://sassdoc.com
+
+////
+/// An arctic, north-bluish color palette.
+/// Created for the clean- and minimal flat design pattern to achieve a optimal focus and readability for code syntax
+/// highlighting and UI.
+/// It consists of a total of sixteen, carefully selected, dimmed pastel colors for a eye-comfortable, but yet colorful
+/// ambiance.
+///
+/// @author Arctic Ice Studio
+////
+
+/// Base component color of "Polar Night".
+///
+/// Used for texts, backgrounds, carets and structuring characters like curly- and square brackets.
+///
+/// @access public
+/// @example scss - SCSS
+/// /* For dark ambiance themes */
+/// .background {
+/// background-color: $nord0;
+/// }
+/// /* For light ambiance themes */
+/// .text {
+/// color: $nord0;
+/// }
+/// @group polarnight
+/// @since 0.1.0
+$nord0: #2e3440;
+
+/// Lighter shade color of the base component color.
+///
+/// Used as a lighter background color for UI elements like status bars.
+///
+/// @access public
+/// @group polarnight
+/// @see $nord0
+/// @since 0.1.0
+$nord1: #3b4252;
+
+/// Lighter shade color of the base component color.
+///
+/// Used as line highlighting in the editor.
+/// In the UI scope it may be used as selection- and highlight color.
+///
+/// @access public
+/// @example scss - SCSS
+/// /* Code Syntax Highlighting scope */
+/// .editor {
+/// &.line {
+/// background-color: $nord2;
+/// }
+/// }
+///
+/// /* UI scope */
+/// button {
+/// &:selected {
+/// background-color: $nord2;
+/// }
+/// }
+/// @group polarnight
+/// @see $nord0
+/// @since 0.1.0
+$nord2: #434c5e;
+
+/// Lighter shade color of the base component color.
+///
+/// Used for comments, invisibles, indent- and wrap guide marker.
+/// In the UI scope used as pseudoclass color for disabled elements.
+///
+/// @access public
+/// @example scss - SCSS
+/// /* Code Syntax Highlighting scope */
+/// .editor {
+/// &.indent-guide,
+/// &.wrap-guide {
+/// &.marker {
+/// color: $nord3;
+/// }
+/// }
+/// }
+/// .comment,
+/// .invisible {
+/// color: $nord3;
+/// }
+///
+/// /* UI scope */
+/// button {
+/// &:disabled {
+/// background-color: $nord3;
+/// }
+/// }
+/// @group polarnight
+/// @see $nord0
+/// @since 0.1.0
+$nord3: #4c566a;
+
+/// Base component color of "Snow Storm".
+///
+/// Main color for text, variables, constants and attributes.
+/// In the UI scope used as semi-light background depending on the theme shading design.
+///
+/// @access public
+/// @example scss - SCSS
+/// /* For light ambiance themes */
+/// .background {
+/// background-color: $nord4;
+/// }
+/// /* For dark ambiance themes */
+/// .text {
+/// color: $nord4;
+/// }
+/// @group snowstorm
+/// @since 0.1.0
+$nord4: #d8dee9;
+
+/// Lighter shade color of the base component color.
+///
+/// Used as a lighter background color for UI elements like status bars.
+/// Used as semi-light background depending on the theme shading design.
+///
+/// @access public
+/// @group snowstorm
+/// @see $nord4
+/// @since 0.1.0
+$nord5: #e5e9f0;
+
+/// Lighter shade color of the base component color.
+///
+/// Used for punctuations, carets and structuring characters like curly- and square brackets.
+/// In the UI scope used as background, selection- and highlight color depending on the theme shading design.
+///
+/// @access public
+/// @group snowstorm
+/// @see $nord4
+/// @since 0.1.0
+$nord6: #eceff4;
+
+/// Bluish core color.
+///
+/// Used for classes, types and documentation tags.
+///
+/// @access public
+/// @group frost
+/// @since 0.1.0
+$nord7: #8fbcbb;
+
+/// Bluish core accent color.
+///
+/// Represents the accent color of the color palette.
+/// Main color for primary UI elements and methods/functions.
+///
+/// Can be used for
+/// - Markup quotes
+/// - Markup link URLs
+///
+/// @access public
+/// @group frost
+/// @since 0.1.0
+$nord8: #88c0d0;
+
+/// Bluish core color.
+///
+/// Used for language-specific syntactic/reserved support characters and keywords, operators, tags, units and
+/// punctuations like (semi)colons,commas and braces.
+///
+/// @access public
+/// @group frost
+/// @since 0.1.0
+$nord9: #81a1c1;
+
+/// Bluish core color.
+///
+/// Used for markup doctypes, import/include/require statements, pre-processor statements and at-rules (`@`).
+///
+/// @access public
+/// @group frost
+/// @since 0.1.0
+$nord10: #5e81ac;
+
+/// Colorful component color.
+///
+/// Used for errors, git/diff deletion and linter marker.
+///
+/// @access public
+/// @group aurora
+/// @since 0.1.0
+$nord11: #bf616a;
+
+/// Colorful component color.
+///
+/// Used for annotations.
+///
+/// @access public
+/// @group aurora
+/// @since 0.1.0
+$nord12: #d08770;
+
+/// Colorful component color.
+///
+/// Used for escape characters, regular expressions and markup entities.
+/// In the UI scope used for warnings and git/diff renamings.
+///
+/// @access public
+/// @group aurora
+/// @since 0.1.0
+$nord13: #ebcb8b;
+
+/// Colorful component color.
+///
+/// Main color for strings and attribute values.
+/// In the UI scope used for git/diff additions and success visualizations.
+///
+/// @access public
+/// @group aurora
+/// @since 0.1.0
+$nord14: #a3be8c;
+
+/// Colorful component color.
+///
+/// Used for numbers.
+///
+/// @access public
+/// @group aurora
+/// @since 0.1.0
+$nord15: #b48ead;
\ No newline at end of file
diff --git a/archive/v2/old/index.html b/archive/v2/old/index.html
new file mode 100644
index 0000000..57f533e
--- /dev/null
+++ b/archive/v2/old/index.html
@@ -0,0 +1,144 @@
+
+
+
+
+
+
+
+
+
+
Havox.org V2
+
+
+
About havox 
+
This is havox - idk rlry know what havox is but isss whatever
+
+
Feel free to check out any of my tutorials or one of my various js projects
+
Why V2?
+
+ This is the second revision of Havox. After havox's initial conception in late 2017, it has suffered from a chronic case of spaghetti code. An update with: neater and better code, updated theming and overall cleaner UI was long overdue.
+
+
Comrades
+
+ The Union of the indestructible republics of the free ones
+ The Great Russia has forever consolidated itself.
+ Long live the people created by the will of the people
+ A single, mighty Soviet Union!
+
+
+ Be glorious, our free Fatherland,
+ Friendship, peoples a reliable stronghold!
+ The Soviet banner, the banner of the people
+ Let it lead from victory to victory!
+
+
+ Through the thunderstorms the sun of freedom shone,
+ And Lenin, the great one, illumined our way.
+ We were brought up by Stalin - to be faithful to the people.
+ We were inspired by work and deeds.
+
+
+ Be glorious, Fatherland freer,
+ Happiness of peoples reliable stronghold!
+ The Soviet banner, the banner of the people
+ Let it lead from victory to victory!
+
+
+ We raised our army in the battles,
+ The invaders of the vile from the road are estimated!
+ We are in the battles to decide the fate of generations,
+ We will lead our glory to our Fatherland!
+
+
+ Be glorious, our free Fatherland,
+ the glory of peoples is a reliable stronghold!
+ The Soviet banner, the banner of the people
+ Let it lead from victory to victory!
+
+
+
+ The Union of the indestructible republics of the free ones
+ The Great Russia has forever consolidated itself.
+ Long live the people created by the will of the people
+ A single, mighty Soviet Union!
+
+
+ Be glorious, our free Fatherland,
+ Friendship, peoples a reliable stronghold!
+ The Soviet banner, the banner of the people
+ Let it lead from victory to victory!
+
+
+ Through the thunderstorms the sun of freedom shone,
+ And Lenin, the great one, illumined our way.
+ We were brought up by Stalin - to be faithful to the people.
+ We were inspired by work and deeds.
+
+
+ Be glorious, Fatherland freer,
+ Happiness of peoples reliable stronghold!
+ The Soviet banner, the banner of the people
+ Let it lead from victory to victory!
+
+
+ We raised our army in the battles,
+ The invaders of the vile from the road are estimated!
+ We are in the battles to decide the fate of generations,
+ We will lead our glory to our Fatherland!
+
+
+ Be glorious, our free Fatherland,
+ the glory of peoples is a reliable stronghold!
+ The Soviet banner, the banner of the people
+ Let it lead from victory to victory!
+
+ I am a Computer Science graduate from the University of Nottingham,
+ with a degree awarded in August 2022. I joined FDM as a Java
+ Software Developer in June 2023, and since then I have been
+ enhancing my skills in various technologies and frameworks. Some of
+ the skills I have learnt at FDM are: creating REST APIs using the
+ Java Spring Framework, using postman api, JUnit and Mockito to test
+ them, whilst using H2, JDBC and MySQL to persist data.
+
+
+ These skills complement my previous academic experience in areas
+ such as: Android Development, Distributed Systems, Game Development,
+ Malware Analysis, Data Visualisation, Graphics, Security,
+ Cryptography, Databases, Algorithms and Efficiency.
+
+
+ I am passionate about learning new things and applying them to
+ real-world problems. I am looking for opportunities to further
+ develop my career as a software engineer and contribute to
+ innovative projects.
+
+
About Havox
+
+ Havox is a website I have been using to host my projects and
+ tutorials since 2017. Back then a friend introduced me to the
+ concept of a VPS, using a debain box to run an nginx web server.
+ Several years later not much has changed (apart from the server,
+ domain and design - all several times).
+
+
+ When I first started learning to code, I took inspiration from
+ The Coding Train and found an interest in visualising
+ algorithms. This was much more interesting than seeing a console
+ output.
+
+
+
+
+
+
+
+
HavoxV3
+ 2023-Present
+
+ Here havox morphed into more of a portfolio rather than a fun
+ side project. Now fully taking advantage of Scss, considering
+ mobile view-widths and general UI design.
+
+
+
+
+
+
+
+
+
+
HavoxV2
+
+ 2019-2022
+
+ My first project using Scss! Also the first website to be
+ hosted on my own hardware - this allowed me to upload far more
+ projects.
+
+
+
+
+
+
+
+
+
Havox.org
+ 2017-2019
+
+ My first website! The whole website was stored in one php
+ file🤣 However I did have a quote of the day feature using
+ python script scheduled by a cronjob using php to dynamically
+ display the quote.
+
+ Welcome to havox - a showcase of my projects and tutorials that
+ I have created over the years. The tutorials are mainly for my
+ own learning purposes, but I hope they can also benefit others
+ who are interested in the same topics. You can browse through my
+ tutorials or explore my JavaScript projects.
+
+ Professional experience in Java, using Java Spring Boot to
+ create RESTful APIs. Completed university courses in: C, C++,
+ Java and Haskell.
+
+
+
+
+
Technical Background
+
+ Long term linux user with server experience hosting web and
+ media servers, along with cyber secure networking practices.
+
+
+
+
+
Tutoring
+
+ Tutor with 4 years experience; tutoring computer science A-Level
+ in addition to tutoring maths and english to children aged 4-14.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
My Tutorials
+
+ I am a strong believer that the best way to learn is to teach.
+ When I come across something I find interesting and want to deepen
+ my understanding I write a short tutorial.
+
+ Best way to learn is to teach. When I find something interesting I write a short tutorial — mainly to
+ consolidate my own understanding, but if it helps someone else that's a win.
+
+
+ Had to really try my best to not touch anything here. Every tutorial here is exacty how it was when I wrote
+ it in sixth form.
+
+ This interactive sketch visualizes a Fourier series using epicycles. Each circle represents
+ an odd harmonic; as you add more epicycles with the slider, the path on the right converges toward a
+ square wave. The rotating vectors sum to a point whose vertical position is traced over time.
+
+
+
+
+
+
+
+
+
+
diff --git a/projects/game_of_life.html b/projects/game_of_life.html
new file mode 100644
index 0000000..b19dbab
--- /dev/null
+++ b/projects/game_of_life.html
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+
+ Game of Life
+
+
+
+
+
+
+
+
diff --git a/projects/marching_squares.html b/projects/marching_squares.html
new file mode 100755
index 0000000..1862d6d
--- /dev/null
+++ b/projects/marching_squares.html
@@ -0,0 +1,173 @@
+
+
+
+
+
+
+
+
+
diff --git a/projects/oscillations_in_3d.html b/projects/oscillations_in_3d.html
new file mode 100644
index 0000000..f31c1a8
--- /dev/null
+++ b/projects/oscillations_in_3d.html
@@ -0,0 +1,62 @@
+
+Oscilations in 3D
+
+
+
+
+
+
+
+
+
+ A small demonstation on how the Monte Carlo method can approximate the value of π
+ by randomly placing points inside a square and checking how many fall within
+ the inner circle. The closer the ratio gets to π/4, the more accurate
+ the estimation becomes.
+
+
+
+
+
+
+
+
+
+
diff --git a/projects/poissonDist.php b/projects/poissonDist.php
new file mode 100644
index 0000000..fc08570
--- /dev/null
+++ b/projects/poissonDist.php
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
diff --git a/projects/sorting_vis.html b/projects/sorting_vis.html
new file mode 100755
index 0000000..6fef429
--- /dev/null
+++ b/projects/sorting_vis.html
@@ -0,0 +1,69 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/projects/warp_lines.html b/projects/warp_lines.html
new file mode 100755
index 0000000..7b6a3f2
--- /dev/null
+++ b/projects/warp_lines.html
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/todo.md b/todo.md
deleted file mode 100644
index 75ea2c6..0000000
--- a/todo.md
+++ /dev/null
@@ -1,5 +0,0 @@
-1. Havox & Me: Add another section for development
-2. Move version history down to the bottom
-3. What I know
-4. Wire tutorials & projects up - maybe swap tutorials & projects
-5. Add Professional github?
diff --git a/tutorials/convex_hull/dot.js b/tutorials/convex_hull/dot.js
new file mode 100644
index 0000000..d0997c8
--- /dev/null
+++ b/tutorials/convex_hull/dot.js
@@ -0,0 +1,7 @@
+class dot{
+
+ constructor(x, y){
+ this.x = x;
+ this.y = y;
+ }
+}
\ No newline at end of file
diff --git a/tutorials/convex_hull/index.html b/tutorials/convex_hull/index.html
new file mode 100644
index 0000000..2f5dbe8
--- /dev/null
+++ b/tutorials/convex_hull/index.html
@@ -0,0 +1,95 @@
+
+
+ Convex Hull
+
+
+
+
+
+
+
+
+
+
+
+
+
Convex Hull - Jarvis' Marsh
+
+
A convex hull is where a convex polygon is constructed to bound a set of points on a 2D plane. So, given all
+ points in the set P, what is the smallest convex polygon we can construct that bounds all points in P.
+
+
Pseudo code:
+
+
+
Randomly place n points on a 2D plane.
+
Find the leftmost point/the point with the smallest x value. Called `gamma`.
+
Find the vector which is the most left which connects `gamma` to point q in set P.
+
Store `gamma` and q in a list and repeat until q = `gamma`.
+
+
+
+
+
+
+
This canvas starts off by generating a set amount of points, placing them at random locations around the
+ screen and adding them to an array. It then finds the index of the point in the array - `vertices[]` - with
+ the lowest x value. It then loops through the array finding all vector's originating from `gamma`,
+ `vec(gammaP(i))`.
+
+
We then examine each vertex and evaluate which one is the leftmost from the perspective of point
+ `gamma`. Say in this case `P(q)` is the leftmost vector so we push q onto the stack.
+
Stack: `((q), (gamma))`
+
We then repeat until we reach our starting point, which in this case means our starting point is the same as
+ the next point (`q=gamma`)
+
+
+
Now the question of how to calculate the leftness of a vector compared to another. We can use the
+ cross product in 2D.
+
Now we just have to add in a little piece of code at the bottom of this function to: increment the iteration
+ number so we don't recurse until eternity, add our leftmost vector to the route stack and call the function
+ again with the next point as a parameter.
+
+
+
+
+
+
+
+
diff --git a/tutorials/convex_hull/sketch.js b/tutorials/convex_hull/sketch.js
new file mode 100644
index 0000000..7bb049e
--- /dev/null
+++ b/tutorials/convex_hull/sketch.js
@@ -0,0 +1,102 @@
+var jarvisMarsh = function( p ){
+
+ const numPoints = 12;
+ var vertices = [];
+ var route = [];
+ let iterations = 0;
+
+ p.setup = function(){
+ p.createCanvas(750, 500);
+ p.background(217);
+
+ button = p.createButton('Reset Button')
+ button.mousePressed(p.resetSketch);
+
+ p.initValues();
+
+ }
+
+ p.draw = function(){}
+
+
+ p.drawLine = function(){
+ let r = p.findLeftPoint();
+ route[iterations] = r;
+
+ p.findLeftLine(r);
+
+ p.stroke(215, 75, 75);
+ p.strokeWeight(1);
+ for (var i = 0; i < route.length - 1; i++){
+ p.line(vertices[route[i]].x, vertices[route[i]].y, vertices[route[i+1]].x, vertices[route[i+1]].y)
+ }
+ }
+
+ p.findLeftLine = function(originPoint){
+ var recordLeftVector = 1;
+ let x1, x2, y1, y2;
+
+ for (var i = 0; i < vertices.length; i++){
+ if (i != originPoint){
+ x1 = vertices[recordLeftVector].x - vertices[originPoint].x;
+ y1 = vertices[recordLeftVector].y - vertices[originPoint].y;
+
+
+
+ x2 = vertices[i].x - vertices[originPoint].x;
+ y2 = vertices[i].y - vertices[originPoint].y;
+
+ //if the result if positive then vector u is left of vector v
+ //where u and v are both vectors from the target point
+ //If its equal to 0 then they're colinear. This is also good :)
+ if ((y2 * x1) - (y1 * x2) <= 0){
+ recordLeftVector = i;
+ }
+
+ }
+ }
+
+ if (originPoint != recordLeftVector && iterations < 75){
+ route[iterations] = recordLeftVector;
+ iterations += 1;
+ p.findLeftLine(recordLeftVector);
+
+ }
+ }
+
+ p.findLeftPoint = function(){
+ let recordLeft = 0;
+
+ for (var i = 0; i < vertices.length; i++){
+ if (vertices[i].x < vertices[recordLeft].x){
+ recordLeft = i;
+ }
+ }
+ return recordLeft;
+ }
+
+ p.resetSketch = function(){
+ vertices.length = 0;
+ p.clear()
+ p.background(217);
+ p.initValues();
+ p.findLeftLine();
+ p.draw();
+ }
+
+ p.initValues = function(){
+ p.stroke(0);
+ p.strokeWeight(7);
+
+ var bufferx = p.width * 0.0625;
+ var buffery = p.height * 0.0625;
+
+ for (var i = 0; i < numPoints; i++){
+ vertices[i] = new dot(p.random(bufferx, p.width - bufferx), p.random(buffery, p.height - buffery));
+ p.point(vertices[i].x, vertices[i].y);
+ }
+ iterations = 0;
+ p.drawLine();
+ }
+};
+var myp5 = new p5(jarvisMarsh, 'c1');
\ No newline at end of file
diff --git a/tutorials/convex_hull/stepByStep.png b/tutorials/convex_hull/stepByStep.png
new file mode 100644
index 0000000..85aa093
Binary files /dev/null and b/tutorials/convex_hull/stepByStep.png differ
diff --git a/tutorials/convex_hull/style.css b/tutorials/convex_hull/style.css
new file mode 100644
index 0000000..b9ecafb
--- /dev/null
+++ b/tutorials/convex_hull/style.css
@@ -0,0 +1,71 @@
+h1{
+ font-family: 'Roboto Condensed', sans-serif;
+ margin: 0;
+ padding: 0 0 15px 0;
+ font-weight: 700;
+ font-size: 32px;
+}
+
+h2{
+ font-family: 'Roboto Condensed', sans-serif;
+ margin: 0;
+ padding: 15px 0 15px 0;
+ font-weight: 650;
+ font-size: 20px;
+}
+
+p{
+ margin: 1px;
+ font-family: 'Roboto', sans-serif;
+ font-size: 19px;
+ padding: 5px 0px 5px 0px;
+}
+
+.psudoCode{
+ font-family: 'Inconsolata', monospace;
+ font-size: 19px;
+ background-color: #D9D9D9;
+ border: 1px solid;
+ padding: 5px;
+ box-shadow: 5px 5px #888888;
+}
+
+a{
+ font-family: 'Inconsolata', monospace;
+ font-size: 19px;
+ background-color: #D9D9D9;
+
+}
+
+button{
+ background-color: white;
+ border: 2px solid #555555;
+ color: black;
+ padding: 16px 32px;
+ text-align: center;
+ text-decoration: none;
+ display: inline-block;
+ font-size: 16px;
+ margin: 4px 2px;
+ -webkit-transition-duration: 0.4s; /* Safari */
+ transition-duration: 0.4s;
+ cursor: pointer;
+ float:right;
+}
+
+button:hover {
+ background-color: #555555;
+ color: white;
+}
+
+.examples{
+ padding: 10px;
+}
+
+
+html{
+ -webkit-user-select: none; /* Safari */
+ -moz-user-select: none; /* Firefox */
+ -ms-user-select: none; /* IE10+/Edge */
+ user-select: none; /* Standard */
+}
diff --git a/tutorials/midpoint_displacement/Jagged_Terrain.jpg b/tutorials/midpoint_displacement/Jagged_Terrain.jpg
new file mode 100644
index 0000000..9f8f827
Binary files /dev/null and b/tutorials/midpoint_displacement/Jagged_Terrain.jpg differ
diff --git a/tutorials/midpoint_displacement/dot.js b/tutorials/midpoint_displacement/dot.js
new file mode 100644
index 0000000..1b1587a
--- /dev/null
+++ b/tutorials/midpoint_displacement/dot.js
@@ -0,0 +1,8 @@
+class dot{
+
+ constructor(x, y){
+ this.x = x;
+ this.y = y;
+ }
+
+}
\ No newline at end of file
diff --git a/tutorials/midpoint_displacement/index.html b/tutorials/midpoint_displacement/index.html
new file mode 100644
index 0000000..6736e6a
--- /dev/null
+++ b/tutorials/midpoint_displacement/index.html
@@ -0,0 +1,104 @@
+
+Midpoint Displacement
+
+
+
+
+
+
+
+
+
+
+
Midpoint Displacement
+
+
Midpoint displacement is a common way to generate terrains in video
+ games. Instead of manually creating a stage you can make an
+ algorithm create one based off randomness. This is faster to
+ calculate than a perlin noise stage which is why it can be found
+ frequently in older video game titles.
+
Psudo code:
+
+
+
Create two points.
+
Find the center x coordinate.
+
Create a new point in between the two first points with some
+ offset in the y coorindate.
+
Repeat. (can be recursive)
+
+
+
+
+
+
+
This canvas starts off with two points in an array called
+ points[], one at height/2 and one at
+ math.random(-vertical_displacement, vertical_displacement).
+ The center point is found and a new point is made and added to the
+ array. This new point is offset on the y-axis by an amount however
+ we can't use the same vertical_displacement as we did before
+ as we'd end up with a very jagged terrain.
+
+
+
+
Instead we have to iterate our variable -
+ vertical_displacement - based on the iteration number of the
+ sketch, in order to generate a smooth stage/terrain.
+
The top formula is the inital value. The second is applied very
+ iteration of the sketch.
+
Right now we can add one point to the list, time to add several! This
+ means for loops. But before we can loop through our array how do we
+ know which point should connect to which? We need to sort the array
+ based on x value, so that the points read off from left to right.
+
+
I just used a bubble sort for simplicity however it's worth pointing
+ out that the number of points grows exponetially - O(2^n)
+ where n = iterations - so after just 10 iterations points.length
+ = 1024. A merge sort could be used to speed up computation
+ times.
+
Now we can use a for loop to go through our array and compare the ith
+ item to the (i+1)th item, and then update iterations and
+ vertical_displacement.
+
+ A Summed-area table is a data structure and algorithm for quickly and
+ efficiently generating the sum of values in a rectangular grid. In image
+ processing it is also known as an integral image.
+
+
+
+
+
+ To generate a summed-area table, each cell is given a value which is the
+ sum of all values in a rectangular subset where the top left corner is the
+ first cell in the grid and where the bottom right cell is the cell you're
+ calculating the value for (assumuing the array starts top left).
+
+
+
+
+
+
+
+ Now each cell has its own count value. This isn't much help on it's own,
+ however we can use it to calculate the sum value of any subset of the grid
+ by interrogating 4 cells and performing only 4 additions. NOTE: we have to
+ add the 'top left' region as it as been subtracted twice.
+
+
+
+
+
+
+
+
Click a sqaure on the grid to turn it on or off.
+
Shift+Click two square to define a rectangle.
+
+ Press the show count button to display the sum value of each cell
+ (in this example a cell's value can only be 0 or 1).
+
+
+ Press show rectangles to see the different regions simplified and
+ see how
+ it changes the sum below.
+
+
+
+
+
+
+
diff --git a/tutorials/summed_area/script.js b/tutorials/summed_area/script.js
new file mode 100644
index 0000000..c9071e4
--- /dev/null
+++ b/tutorials/summed_area/script.js
@@ -0,0 +1,278 @@
+let myWidth = 800, myHeight = 800, offset = 5;
+let r, n = 10;
+
+let t = [];
+let tl = null
+let br = null;
+
+let showCount = false;
+let drawRectMode = 0;
+
+let sOutput, wOutput, tlOutput, tOutput, lOutput;
+
+
+function setup()
+{
+ createCanvas(myWidth + 2*offset, myHeight + 2*offset);
+ r = myWidth / n;
+ for (y = 0; y < n; y ++)
+ {
+ for (x = 0; x < n; x ++)
+ {
+ t.push(new cTile(x*r + offset, y*r + offset, r));
+ }
+ }
+ sOutput = document.getElementById('subsetOutput');
+ wOutput = document.getElementById('wholeOutput');
+ tlOutput = document.getElementById('tlOutput');
+ tOutput = document.getElementById('tOutput');
+ lOutput = document.getElementById('lOutput');
+
+}
+
+function draw()
+{
+ background(46, 52, 64);
+ t.forEach(e => e.show());
+
+ stroke(156,212,228);
+ noFill();
+ strokeWeight(5);
+ if (tl)
+ {
+ if (!br)
+ {
+ rect(tl.x, tl.y, tl.w);
+ } else {
+ rect(tl.x, tl.y, br.x - tl.x + r, br.y - tl.y + r);
+ drawRect(drawRectMode);
+ }
+ }
+}
+
+function defineCount(br)
+{
+ let cols = Math.floor(br.x/ r) + 1;
+ let rows = Math.floor(br.y/ r) + 1;
+ let c = 0;
+ // let index = cols + n * rows;
+
+ for (y = 0; y < rows; y++) {
+ for (x = 0; x < cols; x++){
+ var i = x + n * y;
+ if (t[i].s){
+ c++;
+ }
+ }
+ }
+ return c;
+}
+
+function countSquares(tl, br)
+{
+ let topLeft, left, top, whole;
+ let tlx, tly, brx, bry;
+
+ tlx = Math.floor(tl.x / r);
+ tly = Math.floor(tl.y / r);
+ brx = Math.floor(br.x / r);
+ bry = Math.floor(br.y / r);
+
+ topLeft = t[(tlx-1) + n * (tly-1)].count;
+ left = t[(tlx-1) + n * bry ].count;
+ top = t[brx + n * (tly-1)].count;
+ whole = t[brx + n * bry ].count;
+
+ var s = whole + topLeft - top - left;
+ if (s < 0)
+ s = 0;
+
+ return s;
+}
+
+function drawRect(n)
+{
+ strokeWeight(5);
+ noFill();
+
+ switch(n)
+ {
+ case 1:
+ //whole
+ stroke(180,142,173);
+ fill('rgba(180, 142, 173, 0.05)');
+ rect(offset, offset, br.x - offset + br.w, br.y - offset + br.w);
+ break;
+ case 2:
+ //topleft
+ stroke(208,135,112);
+ fill('rgba(208,135,112, 0.05)');
+ rect(offset, offset, tl.x - offset, tl.y - offset);
+ break;
+ case 3:
+ //left
+ stroke(163,190,140);
+ fill('rgba(163,190,140, 0.05)');
+ rect(offset, offset, tl.x - offset, br.y - offset + br.w);
+ break;
+ case 4:
+ //top
+ stroke(235,203,139);
+ fill('rgba(235,203,139, 0.05)');
+ rect(offset, offset, br.x - offset + br.w, tl.y - offset);
+ break;
+ default:
+ break;
+ }
+
+ strokeWeight(1);
+}
+
+function showCountToggle(){
+ showCount = !showCount;
+}
+
+function toggleRectMode(){
+ if (tl && br)
+ {
+ var tlx = Math.floor(tl.x / r);
+ var tly = Math.floor(tl.y / r);
+ var brx = Math.floor(br.x / r);
+ var bry = Math.floor(br.y / r);
+
+ var topLeft = t[(tlx-1) + n * (tly-1)].count;
+ var left = t[(tlx-1) + n * bry ].count;
+ var top = t[brx + n * (tly-1)].count;
+ var whole = t[brx + n * bry ].count;
+
+ if (drawRectMode == 0){
+ wOutput.innerHTML = whole.toString(10);
+ wOutput.style.fontWeight = "bold";
+ drawRectMode ++;
+ } else if (drawRectMode == 1){
+ tlOutput.innerHTML = topLeft.toString(10);
+ tlOutput.style.fontWeight = "bold";
+ wOutput.style.fontWeight = "normal";
+ drawRectMode ++;
+ } else if (drawRectMode == 2){
+ lOutput.innerHTML = left.toString(10);
+ lOutput.style.fontWeight = "bold";
+ tlOutput.style.fontWeight = "normal";
+ drawRectMode ++;
+ } else if (drawRectMode == 3) {
+ tOutput.innerHTML = top.toString(10);
+ tOutput.style.fontWeight = "bold";
+ lOutput.style.fontWeight = "normal";
+ drawRectMode ++;
+ } else if (drawRectMode == 4) {
+ tOutput.style.fontWeight = "normal";
+ drawRectMode = 0;
+ }
+ }
+}
+
+function updateSum()
+{
+ var tlx = Math.floor(tl.x / r);
+ var tly = Math.floor(tl.y / r);
+ var brx = Math.floor(br.x / r);
+ var bry = Math.floor(br.y / r);
+
+ var topLeft = t[(tlx-1) + n * (tly-1)].count;
+ var left = t[(tlx-1) + n * bry ].count;
+ var top = t[brx + n * (tly-1)].count;
+ var whole = t[brx + n * bry ].count;
+
+ wOutput.innerHTML = whole.toString(10);
+ tlOutput.innerHTML = topLeft.toString(10);
+ lOutput.innerHTML = left.toString(10);
+ tOutput.innerHTML = top.toString(10);
+ sOutput.innerHTML = countSquares(tl,br).toString(10);
+}
+
+function mouseClicked(event)
+{
+ if (event.shiftKey)
+ {
+ if (mouseX > offset && mouseX < myWidth && mouseY > offset && mouseY < myHeight)
+ {
+ let col = Math.floor(mouseX / r);
+ let row = Math.floor(mouseY / r);
+
+ if (tl != null && br != null)
+ {
+ tl = null;
+ br = null;
+ }
+
+ if (!tl)
+ {
+ tl = t[col + n * row];
+ } else {
+ br = t[col + n * row];
+ }
+
+ if (tl != null && br != null)
+ {
+ updateSum();
+ }
+
+ }
+ } else {
+ if (mouseX > 0 && mouseX < myWidth && mouseY > 0 && mouseY < myHeight)
+ {
+ let col = Math.floor(mouseX / r);
+ let row = Math.floor(mouseY / r);
+ t[col + n * row].setState();
+
+ t.forEach(tile => tile.setCount(defineCount(tile)));
+
+ if (tl != null && br != null)
+ {
+ updateSum();
+ }
+ }
+ }
+}
+
+class cTile
+{
+ constructor(_x, _y, _w)
+ {
+ this.x = _x;
+ this.y = _y;
+ this.s = false;
+ this.w = _w;
+ this.count = 0;
+ }
+
+ show()
+ {
+ if (this.s)
+ {
+ fill(191,97,106);
+ } else {
+ noFill();
+ }
+ stroke(236,239,244);
+ strokeWeight(1);
+ rect(this.x, this.y, this.w, this.w);
+ if (showCount)
+ {
+ textSize(18);
+ stroke(255);
+ fill(255);
+ text(this.count.toString(10), this.x + this.w/2 - 3, this.y + this.w/2 + 5);
+ }
+ }
+
+ setState()
+ {
+ this.s = !this.s;
+ }
+
+ setCount(c)
+ {
+ this.count = c;
+ }
+};
\ No newline at end of file
diff --git a/tutorials/summed_area/snippets/countSquares.js b/tutorials/summed_area/snippets/countSquares.js
new file mode 100644
index 0000000..fb53332
--- /dev/null
+++ b/tutorials/summed_area/snippets/countSquares.js
@@ -0,0 +1,14 @@
+// where tl and br are the top left and bottom right cell respectively
+function countSquares(tl, br) {
+
+ // these are the four cells to interrogate
+ let topLeft, left, top, whole;
+
+ // 1d index = x + #rows * y
+ topLeft = t[(tl.x - 1) + n * (tl.y - 1)].count;
+ left = t[(tl.x - 1) + n * bry].count;
+ top = t[br.x + n * (tl.y - 1)].count;
+ whole = t[br.x + n * br.y].count;
+
+ return whole + topLeft - top - left;
+}
diff --git a/tutorials/summed_area/snippets/defineCount.js b/tutorials/summed_area/snippets/defineCount.js
new file mode 100644
index 0000000..ec665bf
--- /dev/null
+++ b/tutorials/summed_area/snippets/defineCount.js
@@ -0,0 +1,16 @@
+// where br is the cell we're calculating
+function defineCount(br) {
+ let c = 0;
+
+ // br.x is the column number
+ // br.y is the row number
+ for (y = 0; y <= br.y; y++) { // loop through all rows above br (and the row br is on)
+ for (x = 0; x <= br.x; x++) { // loop through all columns to the left of br (and br column)
+ var i = x + n * y; // convert 2d array format to 1d index
+ if (t[i].s) { // if cell[i].status==true then increase count
+ c++;
+ }
+ }
+ }
+ return c;
+}
diff --git a/tutorials/summed_area/style.css b/tutorials/summed_area/style.css
new file mode 100644
index 0000000..463f020
--- /dev/null
+++ b/tutorials/summed_area/style.css
@@ -0,0 +1,68 @@
+@import url("https://fonts.googleapis.com/css2?family=Roboto&display=swap");
+html {
+ background-color: #2e3440; }
+
+h1 {
+ color: #eceff4;
+ font-family: "Roboto", sans-serif;
+ font-size: 48pt; }
+
+p {
+ color: #d8dee9;
+ font-family: "Roboto", sans-serif;
+ font-size: 16pt; }
+
+code {
+ font-size: 14pt;
+ border: 4px solid #ebcb8b; }
+
+a {
+ color: #d8dee9;
+ font-family: "Roboto", sans-serif;
+ font-size: 24pt; }
+
+#subsetOutput {
+ color: #88c0d0; }
+
+#wholeOutput {
+ color: #b48ead; }
+
+#tlOutput {
+ color: #d08770; }
+
+#tOutput {
+ color: #ebcb8b; }
+
+#lOutput {
+ color: #a3be8c; }
+
+#sumDiv {
+ text-align: center; }
+
+button {
+ display: inline-block;
+ width: 25%;
+ padding: 10px 25px;
+ margin: 0.1em;
+ border: 4px solid #88c0d0;
+ box-sizing: border-box;
+ text-decoration: none;
+ font-family: 'Roboto',sans-serif;
+ font-size: 14pt;
+ color: #434c5e;
+ background-color: #eceff4;
+ text-align: center;
+ position: relative;
+ transition: all 0.15s; }
+ button:hover {
+ background-color: #4c566a;
+ color: #eceff4;
+ border-color: #5e81ac; }
+ button:focus {
+ border: 4px solid #88c0d0;
+ box-sizing: border-box; }
+
+#chillin {
+ display: none; }
+
+/*# sourceMappingURL=style.css.map */
diff --git a/tutorials/summed_area/style.css.map b/tutorials/summed_area/style.css.map
new file mode 100644
index 0000000..8e96bf0
--- /dev/null
+++ b/tutorials/summed_area/style.css.map
@@ -0,0 +1,7 @@
+{
+"version": 3,
+"mappings": "AACQ,2EAAmE;AAI3E,IAAK;EACD,gBAAgB,EC+BZ,OAAO;;AD5Bf,EAAG;EACC,KAAK,ECuID,OAAO;EDtIX,WAAW,EARD,oBAAoB;EAS9B,SAAS,EAAE,IAAI;;AAGnB,CAAE;EACE,KAAK,EC2GD,OAAO;ED1GX,WAAW,EAdD,oBAAoB;EAe9B,SAAS,EAAE,IAAI;;AAGnB,IAAK;EACD,SAAS,EAAE,IAAI;EACf,MAAM,EAAE,iBAAiB;;AAG7B,CAAE;EACE,KAAK,ECgGD,OAAO;ED/FX,WAAW,EAzBD,oBAAoB;EA0B9B,SAAS,EAAE,IAAI;;AAGnB,aAAc;EACV,KAAK,ECuID,OAAO;;ADpIf,YAAa;EACT,KAAK,ECqMA,OAAO;;ADlMhB,SAAS;EACL,KAAK,ECoKA,OAAO;;ADjKhB,QAAQ;EACJ,KAAK,EC0KA,OAAO;;ADvKhB,QAAQ;EACJ,KAAK,ECgLA,OAAO;;AD7KhB,OAAQ;EACJ,UAAU,EAAE,MAAM;;AAGtB,MAAO;EACH,OAAO,EAAE,YAAY;EACrB,KAAK,EAAE,GAAG;EACV,OAAO,EAAE,SAAS;EAClB,MAAM,EAAE,KAAK;EACb,MAAM,EAAE,iBAAgB;EACxB,UAAU,EAAE,UAAU;EACtB,eAAe,EAAE,IAAI;EACrB,WAAW,EAAE,mBAAmB;EAChC,SAAS,EAAE,IAAI;EACf,KAAK,ECMD,OAAO;EDLX,gBAAgB,EC8EZ,OAAO;ED7EX,UAAU,EAAE,MAAM;EAClB,QAAQ,EAAE,QAAQ;EAClB,UAAU,EAAE,SAAS;EAErB,YAAQ;IACJ,gBAAgB,EC+BhB,OAAO;ID9BP,KAAK,ECuEL,OAAO;IDtEP,YAAY,ECgHX,OAAO;ED7GZ,YAAQ;IACJ,MAAM,EAAE,iBAAgB;IACxB,UAAU,EAAE,UAAU;;AAI9B,QAAS;EACL,OAAO,EAAE,IAAI",
+"sources": ["cStyle.scss","../nord.scss"],
+"names": [],
+"file": "style.css"
+}
\ No newline at end of file
diff --git a/tutorials/tsp/index.html b/tutorials/tsp/index.html
new file mode 100644
index 0000000..fdc9641
--- /dev/null
+++ b/tutorials/tsp/index.html
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+Travelling Sales Person
+
Travelling Sales Person Problem
+
+
+
+
+
+
+
+
+
+
The travelling salesman problem (TSP) asks the following question: "Given a list of cities and the distances
+ between each pair of cities, what is the shortest possible route that visits each city and returns to the origin
+ city?"
+
The problem was first formulated in 1930 and is one of the most intensively studied problems in optimization. It
+ is used as a benchmark for many optimization methods. Even though the problem is computationally difficult, a
+ large number of heuristics and exact algorithms are known, so that some instances with tens of thousands of
+ cities can be solved completely and even problems with millions of cities can be approximated within a small
+ fraction of 1%.
+
The TSP has several applications even in its purest formulation, such as planning, logistics, and the manufacture
+ of microchips. Slightly modified, it appears as a sub-problem in many areas, such as DNA sequencing. In these
+ applications, the concept city represents, for example, customers, soldering points, or DNA fragments, and the
+ concept distance represents travelling times or cost, or a similarity measure between DNA fragments. The TSP
+ also appears in astronomy, as astronomers observing many sources will want to minimize the time spent moving the
+ telescope between the sources. In many applications, additional constraints such as limited resources or time
+ windows may be imposed.
+
These are some of the algorithms I used
+
Note the purple route is the best route it's found so far and the thin white lines are the routes it's trying
+ real time.
+
+
+
+
+
Random Sort
+
+
This canvas sorts through random possiblities. Every frame the program chooses two random
+ points (cities) and swaps them around. eg say the order was London, Paris, Madrid, the program would swap
+ London and Paris so that the new order is: Paris, London, Madrid. The program then compares the distance
+ against the record distance to decide whether the new order is better than the old order. This search method
+ is the most inefficient way, the worst case scenario is never ending, as the point swaping is random the
+ program may never reach the optimum route
+
Lexicographic Order
+
+
This canvas sorts through all possible orders sequentially, so after n! (where n is the
+ number of points) this algorithm is guaranteed to have found the quickest possible route. However it is
+ highly inefficient always taking n! frames to complete and as n increases, time taken increases
+ exponentially.
This canvas is the most efficient at finding the quickest route, it is a mixture of the
+ two methods above. It starts off by creating a population of orders, a fitness is then generated for each
+ order in the population. This fitness decides how likely the order is to be picked and is based on the
+ distance it takes (lower distance is better). When two orders are picked, the algorithm splices the two
+ together at a random term, it's then mutated and compared against the record distance. This takes the least
+ amount of time to find the shortest distance as the algorithm doesn't search through permuations that are
+ obviously longer due to the order.
+
+
+
+
+
+
+
+
diff --git a/tutorials/tsp/sketch.js b/tutorials/tsp/sketch.js
new file mode 100644
index 0000000..5cc5cc3
--- /dev/null
+++ b/tutorials/tsp/sketch.js
@@ -0,0 +1,382 @@
+/*var seedSlider = document.getElementById('sliderRange')
+seedSlider.onChange = function(){
+ return this.value;
+}
+
+var citiesSlider = document.getElementById('nRange')
+citiesSlider.onChange = function(){
+ return this.value;
+}*/
+
+var Seed = 21//seedChange(11);
+var totalCities = 25//citiesChange(9);
+// Sketch One
+var randomSearch = function( p ) { // p could be any variable name
+ var cities = [];
+ var recordDistance;
+ var bestEver;
+ var localSeed = Seed;
+ var localCities = totalCities;
+ p.setup = function() {
+ p.createCanvas(400, 400);
+ p.setupPoints(Seed, totalCities);
+ }
+
+ p.setupPoints = function(rngSeed, numCities){
+ p.randomSeed(rngSeed);
+ for (var i = 0; i < numCities; i++) {
+ var v = p.createVector(p.random(p.width), p.random(p.height/2));
+ cities[i] = v;
+ }
+
+ var d = p.calcDistance(cities);
+ recordDistance = d;
+ bestEver = cities.slice();
+ }
+
+
+ p.draw = function() {
+ if(localSeed != Seed){
+ p.setupPoints(Seed, localCities);
+ localSeed = Seed;
+ }
+ if(localCities != totalCities){
+ setupPoints(localSeed, totalCities);
+ localCities = totalCities;
+ }
+
+ p.background(0);
+
+ p.fill(255);
+ p.stroke(255);
+ for (var i = 0; i < cities.length; i++) {
+ p.ellipse(cities[i].x, cities[i].y, 8, 8);
+ }
+
+ p.stroke(255, 0, 255);
+ p.strokeWeight(4);
+ p.noFill();
+ p.beginShape();
+ for (var i = 0; i < cities.length; i++) {
+ p.vertex(bestEver[i].x, bestEver[i].y);
+ }
+ p.endShape();
+
+ p.stroke(255);
+ p.translate(0 , p.height/2);
+ p.strokeWeight(1);
+ p.noFill();
+ p.beginShape();
+ for (var i = 0; i < cities.length; i++) {
+ p.vertex(cities[i].x, cities[i].y);
+ }
+ p.endShape();
+
+ var i = p.floor(p.random(cities.length));
+ var j = p.floor(p.random(cities.length));
+ p.swap(cities, i, j);
+
+ var d = p.calcDistance(cities);
+ if (d < recordDistance) {
+ recordDistance = d;
+ bestEver = cities.slice();
+ }
+}
+
+p.swap = function(a, i, j) {
+ var temp = a[i];
+ a[i] = a[j];
+ a[j] = temp;
+}
+
+
+p.calcDistance = function(points) {
+ var sum = 0;
+ for (var i = 0; i < points.length - 1; i++) {
+ var d = p.dist(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y);
+ sum += d;
+ }
+ return sum;
+}
+};
+
+var myp5 = new p5(randomSearch, 'c1');
+
+// Sketch Two
+var lexicographicOrder = function( q ) {
+ var cities = [];
+ var order = [];
+ var totalPermutations;
+ var count = 0;
+ var recordDistance;
+ var bestEver;
+
+ q.setup = function() {
+ q.randomSeed(Seed);
+ q.createCanvas(400, 400);
+ for (var i = 0; i < totalCities; i++) {
+ var v = q.createVector(q.random(q.width), q.random(q.height / 2));
+ cities[i] = v;
+ order[i] = i;
+ }
+
+ var d = q.calcDistance(cities, order);
+ recordDistance = d;
+ bestEver = order.slice();
+ }
+
+ q.draw = function() {
+ q.background(0);
+
+ q.fill(255);
+ for (var i = 0; i < cities.length; i++) {
+ q.ellipse(cities[i].x, cities[i].y, 8, 8);
+ }
+
+ q.stroke(255, 0, 255);
+ q.strokeWeight(4);
+ q.noFill();
+ q.beginShape();
+ for (var i = 0; i < order.length; i++) {
+ var n = bestEver[i];
+ q.vertex(cities[n].x, cities[n].y);
+ }
+ q.endShape();
+
+
+ q.translate(0, q.height / 2);
+ q.stroke(255);
+ q.strokeWeight(1);
+ q.noFill();
+ q.beginShape();
+ for (var i = 0; i < order.length; i++) {
+ var n = order[i];
+ q.vertex(cities[n].x, cities[n].y);
+ }
+ q.endShape();
+
+ var d = q.calcDistance(cities, order);
+ if (d < recordDistance) {
+ recordDistance = d;
+ bestEver = order.slice();
+ }
+
+ q.nextOrder();
+ }
+
+ q.swap = function(a, i, j) {
+ var temp = a[i];
+ a[i] = a[j];
+ a[j] = temp;
+ }
+
+
+q.calcDistance = function(points, order) {
+ var sum = 0;
+ for (var i = 0; i < order.length - 1; i++) {
+ var cityAIndex = order[i];
+ var cityA = points[cityAIndex];
+ var cityBIndex = order[i + 1];
+ var cityB = points[cityBIndex];
+ var d = q.dist(cityA.x, cityA.y, cityB.x, cityB.y);
+ sum += d;
+ }
+ return sum;
+}
+
+// This is my lexical order algorithm
+
+ q.nextOrder = function() {
+
+ // STEP 1 of the algorithm
+ // https://www.quora.com/How-would-you-explain-an-algorithm-that-generates-permutations-using-lexicographic-ordering
+ var largestI = -1;
+ for (var i = 0; i < order.length - 1; i++) {
+ if (order[i] < order[i + 1]) {
+ largestI = i;
+ }
+ }
+ if (largestI == -1) {
+ q.noLoop();
+ }
+
+ // STEP 2
+ var largestJ = -1;
+ for (var j = 0; j < order.length; j++) {
+ if (order[largestI] < order[j]) {
+ largestJ = j;
+ }
+ }
+
+ // STEP 3
+ q.swap(order, largestI, largestJ);
+
+ // STEP 4: reverse from largestI + 1 to the end
+ var endArray = order.splice(largestI + 1);
+ endArray.reverse();
+ order = order.concat(endArray);
+}
+
+};
+var myp5 = new p5(lexicographicOrder, 'c2');
+
+var geneticSearch = function( p ) {
+ var cities = [];
+ var popSize = 500;
+ var population = [];
+ var fitness = [];
+ var recordDistance = Infinity;
+ var bestEver;
+ var currentBest;
+ var statusP;
+
+ p.setup = function(){
+ p.randomSeed(Seed);
+ p.createCanvas(400, 400);
+ var order = [];
+ for (var i = 0; i < totalCities; i++) {
+ var v = p.createVector(p.random(p.width), p.random(p.height / 2));
+ cities[i] = v;
+ order[i] = i;
+ }
+
+ for (var i = 0; i < popSize; i++) {
+ population[i] = p.shuffle(order);
+ }
+ statusP = p.createP('').style('font-size', '32pt');
+ }
+
+ p.draw = function() {
+ p.background(0);
+ p.stroke(255);
+ p.fill(255);
+ for (var i = 0; i < cities.length; i++) {
+ p.ellipse(cities[i].x, cities[i].y, 8, 8);
+ }
+
+ p.calculateFitness();
+ p.normalizeFitness();
+ p.nextGeneration();
+
+ p.noFill();
+ p.strokeWeight(4);
+ p.beginShape();
+ for (var i = 0; i < bestEver.length; i++) {
+ var n = bestEver[i];
+ p.stroke(255, 0, 255);
+ p.vertex(cities[n].x, cities[n].y);
+ //p.stroke(255);
+ //p.ellipse(cities[n].x, cities[n].y, 8, 8);
+ }
+ p.endShape();
+
+ p.translate(0, p.height / 2);
+ p.stroke(255);
+ p.strokeWeight(1);
+ p.noFill();
+ p.beginShape();
+ for (var i = 0; i < currentBest.length; i++) {
+ var n = currentBest[i];
+ p.vertex(cities[n].x, cities[n].y);
+ //p.ellipse(cities[n].x, cities[n].y, 8, 8);
+ }
+ p.endShape();
+}
+
+ p.swap = function(a, i, j) {
+ var temp = a[i];
+ a[i] = a[j];
+ a[j] = temp;
+ }
+
+ p.calcDistance = function(points, order) {
+ var sum = 0;
+ for (var i = 0; i < order.length - 1; i++) {
+ var cityAIndex = order[i];
+ var cityA = points[cityAIndex];
+ var cityBIndex = order[i + 1];
+ var cityB = points[cityBIndex];
+ var d = p.dist(cityA.x, cityA.y, cityB.x, cityB.y);
+ sum += d;
+ }
+ return sum;
+ }
+
+ p.calculateFitness = function() {
+ var currentRecord = Infinity;
+ for (var i = 0; i < population.length; i++) {
+ var d = p.calcDistance(cities, population[i]);
+ if (d < recordDistance) {
+ recordDistance = d;
+ bestEver = population[i];
+ }
+ if (d < currentRecord) {
+ currentRecord = d;
+ currentBest = population[i];
+ }
+
+ fitness[i] = 1 / (p.pow(d, 8) + 1);
+ }
+}
+
+ p.normalizeFitness = function() {
+ var sum = 0;
+ for (var i = 0; i < fitness.length; i++) {
+ sum += fitness[i];
+ }
+ for (var i = 0; i < fitness.length; i++) {
+ fitness[i] = fitness[i] / sum;;
+ }
+ }
+
+p.nextGeneration = function() {
+ var newPopulation = [];
+ for (var i = 0; i < population.length; i++) {
+ var orderA = p.pickOne(population, fitness);
+ var orderB = p.pickOne(population, fitness);
+ var order = p.crossOver(orderA, orderB);
+ p.mutate(order, 0.01);
+ newPopulation[i] = order;
+ }
+ population = newPopulation;
+
+}
+
+p.pickOne = function(list, prob) {
+ var index = 0;
+ var r = p.random(1);
+
+ while (r > 0) {
+ r = r - prob[index];
+ index++;
+ }
+ index--;
+ return list[index].slice();
+}
+
+ p.crossOver = function(orderA, orderB) {
+ var start = p.floor(p.random(orderA.length));
+ var end = p.floor(p.random(start + 1, orderA.length));
+ var neworder = orderA.slice(start, end);
+ // var left = totalCities - neworder.length;
+ for (var i = 0; i < orderB.length; i++) {
+ var city = orderB[i];
+ if (!neworder.includes(city)) {
+ neworder.push(city);
+ }
+ }
+ return neworder;
+ }
+
+
+ p.mutate = function(order, mutationRate) {
+ for (var i = 0; i < totalCities; i++) {
+ if (p.random(1) < mutationRate) {
+ var indexA = p.floor(p.random(order.length));
+ var indexB = (indexA + 1) % totalCities;
+ p.swap(order, indexA, indexB);
+ }
+ }
+ }
+}
+var myp5 = new p5(geneticSearch, 'c3');
diff --git a/tutorials/tsp/style.css b/tutorials/tsp/style.css
new file mode 100644
index 0000000..5bef3b4
--- /dev/null
+++ b/tutorials/tsp/style.css
@@ -0,0 +1,160 @@
+@import url('https://fonts.googleapis.com/css?family=Roboto+Condensed');
+
+a{font-family: 'Roboto Condensed', sans-serif; font-size: 18pt;}
+
+h1 {
+ font-family: 'Roboto Condensed', sans-serif;
+ margin: 0;
+ padding: 0 0 15px 0;
+}
+
+h2 {
+ font-family: 'Roboto Condensed', sans-serif;
+ margin: 0;
+ padding: 0 0 15px 0;
+ text-align: center;
+ text-decoration: bold;
+}
+
+@media (min-width: 350px) {
+ h1 {font-size: 3.25em;}
+ img{height: 40px;}
+ p{font-size: 10px;}
+ h2{font-size: 17px;}
+}
+
+@media (min-width: 400px) {
+ h1 {font-size: 3.25em;}
+ img{height: 45px;}
+ p{font-size: 15px;}
+ h2{font-size: 17px;}
+}
+@media (min-width: 440px) {
+ h1 {font-size: 3.5em;}
+ img {height: 100px;}
+ p{font-size: 16px;}
+ h2{font-size: 18px;}
+}
+@media (min-width: 500px) {
+ h1 {font-size: 3.75em;}
+ img{height: 125px;}
+ p{font-size: 16px;}
+ h2{font-size: 19px;}
+}
+@media (min-width: 630px) {
+ h1 {font-size: 5em;}
+ img{height: 150px;}
+ p{font-size: 20px;}
+ h2{font-size: 24px;}
+}
+@media (min-width: 768px) {
+ h1 {
+ font-size: 5em;
+ padding-bottom: 30px;
+ }
+ img{height: 175px;}
+ p{font-size: 22px;}
+ h2{font-size: 26px;}
+}
+@media (min-width: 1200px) {
+ h1 {font-size: 8em;}
+ img{height: 250px;}
+ p{font-size: 24px;}
+ h2{font-size: 28px;}
+}
+
+p{
+
+ font-family: 'Roboto Condensed', sans-serif;
+}
+
+h3{
+ text-align: center;
+ font-size: 30px;
+ font-family: 'Roboto Condensed', sans-serif;
+}
+
+footer{
+ padding: 20px;
+ background-color: #e0e0e0;
+ font-family: 'Roboto Condensed', sans-serif;
+}
+
+@keyframes dimImg{
+ from {opacity: 1;
+ filter: alpha(opacity=100);}
+ to {opacity: 0.4;
+ filter: alpha(opacity=50);}
+}
+
+@keyframes revealText{
+ from {opacity: 0.4;
+ filter: alpha(opacity=50);}
+ to {opacity: 1;
+ filter: alpha(opacity=100);}
+}
+
+.pictureContainer{
+ float: right;
+ position: relative;
+}
+
+.pictureContainer a{
+ opacity: 0;
+ position: absolute;
+ text-align: center;
+ top: 5px;
+ left: 5px;
+}
+
+
+.pictureContainer:hover img{
+
+ animation-name: dimImg;
+ animation-duration: 1s;
+ opacity: 0.4;
+ filter: alpha(opacity=50);
+}
+
+.pictureContainer:hover a{
+ animation-name: revealText;
+ animation-duration: 1s;
+ opacity: 1;
+}
+
+.canvasText{
+ margin: 0px;
+ display: inline-block;
+
+ text-align: left;
+}
+
+#c1, #c2, #c3{
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+ width: 50%;
+}
+
+.button{
+ padding: 16px 32px;
+ text-align: center;
+ text-decoration: none;
+ display: inline-block;
+ font-size: 16px;
+ margin: 4px 2px;
+ -webkit-transition-duration: 0.4s; /* Safari */
+ transition-duration: 0.4s;
+ cursor: pointer;
+ background-color: white;
+ color: black;
+ border: 2px solid #555555;
+ float: right;
+}
+
+.button:hover {
+ background-color: #555555;
+ color: white;
+}
+
+