Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                
<div>
	Click on a tile to place a tile next to it. <br> 
	Move the cursor away to choose the type of tile.<br>
	Move your cursor around to choose where the new tile will go.<br>
	Right click to cancel placing or remove a tile. <br>
	<br>
	The aim it to place the tiles so that the coloured lines are continuous but you dont have to.
</div>
<canvas id="canvas" height= 800 width=800 onselectstart="return false">
	
</canvas>
<div>
You can click the canvas to stop the tiles disapearing at the start if u want.
</div>
              
            
!

CSS

              
                canvas{
	background:#ffb100;
	display:block;
	box-shadow:0px 0px 20px -18px black;
}
body{
	
	//display:flex;
	//flex-flow:row wrap;
	//justify-content:center;
	//align-items:center;
	height:100vh;
	background:#fb1;
	font-family:sans-serif;
	margin:30px  calc(max(50vw, 400px) - 400px);
}
              
            
!

JS

              
                //Inspired by Veritasium

var lineSpace = 10 / 3;
var tileChangeDistance = 50;
var style = {
	colours: {
		background: "#fb1",
		smallFill: "#0002",
		bigFill: "#0001",
		rhombStroke: "#000",
		ruleA: "#f4a",
		ruleB: "#c0ffee"
	},
	lines: {
		big: 1,
		small: 1,
		ruleA: 1,
		ruleB: 1,
		ruleARadius:10/3,
		ruleBRadius:10/3,
	}
};


window.onload = function () {
	// text = new sampleText();
	//setValue();
	var gui = new dat.GUI();
	gui.add(style.colours, "background").onChange(setValue);
	gui.add(style.colours, "smallFill").onChange(setValue);
	gui.add(style.colours, "bigFill").onChange(setValue);
	gui.add(style.colours, "rhombStroke").onChange(setValue);
	gui.add(style.colours, "ruleA").onChange(setValue);
	gui.add(style.colours, "ruleB").onChange(setValue);
	
	gui.add(style.lines, "big").onChange(setValue);
	gui.add(style.lines, "small").onChange(setValue);
	gui.add(style.lines, "ruleA").onChange(setValue);
	gui.add(style.lines, "ruleB").onChange(setValue);
	
	gui.add(style.lines, "ruleARadius").onChange(setValue);
	gui.add(style.lines, "ruleBRadius").onChange(setValue);
	gui.close();
};
function setValue(){
	world.Draw();
}

class Vec2 {
	constructor(x, y) {
		if (x.hasOwnProperty("x")) {
			if (y && y.hasOwnProperty("x")) {
				var out = y.sub(x);
				this.x = out.x;
				this.y = out.y;
			}else{
				this.x = x.x;
				this.y = x.y;
			}
		} else {
			this.x = x;
			this.y = y;
		}
	}

	distance(vec) {
		var delta = this.sub(vec);
		return delta.magnitude();
	}

	add(vec) {
		return new Vec2(this.x + vec.x, this.y + vec.y);
	}
	sub(vec) {
		return new Vec2(this.x - vec.x, this.y - vec.y);
	}
	times(factor) {
		return new Vec2(this.x * factor, this.y * factor);
	}
	magnitude() {
		return Math.sqrt(this.x * this.x + this.y * this.y);
	}
	normalised() {
		var out = this.times(1 / this.magnitude());
		return new Vec2(out.x, out.y);
	}
	dot(vec) {
		return this.x * vec.x + this.y * vec.y;
	}
	angle(vec) {
		var x1 = this.x;
		var y1 = this.y;
		var x2 = vec.x;
		var y2 = vec.y;

		var dot = x1 * x2 + y1 * y2; //# dot product between [x1, y1] and [x2, y2]
		var det = x1 * y2 - y1 * x2; //# determinant
		var angle = Math.atan2(det, dot); //# atan2(y, x) or atan2(sin, cos)
		return angle;
		/*
		return Math.acos(
			(this.x * vec.x + this.y * vec.y) /
				Math.sqrt(this.x * this.x + this.y * this.y) /
				Math.sqrt(vec.x * vec.x + vec.y * vec.y)
		);*/
	}
	rotate(angle) {
		var magnitude = this.magnitude();
		var myangle = this.angle(new Vec2(0, 1));
		var newAngle = myangle + angle;

		var x = Math.sin(newAngle);
		var y = Math.cos(newAngle);

		var newVec = new Vec2(x, y);
		newVec = newVec.times(magnitude);
		return newVec;
	}

	clone() {
		return new Vec2(this.x, this.y);
	}
}

const seg = Math.PI / 5;
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

var scale = 50;
var offset = new Vec2(1, 1);

