🏡 index : old_projects/demo_old.git

author yaqubroli <walchuk2018@icloud.com> 2023-01-03 13:55:14.0 -08:00:00
committer yaqubroli <walchuk2018@icloud.com> 2023-01-03 13:55:14.0 -08:00:00
commit
1b12613a1b8015fbdfd92f576162d6adccc5ab70 [patch]
tree
e434e7ae20d781f5b2941685743162f7819cce70
download
1b12613a1b8015fbdfd92f576162d6adccc5ab70.tar.gz

Initial commit



Diff

 .DS_Store                                  |   0 
 .classpath                                 |  10 ++++++++++
 .gitignore                                 |   1 +
 .project                                   |  17 +++++++++++++++++
 .settings/org.eclipse.core.resources.prefs |   2 ++
 .settings/org.eclipse.jdt.core.prefs       |  14 ++++++++++++++
 src/.DS_Store                              |   0 
 src/module-info.java                       |   3 +++
 src/absmaths/AbsMaths.java                 |  51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/absmaths/Complex.java                  |  23 +++++++++++++++++++++++
 src/absmaths/Matrix.java                   |  66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/absmaths/package-info.java             |   1 +
 src/eyecandy/CubeRotator.java              | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/eyecandy/Drawable.java                 |   8 ++++++++
 src/eyecandy/Mandelbrot.java               |  53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/eyecandy/Modulo.java                   | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/eyecandy/Sorter.java                   | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/eyecandy/StackableBackground.java      |  51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/eyecandy/ball.gif                      |   0 
 src/eyecandy/fontsheet.gif                 |   0 
 src/main/DemoPanel.java                    |  83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/main/KeyHandler.java                   |  35 +++++++++++++++++++++++++++++++++++
 src/main/Main.java                         |  27 +++++++++++++++++++++++++++
 23 files changed, 813 insertions(+)

diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..31ec78e3db287211329a36139dfbf9066092050d 100644
Binary files /dev/null and a/.DS_Store differdiff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..57bca72 100644
--- /dev/null
+++ a/.classpath
@@ -1,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17">
		<attributes>
			<attribute name="module" value="true"/>
		</attributes>
	</classpathentry>
	<classpathentry kind="src" path="src"/>
	<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6dd29b7 100644
--- /dev/null
+++ a/.gitignore
@@ -1,0 +1,1 @@
bin/
diff --git a/.project b/.project
new file mode 100644
index 0000000..40826cd 100644
--- /dev/null
+++ a/.project
@@ -1,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
	<name>Demo</name>
	<comment></comment>
	<projects>
	</projects>
	<buildSpec>
		<buildCommand>
			<name>org.eclipse.jdt.core.javabuilder</name>
			<arguments>
			</arguments>
		</buildCommand>
	</buildSpec>
	<natures>
		<nature>org.eclipse.jdt.core.javanature</nature>
	</natures>
</projectDescription>
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..99f26c0 100644
--- /dev/null
+++ a/.settings/org.eclipse.core.resources.prefs
@@ -1,0 +1,2 @@
eclipse.preferences.version=1
encoding/<project>=UTF-8
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..8c9943d 100644
--- /dev/null
+++ a/.settings/org.eclipse.jdt.core.prefs
@@ -1,0 +1,14 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=17
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=17
diff --git a/src/.DS_Store b/src/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..86b16195756e0a34218dfa4ac7cf19a2a8952ea9 100644
Binary files /dev/null and a/src/.DS_Store differdiff --git a/src/module-info.java b/src/module-info.java
new file mode 100644
index 0000000..00ac793 100644
--- /dev/null
+++ a/src/module-info.java
@@ -1,0 +1,3 @@
module Demo {
	requires java.desktop;
}
diff --git a/src/absmaths/AbsMaths.java b/src/absmaths/AbsMaths.java
new file mode 100644
index 0000000..b4bb4d0 100644
--- /dev/null
+++ a/src/absmaths/AbsMaths.java
@@ -1,0 +1,51 @@


