having tetris blocks fall in javascript - javascript

I was wondering how to go about making tetris pieces fall, I have followed a few tutorials and I have made the complete game, but I am a little stumped on how they actually got the pieces to fall, and how they made the 2d arrays into actual blocks, can someone guide me in the right direction here? I am just trying to learn the process better, this was all done in a canvas.
for example, here is the lpiece
function LPiece(){
//the pieces are represented in arrays shaped like the pieces, for example, here is an L.
//each state is a form the piece takes on, so each state after this.state1 is this.state1 rotated.
//each state is individual so we can call out which state to use depending on the option selected.
this.state1 = [ [1, 0],
[1, 0],
[1, 1]];
//and heres a different piece
this.state2 = [ [0, 0, 1],
[1, 1, 1]];
this.state3 = [
[1,1],
[0,1],
[0,1]];
this.state4 = [
[1, 1, 1],
[1, 0, 0]];
//and now we tie them all to one
this.states = [this.state1, this.state2, this.state3, this.state4];
//reference to the state the piece is currently in.
this.curState = 0;
//color of piece
this.color = 0;
//tracking pieces of grid of x and y coords, this is set at 4, -3 so it isn't initially visable.
this.gridx = 4;
this.gridy = -3;
}
piece.color = Math.floor(Math.random() *8);
I added comments to try to make me understand initially
and here is the image they used for each block, each block was one color
http://i.imgur.com/Mh5jMox.png
so how would he translate the array to be an actual block, and then get that to fall from the board, I have searched endlessly, I am just confused, like how he set the gridx and gridy to the x and y coordinates without ever saying this.y = gridy or something like that? does anyone have any suggestions on what to do here? thanks
here is how he drew the piece i guess, I still don't understand how he linked the x and y to the gridx and y of the piece without actually saying the x and y is grid x and y.
function drawPiece(p){
//connecting the y and x coords or pieces to draw using the arrays to pieces we defined earlier.
var drawX = p.gridx;
var drawY = p.gridy;
var state = p.curState;
//looping through to get a pieces currentstate, and drawing it to the board.
//rows, the p.state is deciding the length by the currentstate(the block width)
for(var r = 0, len = p.states[state].length; r < len; r++){
//columns the p.state is deciding the length by the currentstate(the block width)
for(var c = 0, len2 = p.states[state][r].length; c < len2; c++){
//detecting if there is a block to draw depending on size of value returned.
if(p.states[state][r][c] == 1 && drawY >= 0){
ctx.drawImage(blockImg, p.color * SIZE, 0, SIZE, SIZE, drawX * SIZE, drawY * SIZE, SIZE, SIZE);
}
drawX += 1;
}
//resetting the gridx
drawX = p.gridx;
//incrementing gridy to get the second layer of arrays from the block states.
drawY += 1;
}
}

He converts to canvas pixel units in ctx.drawImage here is a simplified version
var canvasX = drawX * SIZE;
var canvasY = drawY * SIZE;
ctx.drawImage(blockImg, p.color * SIZE, 0, SIZE, SIZE, canvasX, canvasY, SIZE, SIZE);
https://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D#drawImage()

Related

Improving visualizer look

I am not happy with how this code draws this music visualizer using canvas and getByteFrequencyData. https://share.getcloudapp.com/Kou7AJb1
It seems the bars are too large and I think its because the FFT (Fast Fourier Transform) array contains a wide spectrum of data but in my code I am generating n amount of bars based on the width of the canvas.
Then after having n bars I am mapping the FFT to the same index of the bar leaving out lots of useful information.
function convertRange(value: any, r1: any, r2: any) {
return ((value - r1[0]) * (r2[1] - r2[0])) / (r1[1] - r1[0]) + r2[0];
}
// line up and down
const drawVisualizer2 = ({ canvas, frameData, background }: any) => {
const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
// TODO: Improve?
const bars = Math.round(canvas.width) / 15 - 1;
const max_of_array = Math.max.apply(Math, frameData.fft);
for (let i = 0; i < bars; i++) {
const height = convertRange(frameData.fft[i], [0, max_of_array], [0, canvas.height / 2 - 20]);
const centerY = canvas.height / 2;
// draw the bar
ctx.strokeStyle = background ? background.colors[0] : "#ffffff";
ctx.lineWidth = 10;
ctx.lineCap = "round";
ctx.beginPath();
ctx.moveTo((i + 1) * 15, centerY);
ctx.lineTo((i + 1) * 15, centerY + height);
ctx.stroke();
ctx.beginPath();
ctx.moveTo((i + 1) * 15, centerY);
ctx.lineTo((i + 1) * 15, centerY - height);
ctx.stroke();
}
};
export default drawVisualizer2;
What I think needs to be done is average out the FFT based on the amount of bars in the loop. If that makes sense what is a practical approach code wise to achieve that?
I hope this makes sense, happy to clarify if needed.
I assume that frameData.fft is the Uint8Array containing the actual frequency data returned by the AnalyserNode.getByteFrequencyData() method.
Your assumption is right - the number of bars of course doesn't match the number of items stored in array and with a loop like
for (let i = 0; i < bars; i++) {
...
frameData.fft[i]
...
}
you're just using the first few values from zero up to the number of bars and ultimately skipping the entire rest of the array.
The fix is quite simple though:
Instead of grabbing values from the array in intervals of 1, the interval must be the number of elements in the array divided by the number of bars. This number is then multiplied by the variable i inside the for-loop and rounded as the division might result in a decimal number and the array's elements are at integer positions.
Here's an example:
let frameData = {
fft: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
};
let bars = 5;
let steps = frameData.fft.length / bars;
for (let i = 0; i < bars; i++) {
console.log(frameData.fft[Math.round(i * steps)]);
}