function offsetAndScale(pos) {
	pos = pos.add(offset);
	pos = pos.times(scale);
	return pos;
}
/*
function cnvToWorldOffsetAndScale(pos){
	//n = (i+offset)*scale
	//i= (n/scale)-offset
	pos = pos.times(1/scale)
	pos = pos.add(offset.times(-1));
	return pos;
}
*/
class Tile {
	constructor(pos = new Vec2(0, 0), rotation = 0, alt) {
		if (pos.hasOwnProperty("pos")) {
			this.rotation = pos.angle + rotation;
			this.pos = pos.pos;
			this.alt = alt;

			if (alt) {
				var translation = new Vec2(
					Math.cos(-this.rotation),
					Math.sin(-this.rotation)
				);
				this.pos = this.pos.add(translation.times(-1));
			}
		} else {
			this.pos = pos;
			this.rotation = rotation;
		}
		this.verts = [new Vec2(0, 0), new Vec2(1, 0), new Vec2(1, 1), new Vec2(0, 1)];
		ctx.strokeStyle = style.colours.rhombStroke;
		ctx.globalCompositeOperation = "replace";
	}
	Draw() {
		
		var rotatedVerts = this.verts.map((i) => i.rotate(this.rotation));
		var translatedVerts = rotatedVerts.map((i) => i.add(this.pos));
		var transformedVerts = translatedVerts.map((i) => offsetAndScale(i));

		ctx.beginPath();
		ctx.moveTo(transformedVerts[0].x, transformedVerts[0].y);
		ctx.lineTo(transformedVerts[1].x, transformedVerts[1].y);
		ctx.lineTo(transformedVerts[2].x, transformedVerts[2].y);
		ctx.lineTo(transformedVerts[3].x, transformedVerts[3].y);
		ctx.lineTo(transformedVerts[0].x, transformedVerts[0].y);
		ctx.fill();
		ctx.stroke();

		return transformedVerts;
	}
	getTransformedLocalVerts() {
		var rotatedVerts = this.verts.map((i) => i.rotate(this.rotation));
		var translatedVerts = rotatedVerts.map((i) => i.add(this.pos));
		var transformedVerts = translatedVerts.map((i) => offsetAndScale(i));
		return transformedVerts;
	}