package absmaths;

public class AbsMaths {
	public static int[][] newTimesTable (int width, int height) { // generates a new 2d array times table of arbitrary size
		int[][] arr = new int[width][height];
		for (int x=0; x<width; x++) {
			for (int y=0; y<height; y++) {
				arr[x][y] = x * y;
			}
		}
		return arr;
	}
	
	public static int[] sanitizeToBounds (int[] array, int floor, int ceiling) { // i wish to death that i had an array map function here
		int[] result = new int[array.length];
		for (int i = 0; i < result.length; i++) {
			result[i] = Math.min(ceiling, Math.max(floor, array[i]));
		}
		return result;
	}
	
	public static float[] sanitizeToBounds (float[] array, float floor, float ceiling) {
		float[] result = new float[array.length];
		for (int i = 0; i < result.length; i++) {
			result[i] = Math.min(ceiling, Math.max(floor, array[i]));
		}
		return result;
	}
	
	public static double[] sanitizeToBounds (double[] array, double floor, double ceiling) {
		double[] result = new double[array.length];
		for (int i = 0; i < result.length; i++) {
			result[i] = Math.min(ceiling, Math.max(floor, array[i]));
		}
		return result;
	}
	
	public static int sanitizeToBounds (int sanitizand, int floor, int ceiling) {
		return Math.min(ceiling, Math.max(floor, sanitizand));
	}

	public static float sanitizeToBounds (float sanitizand, float floor, float ceiling) {
		return Math.min(ceiling, Math.max(floor, sanitizand));
	}

	public static double sanitizeToBounds (double sanitizand, double floor, double ceiling) {
		return Math.min(ceiling, Math.max(floor, sanitizand));
	}
}
diff --git a/src/absmaths/Complex.java b/src/absmaths/Complex.java
new file mode 100644
index 0000000..02410ea 100644
--- /dev/null
+++ a/src/absmaths/Complex.java
@@ -1,0 +1,23 @@
package absmaths;

public class Complex {
	public double i; // imaginary
	public double r; // real
	
	public Complex(double r, double i) {
		this.i = i;
		this.r = r;
	}
	
	public Complex square() {
		this.r = this.r * this.r - this.i * this.i;
		this.i = 0;
		return this;
	}
	
	public Complex add(Complex x) {
		this.r += x.r;
		this.i += x.i;
		return this;
	}
}
diff --git a/src/absmaths/Matrix.java b/src/absmaths/Matrix.java
new file mode 100644
index 0000000..4efff9d 100644
--- /dev/null
+++ a/src/absmaths/Matrix.java
@@ -1,0 +1,66 @@
package absmaths;

import java.util.Arrays;

public class Matrix {
	double[][] matrix;

	public double[][] getMatrix() {
		return matrix;
	}

	public void setMatrix(double[][] matrix) {
		this.matrix = matrix;
	}
	
	public Matrix(double[][] matrix) {
		this.setMatrix(matrix);
	}
	
	public Matrix(int rows, int columns) {
		this.setMatrix(new double[rows][columns]);
	}

	public Matrix getProduct(Matrix m) { // regular matrix multiplication
		double[][] multiplicand = m.getMatrix();
		double[][] result = new double[matrix.length][multiplicand[0].length];
		for (int i = 0; i < matrix.length; i++) {
			for (int j = 0; j < multiplicand[0].length; j++) {
				result[i][j] = vectorDotProduct(result[i], getColumnAsArray(j, multiplicand));
			}
		}
		return new Matrix(result);
	}

	public double[] getColumnAsArray(int indicand) {
		return getColumnAsArray(indicand, this.matrix);
	}
	
	public static double[] getColumnAsArray(int indicand, Matrix x) {
		return getColumnAsArray(indicand, x.getMatrix());
	}
	
	public static double[] getColumnAsArray(int indicand, double[][] x) {
		double[] result = new double[x.length];
		for (int i = 0; i < result.length; i++) {
			result[i] = x[i][indicand];
		}
		return result;
	}
	