How to improve accuracy of a FeedForward Neural Network?

I want to draw StackOverflow's logo with this Neural Network:
The NN should ideally become [r, g, b] = f([x, y]). In other words, it should return RGB colors for a given pair of coordinates. The FFNN works pretty well for simple shapes like a circle or a box. For example after several thousands epochs a circle looks like this:
Try it yourself: https://codepen.io/adelriosantiago/pen/PoNGeLw
However since StackOverflow's logo is far more complex even after several thousands of iterations the FFNN's results are somewhat poor:
From left to right:
StackOverflow's logo at 256 colors.
With 15 hidden neurons: The left handle never appears.
50 hidden neurons: Pretty poor result in general.
0.03 as learning rate: Shows blue in the results (blue is not in the orignal image)
A time-decreasing learning rate: The left handle appears but other details are now lost.
Try it yourself: https://codepen.io/adelriosantiago/pen/xxVEjeJ
Some parameters of interest are synaptic.Architect.Perceptron definition and learningRate value.
How can I improve the accuracy of this NN?
Could you improve the snippet? If so, please explain what you did. If there is a better NN architecture to tackle this type of job could you please provide an example?
Additional info:
Artificial Neural Network library used: Synaptic.js
To run this example in your localhost: See repository
By adding another layer, you get better results :
let perceptron = new synaptic.Architect.Perceptron(2, 15, 10, 3)
There are small improvements that you can do to improve efficiency (marginally):
Here is my optimized code:
const width = 125
const height = 125
const outputCtx = document.getElementById("output").getContext("2d")
const iterationLabel = document.getElementById("iteration")
const stopAtIteration = 3000
let perceptron = new synaptic.Architect.Perceptron(2, 15, 10, 3)
let iteration = 0
let inputData = (() => {
const tempCtx = document.createElement("canvas").getContext("2d")
tempCtx.drawImage(document.getElementById("input"), 0, 0)
return tempCtx.getImageData(0, 0, width, height)
})()
const getRGB = (img, x, y) => {
var k = (height * y + x) * 4;
return [
img.data[k] / 255, // R
img.data[k + 1] / 255, // G
img.data[k + 2] / 255, // B
//img.data[(height * y + x) * 4 + 3], // Alpha not used
]
}
const paint = () => {
var imageData = outputCtx.getImageData(0, 0, width, height)
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
var rgb = perceptron.activate([x / width, y / height])
var k = (height * y + x) * 4;
imageData.data[k] = rgb[0] * 255
imageData.data[k + 1] = rgb[1] * 255
imageData.data[k + 2] = rgb[2] * 255
imageData.data[k + 3] = 255 // Alpha not used
}
}
outputCtx.putImageData(imageData, 0, 0)
setTimeout(train, 0)
}
const train = () => {
iterationLabel.innerHTML = ++iteration
if (iteration > stopAtIteration) return
let learningRate = 0.01 / (1 + 0.0005 * iteration) // Attempt with dynamic learning rate
//let learningRate = 0.01 // Attempt with non-dynamic learning rate
for (let x = 0; x < width; x += 1) {
for (let y = 0; y < height; y += 1) {
perceptron.activate([x / width, y / height])
perceptron.propagate(learningRate, getRGB(inputData, x, y))
}
}
paint()
}
const startTraining = (btn) => {
btn.disabled = true
train()
}
EDIT : I made another CodePen with even better results:
https://codepen.io/xurei/pen/KKzWLxg
It is likely to be over-fitted BTW.
The perceptron definition:
let perceptron = new synaptic.Architect.Perceptron(2, 8, 15, 7, 3)
Taking some insights from the lecture/slides of Bhiksha Raj (from slides 62 onwards), and summarizing as below:
Each node can be assumed like a linear classifier, and combination of several nodes in a single layer of neural networks can approximate any basic shapes. For example, a rectangle can be formed by 4 nodes for each lines, assuming each nodes contributes to one line, and the shape can be approximated by the final output layer.
Falling back to the summary of complex shapes such as circle, it may require infinite nodes in a layer. Or this would likely hold true for a single layer with two disjoint shapes (A non-overlapping triangle and rectangle). However, this can still be learnt using more than 1 hidden layers. Where, the 1st layer learns the basic shapes, followed by 2nd layer approximating their disjoint combinations.
Thus, you can assume that this logo is combination of disjoint rectangles (5 rectangles for orange and 3 rectangles for grey). We can use atleast 32 nodes in 1st hidden layer and few nodes in the 2nd hidden layer. However, we don't have control over what each node learns. Hence, a few more number of neurons than required neurons should be helpful.