	getSidePlacementAndRotation(side) {
		side = side % this.verts.length;
		var rotatedVerts = this.verts.map((i) => i.rotate(this.rotation));
		var translatedVerts = rotatedVerts.map((i) => i.add(this.pos));
		var vert = translatedVerts[side];
		var sideVec = translatedVerts[
			(side + this.verts.length + 1) % this.verts.length
		].sub(translatedVerts[side]);
		var ang = sideVec.angle(new Vec2(0, 1));
		return { pos: vert, angle: ang };
	}
	getCenter() {
		var rotatedVerts = this.verts.map((i) => i.rotate(this.rotation));
		var translatedVerts = rotatedVerts.map((i) => i.add(this.pos));
		return this.pos.add(translatedVerts[1].add(translatedVerts[2]).times(0.5));
	}
}
class SmallRhombus extends Tile {
	constructor(pos, rotation, flipped) {
		if (pos.hasOwnProperty("pos")) {
			pos.angle -= 1.5 * seg;
		}

		super(pos, rotation, flipped);

		this.verts = [
			new Vec2(0, 0),
			new Vec2(1, 0),
			new Vec2(1 + Math.cos(Math.PI / 5), Math.sin(Math.PI / 5)),
			new Vec2(Math.cos(Math.PI / 5), Math.sin(Math.PI / 5))
		];

		if (this.alt) {
			var trans = this.verts[1].add(this.verts[2].times(-1));
			trans = trans.rotate(this.rotation);
			this.pos = this.pos.add(trans);
		}

		/*
			
	
			new BigRhombus(tiles[sInd].getSidePlacementAndRotation(1), 9 * seg, true)
			new BigRhombus(tiles[sInd].getSidePlacementAndRotation(1), 5 * seg, true)
			new BigRhombus(tiles[sInd].getSidePlacementAndRotation(2), 0 * seg)
			new BigRhombus(tiles[sInd].getSidePlacementAndRotation(0), 7 * seg)
		);

		*/

		this.possibleAdjacents = [
			[
				function (tile) {
					return new SmallRhombus(
						tile.getSidePlacementAndRotation(0),
						5 * seg,
						true
					);
				},
				function (tile) {
					return new BigRhombus(tile.getSidePlacementAndRotation(1), 9 * seg, true);
				}
			],
			[
				function (tile) {
					return new SmallRhombus(tile.getSidePlacementAndRotation(2), 8 * seg);
				},
				function (tile) {
					return new BigRhombus(tile.getSidePlacementAndRotation(1), 5 * seg, true);
				}
			],
			[
				function (tile) {
					return new BigRhombus(tile.getSidePlacementAndRotation(2), 0 * seg);
				}
			],
			[
				function (tile) {
					return new BigRhombus(tile.getSidePlacementAndRotation(0), 7 * seg);
				}
			]
		];
	}
	getSidePlacementAndRotation(side) {
		var output = super.getSidePlacementAndRotation(side);
		return output;
	}
	Draw() {
		ctx.lineWidth = style.lines.small;
		ctx.fillStyle = style.colours.smallFill;
		ctx.strokeStyle = style.colours.rhombStroke;
		var verts = super.Draw();

		ctx.strokeStyle = style.colours.ruleA;
		ctx.lineWidth = style.lines.ruleA;

		ctx.beginPath();
		var o = 1;
		ctx.arc(
			verts[1].x,
			verts[1].y,
			scale / style.lines.ruleARadius,
			0 - this.rotation + (o + 0) * seg,
			0 - this.rotation + (o + 4) * seg
		);
		ctx.stroke();

		ctx.strokeStyle = style.colours.ruleB;
		ctx.lineWidth = style.lines.ruleB;
		ctx.beginPath();
		o = 6;
		ctx.arc(
			verts[3].x,
			verts[3].y,
			scale / style.lines.ruleBRadius,
			0 - this.rotation + (o + 0) * seg,
			0 - this.rotation + (o + 4) * seg
		);
		ctx.stroke();

		ctx.strokeStyle = "#000";
	}
}
class BigRhombus extends Tile {
	constructor(pos, rotation, alt) {
		if (pos.hasOwnProperty("pos")) {
			pos.angle -= 0.5 * seg;
		}
		super(pos, rotation, alt);
		this.verts = [
			new Vec2(0, 0),
			new Vec2(1, 0),
			new Vec2(1 + Math.cos((Math.PI * 2) / 5), Math.sin((Math.PI * 2) / 5)),
			new Vec2(Math.cos((Math.PI * 2) / 5), Math.sin((Math.PI * 2) / 5))
		];
		if (this.alt) {
			var trans = this.verts[1].add(this.verts[2].times(-1));
			trans = trans.rotate(this.rotation);
			this.pos = this.pos.add(trans);
		}

		this.possibleAdjacents = [
			[
				function (tile) {
					return new SmallRhombus(tile.getSidePlacementAndRotation(0), 0 * seg);
				},
				function (tile) {
					return new BigRhombus(tile.getSidePlacementAndRotation(0), 0 * seg);
				}
			],
			[
				function (tile) {
					return new SmallRhombus(
						tile.getSidePlacementAndRotation(1),
						5 * seg,
						true
					);
				},
				function (tile) {
					return new BigRhombus(tile.getSidePlacementAndRotation(2), 1 * seg, true);
				}
			],
			[
				function (tile) {
					return new SmallRhombus(tile.getSidePlacementAndRotation(3), 6 * seg);
				},
				function (tile) {
					return new BigRhombus(tile.getSidePlacementAndRotation(2), 5 * seg, true);
				}
			],
			[
				function (tile) {
					return new SmallRhombus(
						tile.getSidePlacementAndRotation(0),
						2 * seg,
						true
					);
				},
				function (tile) {
					return new BigRhombus(tile.getSidePlacementAndRotation(0), 6 * seg);
				}
			]
		];
	}
	getSidePlacementAndRotation(side) {
		var output = super.getSidePlacementAndRotation(side);
		return output;
	}
	Draw() {
		ctx.lineWidth = style.lines.big;
		ctx.fillStyle = style.colours.bigFill;
		ctx.strokeStyle = style.colours.rhombStroke;
		var verts = super.Draw();

		ctx.strokeStyle = style.colours.ruleA;
		ctx.lineWidth = style.lines.ruleA;
		ctx.beginPath();
		var o = 5;
		ctx.arc(
			verts[2].x,
			verts[2].y,
			scale / style.lines.ruleARadius,
			0 - this.rotation + (o + 0) * seg,
			0 - this.rotation + (o + 2) * seg
		);
		ctx.stroke();

		ctx.strokeStyle = style.colours.ruleB;
		ctx.lineWidth = style.lines.ruleB;
		ctx.beginPath();
		o = 0;
		ctx.arc(
			verts[0].x,
			verts[0].y,
			scale - scale / style.lines.ruleBRadius,
			0 - this.rotation + (o + 0) * seg,
			0 - this.rotation + (o + 2) * seg
		);
		ctx.stroke();

		ctx.strokeStyle = "#000";
	}
}

class PlaceableTile {
	constructor(tile) {
		this.tile = tile;
	}
	isClickInsideThisTile(clickPosLocal) {
		var verts = this.tile.getTransformedLocalVerts();

		var check1 = this.IsInRightRhobusAxis(
			verts[0],
			verts[1],
			verts[3],
			clickPosLocal
		);
		var check2 = this.IsInRightRhobusAxis(
			verts[1],
			verts[2],
			verts[0],
			clickPosLocal
		);

		if (check1 && check2) {
			//This has been clicked!
			return true;
			//this.clicked(clickPosLocal);
		}
		return false;
	}
	IsInRightRhobusAxis(a, b, c, pos) {
		var gradient = (a.y - c.y) / (a.x - c.x);
		if (isNaN(gradient)) {
			gradient = 0.00001;
		}
		var c1 = a.y - a.x * gradient;
		var c2 = b.y - b.x * gradient;

		var topC = Math.max(c1, c2);
		var botC = Math.min(c1, c2);
		/*
		ctx.beginPath();
		ctx.moveTo(0,topC);
		ctx.lineTo(1000,topC+(gradient*1000))
		ctx.stroke();
		ctx.beginPath();
		
		ctx.moveTo(0,botC);
		ctx.lineTo(1000,botC+(gradient*1000))
		ctx.stroke();
*/

		var upperLimit = pos.x * gradient + topC;
		var lowerLimit = pos.x * gradient + botC;
		var mousePos = pos.y;

		return pos.y > lowerLimit && pos.y < upperLimit;

		//dy/dx => -dx/dy
	}
	mouseMoved(pos) {
		this.ghostNewTile(pos);
	}
	clicked(pos) {
		var tile = this.getNewTile(pos);
		var placeableTile = new PlaceableTile(tile);
		return placeableTile;
		/*
		console.log("Ive Been clicked!");
		//get edge
			//get center of rhomb
			var center = this.tile.getCenter();
			//get dmousePos
			var dMousePos = pos.add(center.times(-1));
			//rotate it by romb rotation
			var dMouseRotated = dMousePos.rotate(this.tile.rotation);
			//get angle %~90
			var side = Math.floor(dMouseRotated.angle(new Vec2(0,1))/(Math.PI/2));
			console.log(side);
		//get possibilities 
			//array of all possibilities
			
			//choose for edge
		//choose one?
			//Distance from edge / 20?
		//place
			//new tile defined by edge posisbilities 
			//new placeable tile with rhomb as param.
		
		//users perspective:
		//Click a rhomb,
		//move mouse to pick edge and posibility
		//click again to place.
		*/
	}