	public static double vectorDotProduct(double[] x, double[] y) {
		double product = 0;
		if (x.length == y.length) {
			for (int i = 0; i < x.length; i++) {
				product += x[i] * y[i];
			}
		} else {
			System.out.println("Dot product length wrong!");
		}
		return product;
	}
	
	public String toString() {
		return Arrays.deepToString(matrix);
	}
}
diff --git a/src/absmaths/package-info.java b/src/absmaths/package-info.java
new file mode 100644
index 0000000..911dde4 100644
--- /dev/null
+++ a/src/absmaths/package-info.java
@@ -1,0 +1,1 @@
package absmaths;
diff --git a/src/eyecandy/CubeRotator.java b/src/eyecandy/CubeRotator.java
new file mode 100644
index 0000000..078482a 100644
--- /dev/null
+++ a/src/eyecandy/CubeRotator.java
@@ -1,0 +1,118 @@
package eyecandy;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;

import absmaths.Matrix;

public class CubeRotator implements Drawable {
	
	final int scaleFactor = 50;
	
	final int width = 320;
	final int height = 200;
	
	final int offsetWidth = width / 2;
	final int offsetHeight = height / 2;
	
	enum Axis {
		X,
		Y,
		Z
	}
	
	final Matrix[] verticesPrimary = new Matrix[] {
			new Matrix(new double[][] {{-1}, {-1}, {1}}), //incredibly cursed I know
			new Matrix(new double[][] {{1}, {-1}, {1}}),
			new Matrix(new double[][] {{1}, {1}, {1}}),
			new Matrix(new double[][] {{-1}, {1}, {1}}),
			new Matrix(new double[][] {{-1}, {-1}, {-1}}),
			new Matrix(new double[][] {{1}, {-1}, {-1}}),
			new Matrix(new double[][] {{1}, {1}, {-1}}),
			new Matrix(new double[][] {{-1}, {1}, {-1}})
	};
	
	Matrix[] vertices = verticesPrimary.clone();
	
	Matrix[] verticesProjected = new Matrix[8];
	
	Matrix projectionMatrix = new Matrix(new double[][] {
		{1, 0, 0},
		{0, 1, 0},
		{0, 0, 0}
	});
	
	BufferedImage ball = null;

	public CubeRotator() {
		try {
			ball = ImageIO.read(getClass().getResourceAsStream("ball.gif"));
		} catch (IOException e) {
			System.out.println(e);
		}
	}
	
	public Matrix generateTransform(Axis axis, double theta) {
		switch (axis) {
		case X:
			return new Matrix(new double[][] {
				{1, 0, 0},
				{0, Math.cos(theta), Math.sin(theta) * -1},
				{0, Math.sin(theta), Math.cos(theta)}
			});
		case Y:
			return new Matrix(new double[][] {
				{Math.cos(theta), 0, Math.sin(theta)},
				{0, 1, 0},
				{Math.sin(theta) * -1, 0, Math.cos(theta)}
			});
		case Z:
			return new Matrix(new double[][] {
				{Math.cos(theta), Math.sin(theta) * - 1, 0},
				{Math.sin(theta), Math.cos(theta), 0},
				{0, 0, 1}
			});
		default:
			return null;
		}
	}
	
	@Override
	public void update() {
		Matrix testMatrixA = new Matrix(
				new double[][] {
					{0, 3, 5},
					{5, 5, 2}
				}
		);
		Matrix testMatrixB = new Matrix(
				new double[][] {
					{3, 4},
					{3, -2},
					{4, -2}
				}
		);
		System.out.println(Matrix.getColumnAsArray(0, testMatrixB));
		//System.out.println(testMatrixA.getProduct(testMatrixB).toString());
		for (int i = 0; i < vertices.length; i++) {
			verticesProjected[i] = vertices[i].getProduct(projectionMatrix);
		}
	}

