"use strict" class Cube{ constructor(){ this.bricks = []; for (let x = -1; x < 2; x++){ for (let y = -1; y < 2; y++){ for (let z = -1; z < 2; z++){ this.bricks.push(new Brick(x, y, z)); } } } } show(){ push(); if (this.rotation){ for (let i = -1; i < 2; i++){ if (i === this.rotation.index){ push(); eval("rotate" + this.rotation.axis.toUpperCase() + "(this.rotation.angle);"); for (let b of this.getLayer(this.rotation.axis, i)) b.show(); pop(); } else { for (let b of this.getLayer(this.rotation.axis, i)) b.show(); } } } else { for (let b of this.bricks) b.show(); } pop(); } update(){ if (this.rotation) this.rotation.update(); } updatePos(){ let m = new Matrix(); m.rotate(this.rotation.angle); let o = other(this.rotation.axis), bricks = [], fields = [], resultBricks = [], iBricks = 0, pBricks, resultFields = [], iFields = 0, pFields; for (let b of this.getLayer(this.rotation.axis, this.rotation.index)){ eval("bricks.push({x: b.pos." + o[0] + ", y: b.pos." + o[1] + "});"); for (let f of b.fields){ for (let p of f.points){ eval("fields.push({x: p." + o[0] + ", y: p." + o[1] + "});"); } } } // transform points while(pBricks = bricks[iBricks++]) resultBricks.push(m.applyToPoint(pBricks)); while(pFields = fields[iFields++]) resultFields.push(m.applyToPoint(pFields)); iBricks = 0, iFields = 0; for (let b of this.getLayer(this.rotation.axis, this.rotation.index)){ let p = resultBricks[iBricks]; eval("b.pos." + o[0] + " = p.x;"); eval("b.pos." + o[1] + " = p.y;"); iBricks++; for (let f of b.fields){ for (let pt of f.points){ let pf = resultFields[iFields]; eval("pt." + o[0] + " = pf.x;"); eval("pt." + o[1] + " = pf.y;"); iFields++; } } } } getLayer(axis, index){ let bricks = []; for (let b of this.bricks){ if (eval("b.pos." + axis) === index){ bricks.push(b); } } return bricks; } rotate(axis, index, dir, v){ if (!this.rotation) this.rotation = new Rotation(axis, index, dir, v, () => { cube.updatePos(); cube.rotation = null; }); } scramble(count){ let cube = this; if (count === 0) this.rotation = null; else this.rotation = new Rotation(random(["x", "y", "z"]), random([-1, 1]), random([-1, 1]), PI / 32, () => { cube.updatePos(); cube.scramble(count - 1); }); } } function other(axis){ switch(axis){ case "x": return ["y", "z"]; case "y": return ["z", "x"]; case "z": return ["x", "y"]; } } class Brick{ constructor(x, y, z){ this.pos = createVector(x, y, z); this.size = 85; this.createFields(); } show(){ this.pos.mult(this.size); push(); translate(this.pos.x, this.pos.y, this.pos.z); for (let f of this.fields) f.show(); pop(); this.pos.div(this.size); } createFields(){ this.fields = []; let sides = [[],[],[],[],[],[]]; let c = [ "#F00", "#F90", "#FFF", "#FF0", "#00F", "#0F0", "#000" ]; for (let x = -1; x < 2; x++){ for (let y = -1; y < 2; y++){ for (let z = -1; z < 2; z++){ let arr = []; if (x > 0 && y && z){ if (this.pos.x > 0) arr.push({i: 0, colored: true}); else arr.push({i: 0}); } if (x < 0 && y && z){ if (this.pos.x < 0) arr.push({i: 1, colored: true}); else arr.push({i: 1}); } if (y > 0 && x && z){ if (this.pos.y > 0) arr.push({i: 2, colored: true}); else arr.push({i: 2}); } if (y < 0 && x && z){ if (this.pos.y < 0) arr.push({i: 3, colored: true}); else arr.push({i: 3}); } if (z > 0 && x && y){ if (this.pos.z > 0) arr.push({i: 4, colored: true}); else arr.push({i: 4}); } if (z < 0 && x && y){ if (this.pos.z < 0) arr.push({i: 5, colored: true}); else arr.push({i: 5}); } for (let a of arr){ sides[a.i].push({p: createVector(x, y, z), colored: a.colored}); } } } } for (let s of sides){ let points = []; for (let a of s) points.push(a.p); let color = s[0].colored ? c[sides.indexOf(s)] : c[6]; this.fields.push(new Field(points, color, this.size)); } } hasColor(c){ for (let f of this.fields){ if (f.colorEquals(c)){ return true; } } return false; } get isEdge(){ return this.coloredCount === 2; } get isCorner(){ return this.coloredCount === 3; } get isFlat(){ return this.coloredCount === 1; } get coloredCount(){ let count = 0; for (let f of this.fields){ if (!f.colorEquals("#000")) { count++; } } return count; } } class Field{ constructor(points, c, size){ //relative to brick this.color = { light: color(c), dark: dark(color(c), 0.4) }; this.bSize = size; this.size = size * 0.95; this.points = points; } show(){ for (let p of this.points) p.mult(this.size / 2); let p = this.points; if (this.dark) fill(this.color.dark); else fill(this.color.light); strokeWeight(8); stroke(0); beginShape(); vertex(p[0].x, p[0].y, p[0].z); vertex(p[2].x, p[2].y, p[2].z); vertex(p[3].x, p[3].y, p[3].z); vertex(p[1].x, p[1].y, p[1].z); endShape(CLOSE); for (let p of this.points) p.div(this.size / 2); } colorEquals(c){ let equal = true; for (let i = 0; i < 3; i++){ if (this.color.light.levels[i] !== color(c).levels[i]){ equal = false; } } return equal; } } function dark(c, val){ return color(red(c) * val, green(c) * val, blue(c) * val); }