	ghostNewTile(pos) {
		var tile = this.getNewTile(pos);
		//console.log("moving");
		
		tile.Draw();
		var centerLoc = offsetAndScale(this.tile.getCenter().times(0.5));
		ctx.strokeStyle="black";
		ctx.beginPath();
		ctx.arc(centerLoc.x,centerLoc.y,tileChangeDistance,0,Math.PI*2);
		ctx.moveTo(centerLoc.x+tileChangeDistance*2,centerLoc.y);
		ctx.arc(centerLoc.x,centerLoc.y,tileChangeDistance*2,0,Math.PI*2);
		ctx.stroke();
		
	}
	getNewTile(pos) {
		//get edge
		//get center of rhomb
		var center = offsetAndScale(this.tile.getCenter().times(0.5));
		/*
		ctx.beginPath();
		ctx.arc(center.x, center.y, 20, 0, 2 * Math.PI);
		ctx.stroke();*/

		//get dmousePos
		var dMousePos = pos.add(center.times(-1));
		//rotate it by romb rotation
		//var dMouseRotated = dMousePos.rotate(this.tile.rotation);
		var dMouseRotated = dMousePos;
		//get angle %~90
		var side =
			Math.floor(
				0.5 +
					(Math.PI * 2 + this.tile.rotation - dMouseRotated.angle(new Vec2(0, 1))) /
						(Math.PI / 2)
			) + 2;
		//get possibilities
		//array of all possibilities
		var allpossibilities = this.tile.possibleAdjacents;
		//choose for edge
		var edgePossibilities = allpossibilities[side % allpossibilities.length];
		//choose one?
		//Distance from edge / 20?
		var choice = Math.floor(dMouseRotated.magnitude() / tileChangeDistance);
		var placementChoice = edgePossibilities[choice % edgePossibilities.length];
		//
		//place
		var chosenTile = placementChoice(this.tile);

		return chosenTile;
		//new tile defined by edge posisbilities
		//new placeable tile with rhomb as param.
	}
	
}