	@Override
	public void draw(Graphics2D graphics2d) {
		for (int i = 0; i < verticesProjected.length; i++) {
			double[][] coords = verticesProjected[i].getMatrix();
			graphics2d.drawImage(
					ball, 
					null, 
					((int)coords[0][0] * scaleFactor) + offsetWidth, 
					((int)coords[0][1] * scaleFactor) + offsetHeight
			);
		}
	}

}
diff --git a/src/eyecandy/Drawable.java b/src/eyecandy/Drawable.java
new file mode 100644
index 0000000..680eec1 100644
--- /dev/null
+++ a/src/eyecandy/Drawable.java
@@ -1,0 +1,8 @@
package eyecandy;

import java.awt.Graphics2D;

public interface Drawable {
	public void update();
	public void draw(Graphics2D graphics2D);
}
diff --git a/src/eyecandy/Mandelbrot.java b/src/eyecandy/Mandelbrot.java
new file mode 100644
index 0000000..8e9c326 100644
--- /dev/null
+++ a/src/eyecandy/Mandelbrot.java
@@ -1,0 +1,53 @@
package eyecandy;

import java.awt.Color;

public class Mandelbrot extends StackableBackground {
	final int width = 320;
	final int height = 200;
	
	final int offsetX = width / 2;
	final int offsetY = height / 2;
	
	final int mandelCycles = 2000; // how many iterations
	
	public Color getFractalValue(double c_r, double c_i) {
		int i = 0;
		double x = 0, y = 0;
		/*
		 * Instead of creating an object for complex numbers I used two doubles.
		 * The reason is because there was some guy on a blog who explained it this way
		 * and I don't have the patience to make it work better
		 */
		while (x*x+y*y <= 4 && i < mandelCycles) {
			double x_new = x*x - y*y + c_r;
            y = 2*x*y + c_i;
            x = x_new;
            i++;
		}
		if (i < mandelCycles) {
			//System.out.println(c_i + " ," + c_r);
			return Color.white;
		} else {
			return Color.black;
		}
	}
	
	@Override
	public int getColour(int x, int y) {
		/*int mandelValue = (int)iterate(
				(double)(x - offsetX) / 40, 
				(double)(y - offsetY) / 40
			);
		mandelValue = Math.min(Math.abs(mandelValue / 5), 255);*/
		return getFractalValue(
				(double)(x - offsetX) / 80, 
				(double)(y - offsetY) / 80
			).getRGB();
	}
	
	@Override
	public int[] transformScreen() {
		return new int[]{0, 0};
	}
}
diff --git a/src/eyecandy/Modulo.java b/src/eyecandy/Modulo.java
new file mode 100644
index 0000000..497bf91 100644
--- /dev/null
+++ a/src/eyecandy/Modulo.java
@@ -1,0 +1,113 @@
package eyecandy;

import java.awt.Color;
import java.lang.Math;

import absmaths.AbsMaths;
import absmaths.Matrix;

/*
 *  TODO:
 *  * remove rotationIteration [DONE]
 *  * remove plazmaIteration [DONE]
 *  * provide a way to switch modes with a cool transition
 *  * use LUTs to save compute time each time
 *  * export 
 */
public class Modulo extends StackableBackground {
	final int rotationIterationCycle = 768;
	final int plazmaIterationCycle = 234;
	final int plazmaSpeedFactor = 3; // to be honest I forget what this does
	
	final boolean procedural = true;
	
	final Matrix transformYUVtoRGB = new Matrix( // transformation matrix that turns YUV PAL colours into RGB; also stolen from wikipedia
		new double[][] {
			{1, 0, 1.13983},
			{1, -0.39465, -0.58060},
			{1, 2.03211, 0}
		}
	);
	
	final int width = 640;
	final int height = 400;
		
	int timeOfLastTransition;
	
	int[][] moduloTable = AbsMaths.newTimesTable(width, height);
	