Finding the closest indexed color value to the current color in javascript / p5.js

I have an array of "indexed" RGBA color values, and for any given image that I load, I want to be able to run through all the color values of the loaded pixels, and match them to the closest of my indexed color values. So, if the pixel in the image had a color of, say, RGBA(0,0,10,1), and the color RGBA(0,0,0,1) was my closest indexed value, it would adjust the loaded pixel to RGBA(0,0,0,1).
I know PHP has a function imagecolorclosest
int imagecolorclosest( $image, $red, $green, $blue )
Does javascript / p5.js / processing have anything similar? What's the easiest way to compare one color to another. Currently I can read the pixels of the image with this code (using P5.js):
let img;
function preload() {
img = loadImage('assets/00.jpg');
}
function setup() {
image(img, 0, 0, width, height);
let d = pixelDensity();
let fullImage = 4 * (width * d) * (height * d);
loadPixels();
for (let i = 0; i < fullImage; i+=4) {
let curR = pixels[i];
let curG = pixels[i]+1;
let curB = pixels[i+2];
let curA = pixels[i+3];
}
updatePixels();
}
Each color consists 3 color channels. Imagine the color as a point in a 3 dimensional space, where each color channel (red, green, blue) is associated to one dimension. You've to find the closest color (point) by the Euclidean distance. The color with the lowest distance is the "closest" color.
In p5.js you can use p5.Vector for vector arithmetic. The Euclidean distance between to points can be calculated by .dist(). So the distance between points respectively "colors" a and b can be expressed by:
let a = createVector(r1, g1, b1);
let b = createVector(r2, g2, b2);
let distance = a.dist(b);
Use the expression somehow like this:
colorTable = [[r0, g0, b0], [r1, g1, b1] ... ];
int closesetColor(r, g, b) {
let a = createVector(r, g, b);
let minDistance;
let minI;
for (let i=0; i < colorTable; ++i) {
let b = createVector(...colorTable[i]);
let distance = a.dist(b);
if (!minDistance || distance < minDistance) {
minI = i; minDistance = distance;
}
}
return minI;
}
function setup() {
image(img, 0, 0, width, height);
let d = pixelDensity();
let fullImage = 4 * (width * d) * (height * d);
loadPixels();
for (let i = 0; i < fullImage; i+=4) {
let closestI = closesetColor(pixels[i], pixels[i+1], pixels[i+2])
pixels[i] = colorTable[closestI][0];
pixels[i+1] = colorTable[closestI][1];
pixels[i+2] = colorTable[closestI][2];
}
updatePixels();
}
If I understand you correctly you want to keep the colors of an image within a certain limited pallet. If so, you should apply this function to each pixel of your image. It will give you the closest color value to a supplied pixel from a set of limited colors (indexedColors).
// example color pallet (no alpha)
indexedColors = [
[0, 10, 0],
[0, 50, 0]
];
// Takes pixel with no alpha value
function closestIndexedColor(color) {
var closest = {};
var dist;
for (var i = 0; i < indexedColors.length; i++) {
dist = Math.pow(indexedColors[i][0] - color[0], 2);
dist += Math.pow(indexedColors[i][1] - color[1], 2);
dist += Math.pow(indexedColors[i][2] - color[2], 2);
dist = Math.sqrt(dist);
if(!closest.dist || closest.dist > dist){
closest.dist = dist;
closest.color = indexedColors[i];
}
}
// returns closest match as RGB array without alpha
return closest.color;
}
// example usage
closestIndexedColor([0, 20, 0]); // returns [0, 10, 0]
It works the way that the PHP function you mentioned does. If you treat the color values as 3d coordinate points then the closet colors will be the ones with the smallest 3d "distance" between them. This 3d distance is calculated using the distance formula:

sampling an image a tile at a time using canvas, getImageData and a Web Worker

I am attempting to build a simple HTML5 canvas based image processor that takes an image and generates a tiled version of it with each tile being the average color of the underlying image area.
This is easy enough to do outside the context of a Web Worker but I'd like to use a worker so as not to block the ui processing thread. The Uint8ClampedArray form the data takes is giving me a headache with regards to how to process it tile by tile.
Below is a plunk demonstrating what I've done so far and how it's not working.
http://plnkr.co/edit/AiHmLM1lyJGztk8GHrso?p=preview
The relevant code is in worker.js
Here it is:
onmessage = function (e) {
var i,
j = 0,
k = 0,
data = e.data,
imageData = data.imageData,
tileWidth = Math.floor(data.tileWidth),
tileHeight = Math.floor(data.tileHeight),
width = imageData.width,
height = imageData.height,
tile = [],
len = imageData.data.length,
offset,
processedData = [],
tempData = [],
timesLooped = 0,
tileIncremented = 1;
function sampleTileData(tileData) {
var blockSize = 20, // only visit every x pixels
rgb = {r:0,g:0,b:0},
i = -4,
count = 0,
length = tileData.length;
while ((i += blockSize * 4) < length) {
if (tileData[i].r !== 0 && tileData[i].g !== 0 && tileData[i].b !== 0) {
++count;
rgb.r += tileData[i].r;
rgb.g += tileData[i].g;
rgb.b += tileData[i].b;
}
}
// ~~ used to floor values
rgb.r = ~~(rgb.r/count);
rgb.g = ~~(rgb.g/count);
rgb.b = ~~(rgb.b/count);
processedData.push(rgb);
}
top:
for (; j <= len; j += (width * 4) - (tileWidth * 4), timesLooped++) {
if (k === (tileWidth * 4) * tileHeight) {
k = 0;
offset = timesLooped - 1 < tileHeight ? 4 : 0;
j = ((tileWidth * 4) * tileIncremented) - offset;
timesLooped = 0;
tileIncremented++;
sampleTileData(tempData);
tempData = [];
//console.log('continue "top" loop for new tile');
continue top;
}
for (i = 0; i < tileWidth * 4; i++) {
k++;
tempData.push({r: imageData.data[j+i], g: imageData.data[j+i+1], b: imageData.data[j+i+2], a: imageData.data[j+i+3]});
}
//console.log('continue "top" loop for new row per tile');
}
postMessage(processedData);
};
I'm sure there's a better way of accomplishing what I'm trying to do starting at the labeled for loop. So any alternative methods or suggestions would be much appreciated.
Update:
I've taken a different approach to solving this:
http://jsfiddle.net/TunMn/425/
Close, but no.
I know what the problem is but I have no idea how to go about amending it. Again, any help would be much appreciated.
Approach 1: Manually calculating average per tile
Here is one approach you can try:
There is only need for reading, update can be done later using HW acceleration
Use async calls for every row (or tile if the image is very wide)
This gives an accurate result but is slower and depends on CORS restrictions.
Example
You can see the original image for a blink below. This shows the asynchronous approach works as it allows the UI to update while processing the tiles in chunks.
window.onload = function() {
var img = document.querySelector("img"),
canvas = document.querySelector("canvas"),
ctx = canvas.getContext("2d"),
w = img.naturalWidth, h = img.naturalHeight,
// store average tile colors here:
tileColors = [];
// draw in image
canvas.width = w; canvas.height = h;
ctx.drawImage(img, 0, 0);
// MAIN CALL: calculate, when done the callback function will be invoked
avgTiles(function() {console.log("done!")});
// The tiling function
function avgTiles(callback) {
var cols = 8, // number of tiles (make sure it produce integer value
rows = 8, // for tw/th below:)
tw = (w / cols)|0, // pixel width/height of each tile
th = (h / rows)|0,
x = 0, y = 0;
(function process() { // for async processing
var data, len, count, r, g, b, i;
while(x < cols) { // get next tile on x axis
r = g = b = i = 0;
data = ctx.getImageData(x * tw, y * th, tw, th).data; // single tile
len = data.length;
count = len / 4;
while(i < len) { // calc this tile's color average
r += data[i++]; // add values for each component
g += data[i++];
b += data[i++];
i++
}
// store average color to array, no need to write back at this point
tileColors.push({
r: (r / count)|0,
g: (g / count)|0,
b: (b / count)|0
});
x++; // next tile
}
y++; // next row, but do an async break below:
if (y < rows) {
x = 0;
setTimeout(process, 9); // call it async to allow browser UI to update
}
else {
// draw tiles with average colors, fillRect is faster than setting each pixel:
for(y = 0; y < rows; y++) {
for(x = 0; x < cols; x++) {
var col = tileColors[y * cols + x]; // get stored color
ctx.fillStyle = "rgb(" + col.r + "," + col.g + "," + col.b + ")";
ctx.fillRect(x * tw, y * th, tw, th);
}
}
// we're done, invoke callback
callback()
}
})(); // to self-invoke process()
}
};
<canvas></canvas>
<img src="http://i.imgur.com/X7ZrRkn.png" crossOrigin="anonymous">
Approach 2: Letting the browser do the job
We can also let the browser do the whole job exploiting interpolation and sampling.
When the browser scales an image down it will calculate the average for each new pixel. If we then turn off linear interpolation when we scale up we will get each of those average pixels as square blocks:
Scale down image at a ratio producing number of tiles as number of pixels
Turn off image smoothing
Scale the small image back up to the desired size
This will be many times faster than the first approach, and you will be able to use CORS-restricted images. Just note it may not be as accurate as the first approach, however, it is possible to increase the accuracy by scaling down the image in several step, each half the size.
Example
window.onload = function() {
var img = document.querySelector("img"),
canvas = document.querySelector("canvas"),
ctx = canvas.getContext("2d"),
w = img.naturalWidth, h = img.naturalHeight;
// draw in image
canvas.width = w; canvas.height = h;
// scale down image so number of pixels represent number of tiles,
// here use two steps so we get a more accurate result:
ctx.drawImage(img, 0, 0, w, h, 0, 0, w*0.5, h*0.5); // 50%
ctx.drawImage(canvas, 0, 0, w*0.5, h*0.5, 0, 0, 8, 8); // 8 tiles
// turn off image-smoothing
ctx.imageSmoothingEnabled =
ctx.msImageSmoothingEnabled =
ctx.mozImageSmoothingEnabled =
ctx.webkitImageSmoothingEnabled = false;
// scale image back up
ctx.drawImage(canvas, 0, 0, 8, 8, 0, 0, w, h);
};
<canvas></canvas>
<img src="http://i.imgur.com/X7ZrRkn.png" crossOrigin="anonymous">