class TilePlacementWorld {
	constructor() {
		var tiles = this.getInitialTiles('{"tiles":[{"isSmallRhombus":false,"pos":{"x":8,"y":6},"rotation":0.4141592653589794},{"isSmallRhombus":false,"pos":{"x":8,"y":6},"rotation":1.6707963267948962},{"isSmallRhombus":false,"pos":{"x":8,"y":6},"rotation":-3.3557519189487732},{"isSmallRhombus":false,"pos":{"x":8,"y":6},"rotation":-2.0991148575128564},{"isSmallRhombus":false,"pos":{"x":8,"y":6},"rotation":-0.8424777960769392},{"isSmallRhombus":true,"pos":{"x":8.261367277994424,"y":8.604954723645589},"rotation":1.6707963267948958},{"isSmallRhombus":true,"pos":{"x":7.495918156353792,"y":6.8636558891747566},"rotation":5.440707511102647},{"isSmallRhombus":true,"pos":{"x":5.603307766096819,"y":7.053550332066704},"rotation":0.41415926535897807},{"isSmallRhombus":true,"pos":{"x":7.022844582521129,"y":5.787474024901205},"rotation":4.184070449666731},{"isSmallRhombus":true,"pos":{"x":7.9001665833531725,"y":5.004995834721974},"rotation":2.9274333882308134},{"isSmallRhombus":true,"pos":{"x":6.257395460880497,"y":4.046175190430373},"rotation":-0.8424777960769387},{"isSmallRhombus":true,"pos":{"x":9.319703399777483,"y":3.738919527556474},"rotation":4.184070449666731},{"isSmallRhombus":true,"pos":{"x":8.915454972778102,"y":5.597579582009256},"rotation":1.6707963267948962},{"isSmallRhombus":true,"pos":{"x":10.558226095250777,"y":6.556400226300857},"rotation":2.927433388230814},{"isSmallRhombus":true,"pos":{"x":8.665615704993805,"y":6.746294669192806},"rotation":0.4141592653589794},{"isSmallRhombus":false,"pos":{"x":7.022844582521129,"y":5.787474024901205},"rotation":3.5557519189487716},{"isSmallRhombus":false,"pos":{"x":7.9001665833531725,"y":5.004995834721974},"rotation":2.299114857512854},{"isSmallRhombus":false,"pos":{"x":8.915454972778102,"y":5.597579582009256},"rotation":1.0424777960769371},{"isSmallRhombus":false,"pos":{"x":8.665615704993805,"y":6.746294669192806},"rotation":6.069026041820607},{"isSmallRhombus":false,"pos":{"x":8.261367277994424,"y":8.604954723645589},"rotation":1.0424777960769371},{"isSmallRhombus":false,"pos":{"x":8.261367277994424,"y":8.604954723645589},"rotation":6.069026041820606},{"isSmallRhombus":false,"pos":{"x":8.261367277994424,"y":8.604954723645589},"rotation":4.8123889803846875},{"isSmallRhombus":false,"pos":{"x":8.261367277994424,"y":8.604954723645589},"rotation":3.5557519189487703},{"isSmallRhombus":false,"pos":{"x":7.495918156353796,"y":6.863655889174756},"rotation":4.812388980384687,"alt":true},{"isSmallRhombus":false,"pos":{"x":5.603307766096823,"y":7.053550332066699},"rotation":-0.21415926535898278,"alt":true},{"isSmallRhombus":false,"pos":{"x":5.603307766096823,"y":7.053550332066699},"rotation":4.812388980384686},{"isSmallRhombus":false,"pos":{"x":5.603307766096823,"y":7.053550332066699},"rotation":3.5557519189487685},{"isSmallRhombus":false,"pos":{"x":5.603307766096823,"y":7.053550332066699},"rotation":2.2991148575128513},{"isSmallRhombus":false,"pos":{"x":6.257395460880497,"y":4.046175190430373},"rotation":4.812388980384689,"alt":true},{"isSmallRhombus":false,"pos":{"x":6.257395460880497,"y":4.046175190430373},"rotation":3.5557519189487716},{"isSmallRhombus":false,"pos":{"x":6.257395460880497,"y":4.046175190430373},"rotation":2.299114857512855},{"isSmallRhombus":false,"pos":{"x":6.257395460880497,"y":4.046175190430373},"rotation":1.0424777960769376},{"isSmallRhombus":false,"pos":{"x":9.319703399777483,"y":3.738919527556475},"rotation":3.5557519189487707,"alt":true},{"isSmallRhombus":false,"pos":{"x":9.319703399777483,"y":3.738919527556475},"rotation":2.299114857512853},{"isSmallRhombus":false,"pos":{"x":9.319703399777483,"y":3.738919527556475},"rotation":1.0424777960769354},{"isSmallRhombus":false,"pos":{"x":9.319703399777483,"y":3.738919527556475},"rotation":6.069026041820604},{"isSmallRhombus":false,"pos":{"x":10.558226095250777,"y":6.556400226300857},"rotation":2.2991148575128544,"alt":true},{"isSmallRhombus":false,"pos":{"x":10.558226095250777,"y":6.556400226300857},"rotation":1.0424777960769376},{"isSmallRhombus":false,"pos":{"x":10.558226095250777,"y":6.556400226300857},"rotation":6.069026041820607},{"isSmallRhombus":false,"pos":{"x":10.558226095250777,"y":6.556400226300857},"rotation":4.812388980384689},{"isSmallRhombus":true,"pos":{"x":9.238522695473295,"y":8.817480698744383},"rotation":1.0424777960769376,"alt":true},{"isSmallRhombus":true,"pos":{"x":10.003971817113923,"y":10.558779533215219},"rotation":2.299114857512853,"alt":true},{"isSmallRhombus":true,"pos":{"x":8.361200694641251,"y":9.599958888923615},"rotation":-0.2141592653589801},{"isSmallRhombus":false,"pos":{"x":9.238522695473295,"y":8.817480698744385},"rotation":0.4141592653589764},{"isSmallRhombus":false,"pos":{"x":12.300830634370278,"y":8.510225035870489},"rotation":-2.099114857512857,"alt":true},{"isSmallRhombus":false,"pos":{"x":11.896582207370898,"y":10.368885090323271},"rotation":2.9274333882308126,"alt":true},{"isSmallRhombus":false,"pos":{"x":10.003971817113923,"y":10.558779533215217},"rotation":1.6707963267948949,"alt":true},{"isSmallRhombus":false,"pos":{"x":10.658059511897605,"y":7.551404391578883},"rotation":-0.8424777960769392},{"isSmallRhombus":true,"pos":{"x":10.658059511897605,"y":7.551404391578883},"rotation":-0.21415926535897978},{"isSmallRhombus":true,"pos":{"x":11.896582207370896,"y":10.368885090323271},"rotation":-2.7274333882308164,"alt":true},{"isSmallRhombus":true,"pos":{"x":12.30083063437028,"y":8.510225035870489},"rotation":-1.4707963267948987,"alt":true},{"isSmallRhombus":true,"pos":{"x":4.2836043663193335,"y":9.31463080451022},"rotation":1.042477796076934,"alt":true},{"isSmallRhombus":true,"pos":{"x":5.703141182743648,"y":8.048554497344725},"rotation":-0.21415926535898278},{"isSmallRhombus":true,"pos":{"x":7.345912305216322,"y":9.00737514163633},"rotation":4.8123889803846875},{"isSmallRhombus":false,"pos":{"x":6.941663878216938,"y":10.866035196089113},"rotation":-3.355751918948775},{"isSmallRhombus":false,"pos":{"x":7.345912305216323,"y":9.007375141636333},"rotation":4.184070449666727,"alt":true},{"isSmallRhombus":false,"pos":{"x":5.703141182743651,"y":8.048554497344726},"rotation":5.440707511102644,"alt":true},{"isSmallRhombus":false,"pos":{"x":4.283604366319337,"y":9.314630804510223},"rotation":0.4141592653589745,"alt":true},{"isSmallRhombus":false,"pos":{"x":5.049053487959961,"y":11.055929638981059},"rotation":1.670796326794892,"alt":true},{"isSmallRhombus":true,"pos":{"x":6.941663878216938,"y":10.866035196089113},"rotation":-2.727433388230816},{"isSmallRhombus":true,"pos":{"x":5.049053487959961,"y":11.055929638981059},"rotation":-3.9840704496667363},{"isSmallRhombus":true,"pos":{"x":5.341940488102408,"y":4.448595608421107},"rotation":4.812388980384686,"alt":true},{"isSmallRhombus":true,"pos":{"x":3.6991693656297198,"y":3.4897749641295155},"rotation":-0.21415926535897967,"alt":true},{"isSmallRhombus":true,"pos":{"x":4.9376920611030215,"y":6.3072556628738905},"rotation":3.555751918948769},{"isSmallRhombus":false,"pos":{"x":3.0450816708460477,"y":6.497150105765833},"rotation":1.6707963267948924},{"isSmallRhombus":false,"pos":{"x":4.937692061103021,"y":6.30725566287389},"rotation":2.92743338823081,"alt":true},{"isSmallRhombus":false,"pos":{"x":5.3419404881024075,"y":4.448595608421109},"rotation":4.184070449666726,"alt":true},{"isSmallRhombus":false,"pos":{"x":3.6991693656297375,"y":3.489774964129501},"rotation":5.440707511102643,"alt":true},{"isSmallRhombus":false,"pos":{"x":2.279632549205421,"y":4.755851271294993},"rotation":0.4141592653589732,"alt":true},{"isSmallRhombus":true,"pos":{"x":6.7614773045267045,"y":3.1825193012556157},"rotation":2.299114857512855},{"isSmallRhombus":true,"pos":{"x":8.65408769478368,"y":2.9926248583636674},"rotation":3.5557519189487703},{"isSmallRhombus":true,"pos":{"x":9.05833612178306,"y":1.1339648039108847},"rotation":4.812388980384688,"alt":true},{"isSmallRhombus":true,"pos":{"x":2.2796325492054215,"y":4.755851271294994},"rotation":1.0424777960769323,"alt":true},{"isSmallRhombus":true,"pos":{"x":3.0450816708460433,"y":6.49715010576583},"rotation":2.299114857512849,"alt":true},{"isSmallRhombus":false,"pos":{"x":5.996028182886072,"y":1.4412204667847832},"rotation":0.41415926535897885},{"isSmallRhombus":false,"pos":{"x":6.7614773045267045,"y":3.1825193012556157},"rotation":1.670796326794896,"alt":true},{"isSmallRhombus":false,"pos":{"x":8.654087694783678,"y":2.9926248583636665},"rotation":2.927433388230813,"alt":true},{"isSmallRhombus":false,"pos":{"x":9.058336121783059,"y":1.1339648039108843},"rotation":4.18407044966673,"alt":true},{"isSmallRhombus":false,"pos":{"x":7.4155649993103845,"y":0.17514415961928176},"rotation":5.440707511102647,"alt":true},{"isSmallRhombus":true,"pos":{"x":5.996028182886072,"y":1.4412204667847832},"rotation":1.0424777960769376},{"isSmallRhombus":true,"pos":{"x":7.4155649993103845,"y":0.17514415961928176},"rotation":-0.2141592653589809},{"isSmallRhombus":true,"pos":{"x":10.296858817256354,"y":3.9514455026552717},"rotation":1.042477796076935},{"isSmallRhombus":true,"pos":{"x":11.062307938896978,"y":5.692744337126108},"rotation":2.2991148575128513,"alt":true},{"isSmallRhombus":true,"pos":{"x":12.954918329153958,"y":5.502849894234152},"rotation":3.5557519189487716,"alt":true},{"isSmallRhombus":false,"pos":{"x":10.296858817256354,"y":3.9514455026552726},"rotation":0.4141592653589745},{"isSmallRhombus":false,"pos":{"x":11.062307938896977,"y":5.692744337126109},"rotation":1.6707963267948913,"alt":true},{"isSmallRhombus":false,"pos":{"x":12.95491832915395,"y":5.50284989423417},"rotation":2.9274333882308077,"alt":true},{"isSmallRhombus":false,"pos":{"x":13.359166756153341,"y":3.644189839781389},"rotation":4.1840704496667245,"alt":true},{"isSmallRhombus":false,"pos":{"x":11.716395633680673,"y":2.6853691954897783},"rotation":5.440707511102641,"alt":true},{"isSmallRhombus":true,"pos":{"x":13.35916675615334,"y":3.6441898397813883},"rotation":-1.4707963267949022,"alt":true},{"isSmallRhombus":true,"pos":{"x":11.716395633680673,"y":2.685369195489778},"rotation":-0.21415926535898633,"alt":true},{"isSmallRhombus":true,"pos":{"x":3.0450816708460477,"y":6.497150105765833},"rotation":6.069026041820603},{"isSmallRhombus":true,"pos":{"x":4.68785279331872,"y":7.455970750057438},"rotation":4.812388980384686,"alt":true},{"isSmallRhombus":false,"pos":{"x":4.68785279331872,"y":7.455970750057438},"rotation":4.184070449666727},{"isSmallRhombus":true,"pos":{"x":6.941663878216937,"y":10.866035196089113},"rotation":1.0424777960769354,"alt":true},{"isSmallRhombus":false,"pos":{"x":8.36120069464125,"y":9.599958888923616},"rotation":-0.8424777960769416},{"isSmallRhombus":true,"pos":{"x":12.30083063437028,"y":8.510225035870485},"rotation":2.299114857512855,"alt":true},{"isSmallRhombus":true,"pos":{"x":11.535381512729648,"y":6.768926201399653},"rotation":1.0424777960769376,"alt":true},{"isSmallRhombus":false,"pos":{"x":11.535381512729648,"y":6.768926201399653},"rotation":6.697344572538565},{"isSmallRhombus":true,"pos":{"x":9.823785243423693,"y":2.875263638381719},"rotation":2.299114857512852},{"isSmallRhombus":true,"pos":{"x":11.716395633680667,"y":2.685369195489775},"rotation":3.5557519189487694,"alt":true},{"isSmallRhombus":false,"pos":{"x":9.823785243423695,"y":2.875263638381719},"rotation":1.6707963267948938},{"isSmallRhombus":true,"pos":{"x":5.591779755886693,"y":3.2998805212375664},"rotation":3.555751918948772,"alt":true},{"isSmallRhombus":true,"pos":{"x":5.996028182886072,"y":1.4412204667847832},"rotation":4.812388980384689,"alt":true},{"isSmallRhombus":false,"pos":{"x":5.591779755886693,"y":3.299880521237566},"rotation":-3.3557519189487732}]}');

		this.placeableTiles = [];
		tiles.forEach((i) => {
			this.placeableTiles.push(new PlaceableTile(i));
		});
		this.tileClickEventSetup();
		this.mouseMoveEventSetup();
		this.rightClickEventSetup();
		this.selectedTile = false;
		this.Draw();
	}