	public enum Modes {
		HUE_MODE, // modulo value -> H in HSV -> RGB
		PAL_MODE, // modulo value -> -U and V in YUV -> RGB
		GREY_MODE // modulo value -> all RGB
	}
	
	public int getModuloTableEntry(int x) { //who knows
		return (int) (
				(x / (float)(
					(
						((Math.sin((float)iteration / (plazmaIterationCycle / plazmaSpeedFactor)) + 1) * (plazmaIterationCycle / 2)
					) / 5
				) + 0.25)) % 255
			);
	}
	
	public int getColourThroughPAL(int x) {
		x = getModuloTableEntry(x);
		
		// PAL colour spanning via matrix hackery
		Matrix coloursYUV = new Matrix(new double[][]{
			{0},
			{255 - x},
			{x},
		});
		double[] coloursRGB = AbsMaths.sanitizeToBounds(transformYUVtoRGB.getProduct(coloursYUV).getColumnAsArray(0), 0, 255);
		// System.out.println(Arrays.toString(coloursRGB));
		Color color = new Color((int)coloursRGB[0], (int)coloursRGB[1], (int)coloursRGB[2]);
		return color.getRGB();
	}
	
	public int getColourThroughHue(int x) {
		//giant piecewise function stolen from wikipedia and javafied
		float h = (float)getModuloTableEntry(x) / (255f / 6); // hue as float so the multiplication works out
		float w = (float)(1 - Math.abs((h % 2) - 1));
		
		switch ((int)Math.floor(h)) {
			case 0:
				return new Color(1, w, 0).getRGB();
			case 1:
				return new Color(w, 1, 0).getRGB();			
			case 2:
				return new Color(0, 1, w).getRGB();
			case 3:
				return new Color(0, w, 1).getRGB();
			case 4:
				return new Color(w, 0, 1).getRGB();
			case 5:
				return new Color(1, 0, w).getRGB();
			default:
				return 0;
		}
	}
	
	public int getColourStraight(int x) {
		x = getModuloTableEntry(x);
		Color color = new Color(x, x, x);
		return color.getRGB();
	}
	
	@Override
	public int getColour(int x, int y) {
		return getColourStraight(moduloTable[x][y]);
	}
	
	@Override
	public int[] transformScreen() {
		//System.out.println("this is being called");
		return new int[]{
			(int) (Math.sin(((float)(iteration % rotationIterationCycle) / rotationIterationCycle) * 2 * Math.PI) * (width / 4) - (width / 4)),
			(int) (Math.cos(((float)(iteration % rotationIterationCycle) / rotationIterationCycle) * 2 * Math.PI) * (height / 4) - (height / 4))
		};
	}

}
diff --git a/src/eyecandy/Sorter.java b/src/eyecandy/Sorter.java
new file mode 100644
index 0000000..ce0604e 100644
--- /dev/null
+++ a/src/eyecandy/Sorter.java
@@ -1,0 +1,137 @@
package eyecandy;

import java.awt.Color;
import java.awt.Graphics2D;

public class Sorter implements Drawable{
	final int width = 320;
	final int height = 200;
	final int setLength = 93;
	final int setRange = 100;
	final int widthOffset = 20;
	final int heightOffset = 150;
	final int poleDistance = 3;
	
	int[] set = new int[setLength];
	int[] work = new int[setLength];
	
	public enum Modes {
		MERGE_MODE,
		BUBBLE_MODE,
		QUICK_MODE,
	}
	
	private Modes mode;
	/*
	private class BubbleRegisters {
		int hold;
		boolean changed;
	}

	private class MergeRegisters {
		int x;
	}
	
	private class QuickRegisters {
		int x;
	}
	*/
	public Sorter() {
		for (int i = 0; i < setLength; i++) {
			set[i] = (int)Math.floor(Math.random() * setRange);
		}
		mode = Modes.BUBBLE_MODE;
	}
	