Compare pixel values in photoshop

i want to make a little photoshop javascript. Technically, i just need to know how to compare the color values of pixels af if they were an array with three integer values in each, for example: (pseudocode)
for all pixels x
for all pixels y
if left pixel's green channel is bigger than red channel:
set the blue channel to 25
else
if the blue channel is greater than 50
set the green channel to 0
in the documentation, there's a ton of things like filters, text and layers you can do, but how do you do something as simple as this?
Reading and writing pixel values in Photoshop scripts is indeed not as simple as it could be ... Check out the following script which inverts the blue channel of an image:
var doc = app.open(new File("~/Desktop/test1.bmp"));
var sampler = doc.colorSamplers.add([0, 0]);
for (var x = 0; x < doc.width; ++x) {
for (var y = 0; y < doc.height; ++y) {
sampler.move([x, y]);
var color = sampler.color;
var region = [
[x, y],
[x + 1, y],
[x + 1, y + 1],
[x, y + 1],
[x, y]
];
var newColor = new SolidColor();
newColor.rgb.red = color.rgb.red;
newColor.rgb.green = 255 - color.rgb.green;
newColor.rgb.blue = color.rgb.blue;
doc.selection.select(region);
doc.selection.fill(newColor);
}
}
I'm not sure if there's a prettier way of setting a pixel color than the select + fill trick.
This script runs super slow, so maybe Photoshop scripts are not the best tool for pixel manipulation ...

Categories

Resources