	tileClickEventSetup() {
		var vthis = this;
		$("#canvas").on("click", function (e) {
			var pos = new Vec2(e.pageX, e.pageY);
			var canvasLoc = new Vec2($(this).offset().left, $(this).offset().top);
			var mousePos = pos.sub(canvasLoc); //px scale
			if (!vthis.selectedTile) {
				//choose which tile to add to
				vthis.placeableTiles.forEach((i) => {
					if (i.isClickInsideThisTile(mousePos)) {
						console.log("tile chosen");
						vthis.selectedTile = i;
						i.mouseMoved(mousePos);
					}
				});
			} else {
				//confirm placement.
				console.log("tile Placing");
				var placeableTile = vthis.selectedTile.clicked(mousePos);
			
				vthis.placeableTiles.push(placeableTile);
				vthis.selectedTile = false;
					vthis.Draw();
			}
		});
	}

	mouseMoveEventSetup() {
		var vthis = this;
		$("#canvas").mousemove(function (e) {
			//debugger;
			if (vthis.selectedTile) {
				vthis.Draw();
				//choosing placement.
				var pos = new Vec2(e.pageX, e.pageY);
				var canvasLoc = new Vec2($(this).offset().left, $(this).offset().top);
				var mousePos = pos.sub(canvasLoc); //px mousepos
				vthis.selectedTile.mouseMoved(mousePos);
			}
		});
	}