	public void setMode(Modes m) {
		switch (m) {
		case MERGE_MODE:
			mode = Modes.MERGE_MODE;
			break;
		case BUBBLE_MODE:
			mode = Modes.BUBBLE_MODE;
			break;
		case QUICK_MODE:
			mode = Modes.QUICK_MODE;
			break;
		}
	}
	
	private int[] bubbleSort(int[] s) {
		boolean changed = false;
		do {
			for (int i = 0; i < s.length - 1; i++) {
				if (s[i] > s[i + 1]) {
					int hold = s[i]; // swap entries
					s[i] = s[i + 1];
					s[i + 1] = hold;
				}
			}
		} while (changed);
		return s;
	}
	
	private int[] topDownMergeSort(int[] s, int n) {
		int[] w = new int[s.length]; // work array, hence 'w'
		
		return s;
	}
	
	private void topDownSplitMerge() {
		
	}
	@Override
	public void update() {
		switch (mode) {
		case MERGE_MODE:
			set = topDownMergeSort(set);
			break;
		case BUBBLE_MODE:
			set = bubbleSort(set);
			break;
		case QUICK_MODE:
			break;
		}
	}
	
	public void renderSet(int offset, Graphics2D graphics2d) {
		for (int i = 0; i < setLength; i++) {
			graphics2d.drawLine(
					widthOffset + (i*poleDistance) + offset, 
					heightOffset, 
					widthOffset + (i*poleDistance) + offset, 
					heightOffset - set[i]
				);
		}
	}

	@Override
	public void draw(Graphics2D graphics2d) {
		switch (mode) {
			case MERGE_MODE: 
				graphics2d.setColor(Color.cyan);
				break;
			case BUBBLE_MODE: 
				graphics2d.setColor(Color.magenta);
				break;
			case QUICK_MODE: 
				graphics2d.setColor(Color.green);
				break;
		}
		renderSet(0, graphics2d);
		switch (mode) {
			case MERGE_MODE: 
				graphics2d.setColor(Color.blue);
				break;
			case BUBBLE_MODE: 
				graphics2d.setColor(Color.red);
				break;
			case QUICK_MODE: 
				graphics2d.setColor(Color.green);
				break;
		}
		graphics2d.setColor(Color.black);
		renderSet(2, graphics2d);
	}

}
diff --git a/src/eyecandy/StackableBackground.java b/src/eyecandy/StackableBackground.java
new file mode 100644
index 0000000..0dc83cc 100644
--- /dev/null
+++ a/src/eyecandy/StackableBackground.java
@@ -1,0 +1,51 @@
package eyecandy;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;

public class StackableBackground implements Drawable {
	
	final int width = 640;
	final int height = 400;
	
	int iteration = 0; // java doesn't handle unsigned ints; this is effectively our "absolute timer" we treat as unsigned.
	
	boolean procedural = false; // not really necessary, might take it out. just enables/disables rendering.
	
	BufferedImage canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
	
	public int getColour(int x, int y) {
		return 255;
	}
	
	@Override
	public void update() {
		iteration++;
		if (iteration < 0) {
			iteration = Math.abs(iteration); // replace with bitwise hack later
		}
		for (int x=0; x<width; x++) {
			for (int y=0; y<height; y++) {
				canvas.setRGB(x, y, getColour(x, y));
			}
		}
		//System.out.println(iteration);
	}
	
	public int[] transformScreen() {
		return new int[] {0, 0};
	}
	

	@Override
	public void draw(Graphics2D graphics2D) {
		int[] currentTransform = transformScreen();
		graphics2D.drawImage(
				canvas, 
				null, 
				currentTransform[0], 
				currentTransform[1]
			);
	}

}
diff --git a/src/eyecandy/ball.gif b/src/eyecandy/ball.gif
new file mode 100644
index 0000000000000000000000000000000000000000..f081165f3679adaaff1de0719954f80f1e004e43 100644
Binary files /dev/null and a/src/eyecandy/ball.gif differdiff --git a/src/eyecandy/fontsheet.gif b/src/eyecandy/fontsheet.gif
new file mode 100644
index 0000000000000000000000000000000000000000..ca8d3f94cee5b591cee60a80d836d0af0f9d097f 100644
Binary files /dev/null and a/src/eyecandy/fontsheet.gif differdiff --git a/src/main/DemoPanel.java b/src/main/DemoPanel.java
new file mode 100644
index 0000000..cc9af2d 100644
--- /dev/null
+++ a/src/main/DemoPanel.java
@@ -1,0 +1,83 @@
package main;

