// bluepixle.js // This library is to help make using canvas tags easier function deg2rad (deg) { return ((Math.PI*2)/360)*deg; } function rad2deg (rad) { return (1/deg2rad(1))*rad; } function drift(object) { if (object instanceof container) { object.direction = "number" == typeof object.direction ? object.direction : Math.floor(Math.random()*360); object.speed = "number" == typeof object.speed ? object.speed : 1; object.move(object.direction,object.speed); } else { return new TypeError("must provide a container object"); } } function getAngle(ax,ay,bx,by) { var deltaX = bx - ax; var deltaY = by - ay; return rad2deg(Math.atan2(deltaY,deltaX)); } function reflect(inAngle,surfaceAngle) { var normal = surfaceAngle+90; var theta1 = normal - inAngle; var theta2 = normal + theta1; return theta2; } function bounce(object, surface, impactTime) { object.direction = reflect(object.direction,surface.direction-90); object.speed = object.speed; } function valueBetween (value,low,high) { var Tlow = low <= high ? low : high; var Thigh = high >= low ? high : low; if ( (value >= Tlow) & (value <= Thigh) ) { return true; } else { return false; } } function pointInside (x,y,top,right,bottom,left) { if ( valueBetween(x,left,right) & valueBetween(y,top,bottom) ) { return true; } else { return false; } } function collide(objectA,objectB,callBackFunction) { var ASpeed = Math.abs(objectA.speed); var BSpeed = Math.abs(objectB.speed); var simAX = ASpeed <= BSpeed ? objectA.x : objectB.x; var simAY = ASpeed <= BSpeed ? objectA.y : objectB.y; var simBX = ASpeed <= BSpeed ? objectB.x : objectA.x; var simBY = ASpeed <= BSpeed ? objectB.y : objectA.y; var simADir = ASpeed <= BSpeed ? objectA.direction : objectB.direction; var simBDir = ASpeed <= BSpeed ? objectB.direction : objectA.direction; var simASpeed = ASpeed <= BSpeed ? objectA.speed : objectB.speed; var simBSpeed = ASpeed <= BSpeed ? objectB.speed : objectA.speed; var simAheight = ASpeed <= BSpeed ? objectA.sizeX : objectB.sizeX; var simBheight = ASpeed <= BSpeed ? objectB.sizeX : objectA.sizeX; var simAwidth = ASpeed <= BSpeed ? objectA.sizeY : objectB.sizeY; var simBwidth = ASpeed <= BSpeed ? objectB.sizeY : objectA.sizeY; var thetaA = deg2rad(simADir); var deltaAX = Math.cos(thetaA)*simASpeed; var deltaAY = Math.sin(thetaA)*simASpeed; var thetaB = deg2rad(simBDir); var deltaBX = Math.cos(thetaB)*simBSpeed; var deltaBY = Math.sin(thetaB)*simBSpeed; var fastest = Math.max(Math.abs(objectA.speed),Math.abs(objectB.speed)); for (var t = 0; t <= fastest; t++) { var tFraction = t/(fastest+1); var Atop = simAY+(tFraction * deltaAY); var Abottom = simAY+(tFraction * deltaAY)+simAheight; var Aleft = simAX+(tFraction * deltaAX); var Aright = simAX+(tFraction * deltaAX)+simAwidth; var Btop = simBY+(tFraction * deltaBY); var Bbottom = simBY+(tFraction * deltaBY)+simBheight; var Bleft = simBX+(tFraction * deltaBX); var Bright = simBX+(tFraction * deltaBX)+simBwidth; if ( pointInside(Aleft,Atop,Btop,Bright,Bbottom,Bleft) | pointInside(Aright,Atop,Btop,Bright,Bbottom,Bleft) | pointInside(Aleft,Abottom,Btop,Bright,Bbottom,Bleft) | pointInside(Aright,Abottom,Btop,Bright,Bbottom,Bleft) | pointInside(Bleft,Btop,Atop,Aright,Abottom,Aleft) | pointInside(Bright,Btop,Atop,Aright,Abottom,Aleft) | pointInside(Bleft,Bbottom,Atop,Aright,Abottom,Aleft) | pointInside(Bright,Bbottom,Atop,Aright,Abottom,Aleft) ){ return callBackFunction(objectA,objectB,t); } } return false; } function chase (source,target,max,minDis,acc) { acc /= 100; var sourceCenterX = source.x + (source.sizeX/2); var sourceCenterY = source.y + (source.sizeY/2); var targetCenterX = target.x + (target.sizeX/2); var targetCenterY = target.y + (target.sizeY/2); var deltaX = targetCenterX - sourceCenterX; var deltaY = targetCenterY - sourceCenterY; var theta = Math.atan2(deltaY,deltaX); var dis = Math.sqrt(Math.pow(Math.abs(deltaX),2)+Math.pow(Math.abs(deltaY),2)); var dir = theta; if (dis > minDis) { dis = dis <= max ? dis : max; source.direction = rad2deg(dir); source.speed = (dis-minDis)*acc; source.move(source.direction,source.speed); } } function rect (ctx,xOrigin,yOrigin,width,height,fill,stroke,outlineWidth) { // Save current defaults var local = {}; local.fillStyle = "string" == typeof ctx.fillStyle ? ctx.fillStyle : undefined; local.strokeStyle = "string" == ctx.strokeStyle ? ctx.strokeStyle : undefined; local.lineWidth = "number" == ctx.lineWidth ? ctx.lineWidth : undefined; // Check inputs ctx.fillStyle = "string" == typeof fill ? fill : local.fillStyle; ctx.strokeStyle = "string" == typeof stroke ? stroke : "rgba(0,0,0,0)"; ctx.lineWidth = "number" == typeof outlineWidth ? outlineWidth : 1; ctx.fillRect(xOrigin,yOrigin,width,height); ctx.strokeRect(xOrigin,yOrigin,width,height); // restore context defaults ctx.fillStyle = local.fillStyle; ctx.strokeStyle = local.strokeStyle; ctx.lineWidth = local.lineWidth; } function elip (ctx,xOrigin,yOrigin,width,height,fill,stroke,outlineWidth) { // Save current defaults var local = {}; local.fillStyle = ctx.fillStyle; local.strokeStyle = ctx.strokeStyle; local.lineWidth = ctx.lineWidth; // Check inputs ctx.fillStyle = "string" == typeof fill ? fill : local.fillStyle; ctx.strokeStyle = "string" == typeof stroke ? stroke : "rgba(0,0,0,0)"; ctx.lineWidth = "number" == typeof outlineWidth ? outlineWidth : 1; ctx.beginPath(); ctx.ellipse(xOrigin,yOrigin,width,height,0,0,360); ctx.stroke(); ctx.fill(); // restore context defaults ctx.fillStyle = local.fillStyle; ctx.strokeStyle = local.strokeStyle; ctx.lineWidth = local.lineWidth; } function drawPixel (ctx, x, y, r, g, b, a) { // Always validate your input if (false == ctx instanceof CanvasRenderingContext2D) { return new TypeError("ctx must be a 2D rendering context for canvas."); } x = x <= ctx.canvas.width ? x : ctx.canvas.width; y = y <= ctx.canvas.height ? y : ctx.canvas.height; r = r <= 255 ? r : 255; r = r >= 0 ? r : 0; g = g <= 255 ? g : 255; g = g >= 0 ? g : 0; b = b <= 255 ? b : 255; b = b >= 0 ? b : 0; a = a <= 255 ? a : 255; a = a >= 0 ? a : 0; // Do the thing ctx.putImageData(new ImageData(new Uint8ClampedArray([r,g,b,a]),1,1),x,y); } class matrix { constructor(m,n,matrixArray) { if (matrixArray.length != m*n) { return new RangeError("the size of the array does not match the size of the matrix!"); } else { this.array = []; for (var row = 0; row < m; row++) { this.array[row] = []; for (var col = 0; col < n; col++) { this.array[row][col] = matrixArray[n*row+col]; } } this.m = m; this.n = n; } } log() { var string = ""; for (var row = 0; row < this.m; row++) { string += "["; for( var col = 0; col < this.n; col++) { string += this.array[row][col]; string += ","; } string += "]"; console.log(string); string = ""; } } add(matrixb) { if ( matrixb.m == this.m && matrixb.n == this.n ) { matrixb.array.forEach( function(row,rowIndex) { row.forEach( function(col,colIndex) { this.array[rowIndex][colIndex] += col; }); }); } else { throw new RangeError("cannot add matricies of different dimentions."); } } scalarMult(scalar) { var data = this.array; data.forEach( function(row,rI) { row.forEach( function(col,cI) { data[rI][cI] *= scalar; }); }); } transpose() { var tArray = []; for (var row = 0; row < this.n; row++) { tArray[row] = []; } for (var row = 0; row < this.m; row++) { for (var col = 0; col < this.n; col++) { tArray[col][row] = this.array[row][col]; } } var rawArray = []; tArray.forEach(function (row,rI) { row.forEach( function(col,cI) { rawArray.push(tArray[rI][cI]); }); }); return new matrix(this.n,this.m,rawArray); } multiply(matrixb) { if (matrixb.m == this.n) { var m = this.m; var n = this.n; var p = matrixb.n; var A = this; var B = matrixb; var newMatrix = []; for (var i = 0; i < m; i++) { newMatrix[i] = []; for (var j = 0; j < p; j++) { var sum = 0; for (var k = 0; k < n; k++) { sum += A.array[i][k] * B.array[k][j]; } newMatrix[i][j] = sum; } } var rawArray = []; newMatrix.forEach(function (row,rI) { row.forEach( function (col,cI) { rawArray.push(newMatrix[rI][cI]); }); }); return new matrix(m,p,rawArray); } else { return undefined; } } } class point { constructor(x,y) { this.x = "number" == typeof x ? x : 0; this.y = "number" == typeof y ? y : 0; } getCoords() { return [this.x,this.y]; } setCoords(x,y) { this.x = "number" == typeof x ? x : 0; this.y = "number" == typeof y ? y : 0; } setX(x) { this.x = "number" == typeof x ? x : 0; } setY(y) { this.y = "number" == typeof y ? y : 0; } } class vector { constructor(direction,magnitude) { this.direction = "number" == typeof direction ? 0 <= direction ? 360 >= direction ? direction : 360 : 0 : 0; this.magnitude = "number" == typeof magnitude ? magnitude : 0; } getXYcomponents() { var theta = deg2rad(this.direction); var deltaX = Math.cos(theta)*this.magnitude; var deltaY = Math.sin(theta)*this.magnitude; return {"dx":deltaX,"dy":deltaY}; } adjustMag(value) { this.magnitude += "number" == typeof value ? value : 0; } setMag(value) { this.magnitude = "number" == typeof value ? value : this.magnitude; } adjustDir(value) { value = 0 <= value ? 360 >= value ? value: 360 : 0; this.direction += value; } setDir(value) { this.direction = 0 <= value ? 360 >= value ? value: 360 : 0; } executeVector(x,y) { var components = this.getXYcomponents(); return new point(x+components.dx,y+components.dy); } crossProduct(vectorB) { if (vectorB instanceof vector) { var V1 = this.executeVector(0,0); var V2 = vectorB.executeVector(0,0); return (Math.sin(V1.x) * Math.cos(V2.y)) - (Math.cos(V1.y) * Math.sin(V2.x)); } else { throw new TypeError("object is not a vector"); } } } class lineSeg { constructor(x1,y1,x2,y2) { this.pointA = new point(x1,y1); this.pointB = new point(x2,y2); this.center = new point((x1+x2)/2,(y1+y2)/2); this.length = Math.sqrt( Math.pow(Math.abs(x2-x1),2) +Math.pow(Math.abs(y2-y1),2) ); this.angle = getAngle(x1,y1,x2,y2); } adjustA(length) { length = "number" == typeof length ? length : 0; var vecC2A = new vector(getAngle( this.center.x, this.center.y, this.pointA.x, this.pointA.y), this.length/2); vecC2A.adjustMag(length); this.pointA = vecC2A.executeVector(this.center.x,this.center.y); this.center.setCoords( (this.pointA.x+this.pointB.x)/2, (this.pointA.y+this.pointB.y)/2); this.length += length; } adjustB(length) { length = "number" == typeof length ? length : 0; var vecC2B = new vector( getAngle( this.center.x, this.center.y, this.pointB.x, this.pointB.y), this.length/2); vecC2B.adjustMag(length); this.pointB = vecC2B.executeVector(this.center.x,this.center.y); this.center.setCoords( (this.pointA.x+this.pointB.x)/2, (this.pointA.y+this.pointB.y)/2); this.length += length; } adjustCenter(length) { adjustA(length/2); adjustB(length/2); } rotateA(deg) { var abVec = new vector( getAngle( this.pointA.x, this.pointA.y, this.pointB.x, this.pointB.y), this.length); abVec.adjustDir(deg); this.pointB = abVec.executeVector(this.pointA.x,this.pointA.y); this.center.setCoords( (this.pointA.x+this.pointB.x)/2, (this.pointA.y+this.pointB.y)/2); } rotateB(deg) { var baVec = new vector( getAngle( this.pointB.x, this.pointB.y, this.pointA.x, this.pointA.y), this.length); baVec.adjustDir(deg); this.pointA = baVec.executeVector(this.pointB.x,this.pointB.y); this.center.setCoords( (this.pointA.x+this.pointB.x)/2, (this.pointA.y+this.pointB.y)/2); } rotateCenter(deg) { var vecC2A = new vector( getAngle( this.center.x, this.center.y, this.pointA.x, this.pointA.y), this.length/2); var vecC2B = new vector( getAngle( this.center.x, this.center.y, this.pointB.x, this.pointB.y), this.length/2); vecC2A.adjust(deg); vecC2B.adjust(deg); this.pointA = vecC2A.executeVector(this.center.x,this.center.y); this.pointB = vecC2B.executeVector(this.center.x,this.center.y); } getPerp() { this.rotateCenter(90); var newline = new lineSeg( this.pointA.x, this.pointA.y, this.pointB.x, this.pointB.y); this.rotateCenter(-90); return newline; } slope() { var deltaX = this.pointB.x - this.pointA.x; var deltaY = this.pointB.y - this.pointA.y; return deltaY / deltaX; } xIntercept() { return -this.yIntercept()/this.slope(); } yIntercept() { var slope = this.slope(); if (Math.abs(slope) != Infinity) { return -(slope*this.pointA.x)+this.pointA.y; } else { return this.pointA.x; } } intersect(line) { if (line instanceof lineSeg) { var x1 = this.pointA.x; var x2 = this.pointB.x; var x3 = line.pointA.x; var x4 = line.pointB.x; var y1 = this.pointA.y; var y2 = this.pointB.y; var y3 = line.pointA.y; var y4 = line.pointB.y; var deltaAX = x1 - x2; var deltaBX = x3 - x4; var deltaAY = y1 - y2; var deltaBY = y3 - y4; // I don't know wtf to call these var ground = deltaAX*deltaBY-deltaAY*deltaBX; var Across = x1*y2 - y1*x2; var Bcross = x3*y4 - y3*x4; var x = (Across*deltaBX-deltaAX*Bcross)/ground; var y = (Across*deltaBY-deltaAY*Bcross)/ground; if ( valueBetween(x,this.pointA.x,this.pointB.x) && valueBetween(x,line.pointA.x,line.pointB.x) && valueBetween(y,this.pointA.y,this.pointB.y) && valueBetween(y,line.pointA.y,line.pointB.y)) { return new point(x,y); } else { return false; } } else { throw new TypeError("must check against line"); } } } class shape { constructor(type,x,y,sizeX,sizeY,fColor,bColor,bStyle,extraObject) { this.type = "string" == typeof type ? type : "rect"; this.x = "number" == typeof x ? x : 0; this.y = "number" == typeof y ? y : 0; this.sizeX = "number" == typeof sizeX ? sizeX : 1; this.sizeY = "number" == typeof sizeY ? sizeY : 1; this.fColor = "string" == typeof fColor ? fColor : undefined; this.bColor = "string" == typeof bColor ? bColor : undefined; this.bStyle = "number" == typeof bStyle ? bStyle : undefined; this.extraObject = "object" == typeof extraObject ? extraObject : {}; if ("rect" == this.type) { this.render = function (ctx,xPos,yPos) { rect( ctx, this.x+xPos, this.y+yPos, this.sizeX, this.sizeY, this.fColor, this.bColor, this.bStyle); } } else if ("elipse" == this.type) { this.render = function (ctx,xPos,yPos) { var centerX = this.x+xPos+(this.sizeX/2); var centerY = this.y+yPos+(this.sizeY/2); elip( ctx, centerX, centerY, (this.sizeX/2), (this.sizeY/2), this.fColor, this.bColor, this.bStyle); } } else if ("image" == this.type) { this.imageData = this.extraObject.imageData instanceof ImageData ? this.extraObject.imageData : new ImageData( new Uint8ClampedArray([ 255,0,0,255, 0,0,0,0, 255,0,0,255, 0,0,0,0, 255,0,0,255, 0,0,0,0, 255,0,0,255, 0,0,0,0, 255,0,0,255 ]),3,3); this.image = createImageBitmap(this.imageData); this.render = function (ctx,xPos,yPos) { var x = this.x; var y = this.y; var sizeX = this.sizeX; var sizeY = this.sizeY; var contextBlur = ctx.imageSmoothingEnabled; this.image.then(function (image) { ctx.imageSmoothingEnabled = false; ctx.drawImage(image,x+xPos,y+yPos,sizeX,sizeY); ctx.imageSmoothingEnabled = contextBlur; }); } } else { console.log("no valid type defined for shape, defalting to 'rect'"); this.render = function (ctx,xPos,yPos) { rect( ctx, this.x+xPos, this.y+yPos, this.sizeX, this.sizeY, this.fColor, this.bColor, this.bStyle); } } } grow (amp) { this.sizeX+=amp; this.sizeY+=amp; } } class container { constructor(x,y,height,width,speed,direction) { this.x = x; this.y = y; this.sizeY = height; this.sizeX = width; this.speed = "number" == typeof speed ? speed : 0; this.direction = "number" == typeof direction ? direction : 0; // Vectors are the future this.netMovementVector = new vector( this.direction, this.speed); } setShape(myShape) { if (myShape instanceof shape) { this.shape = myShape; } else { throw new TypeError("addShape Method must be given a shape object"); } } nestContainer() { this.containerArray = []; this.addShape = function (newShape) { if (newShape instanceof shape) { var containerIndex = this.containerArray.push( new container(0,0,this.sizeY,this.sizeX)); this.containerArray[containerIndex-1].setShape(newShape); return this.containerArray[containerIndex-1]; } else { throw new TypeError("addShape Method must be given a shape object"); } }; if (this.shape instanceof shape) { this.addShape(me.shape); } this.shape = undefined; this.setShape = undefined; } move(direction,amplitude) { this.direction = direction; this.speed = amplitude; var theta = deg2rad(direction); this.x += Math.cos(theta) * amplitude; this.y += Math.sin(theta) * amplitude; } renderContainer(ctx,xPos,yPos) { var refX = this.x + ("number" == typeof xPos ? xPos : 0); var refY = this.y + ("number" == typeof yPos ? yPos : 0); if ("object" == typeof this.shape) { this.shape.render(ctx,refX,refY); } else if ("object" == typeof this.containerArray) { this.containerArray.forEach( function (container) { container.renderContainer(ctx,refX,refY); }); } else { console.log("Container is empty!!!"); } } grow(amp) { amp = "number"== typeof amp ? amp : 1; this.x -= amp; this.y -= amp; if ("object" == typeof this.shape) { this.shape.grow(amp); } else if ("object" == typeof this.containerArray) { this.containerArray.forEach( function (container) { container.grow(amp); }); } } } class frameBuffer { constructor(ctx, height, width) { this.ctx = ctx; this.height = height; this.width = width; this.containerArray = []; } addContainer(containerObject) { //might change this to auto create containers. if (containerObject instanceof container) { var index = this.containerArray.push(containerObject); return this.containerArray[ index - 1]; } else { throw new TypeError("addContainer Method must be given a container object"); } } pushFrame() { var ctx = this.ctx; ctx.clearRect(0,0,this.width,this.height); this.containerArray.forEach( function (container) { container.renderContainer(ctx,0,0); }); } } class starfield { constructor (buffer,boundWidth,boundHeight,centerX,centerY,starCount,starLife,starSpeed) { //Validate things this.centerX = centerX; this.centerY = centerY; this.starLife = starLife; this.starSpeed = starSpeed; //Math the things this.topBound = centerY - (boundHeight/2); this.bottomBound = centerY + (boundHeight/2); this.leftBound = centerX - (boundWidth/2); this.rightBound = centerX + (boundWidth/2); this.stars = []; this.container = buffer.addContainer( new container(this.centerX,this.centerY,0,0,0)); this.container.nestContainer(); for (var count = 1; count <= starCount; count++) { var star = this.container.addShape( new shape( "rect", ((Math.random()*boundWidth)-(boundWidth/2)), ((Math.random()*boundHeight)-(boundHeight/2)), 1,1, "#ffffff", "#ffffff", 1)); this.stars.push({ "container":star, "life":this.starLife, "speed":this.starSpeed}); } } forward() { var me = this; this.stars.forEach(function (star) { if ( (star.life > 0) & (star.container.shape.x > me.leftBound - me.centerX) & (star.container.shape.x < me.rightBound - me.centerX) & (star.container.shape.y > me.topBound - me.centerY) & (star.container.shape.y < me.bottomBound - me.centerY)) { var xOffset = star.container.shape.x; var yOffset = star.container.shape.y; star.life--; var c = 255*((me.starLife-star.life)/me.starLife)*4; star.container.grow(5/me.starLife); star.container.shape.fColor = "rgb("+c+","+c+","+c+")"; star.container.shape.bColor = "rgb("+c+","+c+","+c+")"; star.speed+=me.starSpeed; star.container.shape.x += star.speed*((xOffset/me.centerX)*0.5); star.container.shape.y += star.speed*((yOffset/me.centerY)*0.5); } else { star.container.x = 0; star.container.y = 0; star.life = me.starLife; star.speed = me.starSpeed; star.container.shape.sizeX = 1; star.container.shape.sizeY = 1; star.container.shape.fColor = "rgb(0,0,0)"; star.container.shape.bColor = "rgb(0,0,0)"; star.container.shape.x = (Math.random()*(me.rightBound-me.leftBound)) -(me.centerX); star.container.shape.y = (Math.random()*(me.bottomBound-me.topBound)) -(me.centerY); } }); } }