	rightClickEventSetup() {
		var vthis = this;
		$("#canvas").contextmenu(function (e) {
			e.preventDefault();
			if(!vthis.selectedTile){
				var pos = new Vec2(e.pageX, e.pageY);
				var canvasLoc = new Vec2($(this).offset().left, $(this).offset().top);
				var mousePos = pos.sub(canvasLoc); //px scale

				//choose which tile to add to
				vthis.placeableTiles.forEach((i) => {
					if (i.isClickInsideThisTile(mousePos)) {
						console.log("tile chosen");
						vthis.placeableTiles = vthis.placeableTiles.filter((k) => k != i);
					}
				});
			}else{
				vthis.selectedTile = false;	
			}
			vthis.Draw();
		});
	}

	getInitialTiles(inputJson) {
		var tiles = [];
		if(inputJson){
			var jsonInputData = JSON.parse(inputJson);
			jsonInputData.tiles.forEach((i)=>{
				if(i.isSmallRhombus){
					tiles.push(new SmallRhombus(new Vec2(i.pos),i.rotation,i.alt));
				}else{
					tiles.push(new BigRhombus(new Vec2(i.pos),i.rotation,i.alt));
				}
			})
		}
		/*
		//Edge Possibilities from edge0 (top when rot=0) going clockwise.
		//Small
		//Smalls
		tiles.push(new SmallRhombus(new Vec2(1, 1)));
		var sInd = tiles.length - 1;

		tiles.push(
			new SmallRhombus(tiles[sInd].getSidePlacementAndRotation(0), 5 * seg, true)
		);
		tiles.push(
			new SmallRhombus(tiles[sInd].getSidePlacementAndRotation(2), 8 * seg)
		);
		//NA Can do it but but cant be contunued
		//NA Can do it but but cant be contunued

		//Big
		//Bigs

		tiles.push(new BigRhombus(new Vec2(7, 1)));
		var sInd = tiles.length - 1;

		tiles.push(
			new BigRhombus(tiles[sInd].getSidePlacementAndRotation(0), 0 * seg)
		);
		tiles.push(
			new BigRhombus(tiles[sInd].getSidePlacementAndRotation(2), 1 * seg, true)
		);
		tiles.push(
			new BigRhombus(tiles[sInd].getSidePlacementAndRotation(2), 5 * seg, true)
		);
		tiles.push(
			new BigRhombus(tiles[sInd].getSidePlacementAndRotation(0), 6 * seg)
		);

		//Small
		//Bigs

		tiles.push(new SmallRhombus(new Vec2(4, 1)));
		var sInd = tiles.length - 1;

		tiles.push(
			new BigRhombus(tiles[sInd].getSidePlacementAndRotation(1), 9 * seg, true)
		);
		tiles.push(
			new BigRhombus(tiles[sInd].getSidePlacementAndRotation(1), 5 * seg, true)
		);
		tiles.push(
			new BigRhombus(tiles[sInd].getSidePlacementAndRotation(2), 0 * seg)
		);
		tiles.push(
			new BigRhombus(tiles[sInd].getSidePlacementAndRotation(0), 7 * seg)
		);

		//Big
		//Smalls

		tiles.push(new BigRhombus(new Vec2(10, 1)));
		var sInd = tiles.length - 1;

		tiles.push(
			new SmallRhombus(tiles[sInd].getSidePlacementAndRotation(0), 0 * seg)
		);
		tiles.push(
			new SmallRhombus(tiles[sInd].getSidePlacementAndRotation(1), 5 * seg, true)
		);
		tiles.push(
			new SmallRhombus(tiles[sInd].getSidePlacementAndRotation(3), 6 * seg)
		);
		tiles.push(
			new SmallRhombus(tiles[sInd].getSidePlacementAndRotation(0), 2 * seg, true)
		);
		
		
		tiles.push(
			new SmallRhombus(new Vec2(4,6), 0)
		);
		*/
		//tiles.push(new BigRhombus(new Vec2(8, 6), Math.PI / 2 + 0.1, true));
		return tiles;
		//Rules ive noticed

		//Only have 2 smalls next to eachother or alone on a vert.

		//Vertice possibilities

		//B 0
		//B 1
		//B 2
		//B 3
		//S 1
		//S 2
		//S 3
		//S 4

		/*
		var tiles=[];
		tiles[0] = new BigRhombus(new Vec2(5,5));

		*/
	}
	