import java.awt.Dimension;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
//import java.awt.event.KeyEvent;

import javax.swing.JPanel;

import eyecandy.Modulo;
import eyecandy.Sorter;
import eyecandy.CubeRotator;
import eyecandy.Mandelbrot;

public class DemoPanel extends JPanel implements Runnable {
	private static final long serialVersionUID = -3550958944572506053L;

	// Screen Settings
	final int scale = 3;
	//final int screenWidth = 320;
	//final int screenHeight = 200; // VGA resolution
	
	final int screenWidth = 320;
	final int screenHeight = 200;
	
	// FPS
	final int FPS = 60;
	final double drawInterval = 1000000000/FPS;
	
	// Draw spanner
	double nextDrawTime;
	double remainingTime;
	
	Thread demoThread;
	//Modulo modulo = new Modulo();
	CubeRotator modulo = new CubeRotator(); // I know it makes no sense, just roll with it
	KeyHandler keyHandler = new KeyHandler();
	
	public DemoPanel() {
		this.setPreferredSize(new Dimension(screenWidth, screenHeight));
		this.setBackground(Color.black);
		this.setDoubleBuffered(true);
	}
	
	
	public void initDemo() {
		demoThread = new Thread(this); // Passing own object to demoThread
		demoThread.start();
	}
	
	@Override
	public void run() {
		while (demoThread != null) {
			update();
			repaint();
			
			try {
				nextDrawTime = System.nanoTime() + drawInterval;
				remainingTime = nextDrawTime - System.nanoTime();
				remainingTime = remainingTime / 1000000;
				if (remainingTime < 0) {
					remainingTime = 0;
				}
				Thread.sleep((long) remainingTime);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	public void update() {
		modulo.update();
	}
	
	public void paintComponent(Graphics graphics) {
		super.paintComponent(graphics);
		Graphics2D g2 = (Graphics2D)graphics;
		modulo.draw(g2);
		g2.dispose();
	}
}
diff --git a/src/main/KeyHandler.java b/src/main/KeyHandler.java
new file mode 100644
index 0000000..611d344 100644
--- /dev/null
+++ a/src/main/KeyHandler.java
@@ -1,0 +1,35 @@
package main;

import java.awt.event.KeyEvent;

/* 
 * This class only exists for development's sake, do not intend to use it in the final product.
 */
import java.awt.event.KeyListener;

public class KeyHandler implements KeyListener {
	
	public int lastTyped = -1;

	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub
		lastTyped = e.getKeyCode();
	}

	@Override
	public void keyPressed(KeyEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void keyReleased(KeyEvent e) {
		// TODO Auto-generated method stub

	}
	
	public void clearTypeBuffer() {
		lastTyped = -1;
	}
}
diff --git a/src/main/Main.java b/src/main/Main.java
new file mode 100644
index 0000000..23fd5f8 100644
--- /dev/null
+++ a/src/main/Main.java
@@ -1,0 +1,27 @@
package main;

import javax.swing.JFrame;

public class Main {
	public static void main(String[] args) {
		
		// Initialize window
		JFrame window = new JFrame();
		window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		window.setResizable(false); // Change later
		
		// New DemoPanel functions as background surface for all blits
		DemoPanel demoPanel = new DemoPanel();
		window.add(demoPanel);
		
		// Pack to size of subcomponents
		window.pack();
		
		// Show
		window.setLocationRelativeTo(null);
		window.setVisible(true);
		
		// Start the demo thread
		demoPanel.initDemo();
	}
}