	exportTiles(){
		var toJsonify = {tiles:[]};
		
		this.placeableTiles.forEach((i) => toJsonify.tiles.push({isSmallRhombus:(i.tile instanceof SmallRhombus),pos:i.tile.pos,rotation:i.tile.rotation,alt:i.tile.alt}));
		return JSON.stringify(toJsonify);
	}

	Draw() {
		ctx.fillStyle = style.colours.background;
		ctx.fillRect(0, 0, 1000, 1000);
		this.placeableTiles.forEach((i) => i.tile.Draw());
	}

	AddRandomTile() {
		var addTo =this.placeableTiles[Math.floor(Math.random() * (this.placeableTiles.length - 1))];
		var pretendMousePos = new Vec2(Math.random()*canvas.width,Math.random()*canvas.height);
		var newTile = addTo.getNewTile(pretendMousePos);
		this.placeableTiles.push(newTile);
	}
	
	ClearAnimation(){
		//PLS IGNORE SPAGETTI CODE
		if(!this.stopClearAnimation){
			if(this.placeableTiles.length>1){
				this.placeableTiles.pop();
				this.Draw();
				this.clearAnimationInterval = setTimeout(()=>{this.ClearAnimation();},20);
			}else{
				clearTimeout(this.clearAnimationInterval);
			}
		}else{
			clearTimeout(this.clearAnimationInterval);	
		}
	}
}

var world = new TilePlacementWorld();

setTimeout(()=>{world.ClearAnimation()},3000);
$("canvas").on("click",function(){
	world.stopClearAnimation = true;
})

for(var i=0;i<1000;i++){
	world.AddRandomTile();
	world.Draw();
}
              
            
!
999px

Console