Loop through imagedata in a HTM5 canvas vertically - javascript

I am trying to loop into a canvas vertically. I mean, Looping through the first column, the the 2nd, etc.
My first goal is to colorize the first half of the width. My code is acting weird and the problem is... I do not know why!
loopVertical = function (data, canvas){
for (var x = 0; x < canvas.width*2; x+=4) {
for (var y = 0; y < canvas.height; y++) {
data[x+y*canvas.width*4] = 255;
}
}
return data;
}
The result: red stripes on my image. And that's not what I want.
After I will need to divide the image in smaller images if I have a vertical line of transparent pixels, but this is not the topic of the question :)

I don't really know about html5 canvas and the image data, but according to this tutorial, my guess is that your outer loop is wrong. Apparently, you also need to take care of operator precedence when calculating the index of your data. Maybe something like :
loopVertical = function (data, canvas){
// first half of the width
for (var x = 0; x < canvas.width / 2; x++) {
for (var y = 0; y < canvas.height; y++) {
data[(x+y*canvas.width)*4] = 255;
}
}
return data;
}

Related

How to Generate a Grid of Points Using Vectors in p5.js

I'm trying to generate a grid of points on the canvas described by vectors in order to make a flow field. I generate the vectors in a nested loop, then push them to a list, and finally attempt to draw them. However, when I attempt to draw them the .x and .y attributes aren't recognised. I think this is because the list of vectors is empty/only has one entry in it and I can't work out why. Apologies if this is a simple problem - this is my first time using javascript and p5.js. My code is shown below, it should generate a uniform grid of points.
let width = 600;
let height = 600;
let points = [];
function setup() {
createCanvas(width, height);
background(30);
let density = 50;
let spacing = width / density;
for (var x = 0; x < width; x += spacing); {
for (var y = 0; y < height; y += spacing); {
var p = createVector(x, y)
points.push(p)
}
}
}
function draw() {
noStroke();
fill(255);
for (var i = 0; i < points.length; i++); {
circle(points[i].x, points[i].y, 1);
}
}
EDIT: My code is definitely generating one vector, but only one for some reason. So I believe the issue is the for loops not executing correctly.
Your for loop syntax is incorrect. There should not be a semicolon after the closing parenthesis and the opening curly brace:
// !
for (var i = 0; i < points.length; i++); {
circle(points[i].x, points[i].y, 1);
}
You will need to fix each of your for loops.

Print out section of html5 canvas

I have a html5 canvas (3000px x 3000px). I am using the following function to print the canvas:
function printCanvas()
{
popup = window.open();
popup.document.write("<br> <img src='"+canvas.toDataURL('png')+"'/>");
popup.print();
}
The problem is, if I have only element on the canvas (say a circle) and press print, it will attempt to print out the entire canvas (3000 x 3000). Is there a way to change it so it only prints a certain section of the canvas or just the part where there are elements and not print out white space.
Thanks.
The other answer describes right approach. The main problem is to find boundaries. I get all image data and in two for-loops find boundaries:
function getCanvasBorders(canvas) {
var topleft = {x: false, y: false};
var botright = {x: false, y: false};
for(var x = 0; x < canvas.width; x++) {
for(var y = 0; y < canvas.height; y++) {
if(!emptyPixel(context, x, y)) {
if(topleft.x === false || x < topleft.x) {
topleft.x = x;
}
if(topleft.y === false || y < topleft.y) {
topleft.y = y;
}
if(botright.x === false || x > botright.x) {
botright.x = x;
}
if(botright.y === false || y > botright.y) {
botright.y = y;
}
}
}
}
return {topleft: topleft, botright: botright};
}
SO snippets can't work with document, so here is a jsfiddle.
Assuming you have a way to track and redraw the objects, you can do the following:
Calculate the max bounds that can contain all the objects
Create a new canvas the size of that bound
Draw in the region the bound represents using drawImage() with its clipping parameters, but offset -x and -y of the bound start position.
Print the temporary canvas instead.
Update: to find edges of image content only, assuming transparency for background, see this answer.

Using arrays for tetris pieces and grid. Why won't this piece show up completely?

Here's the code, below is a link to the jsfiddle.
The first row of the block isn't drawn for some reason, all these 2 dimension for loops are hard for me to wrap my head around, and can't find the reason why the block is only being drawn from the second row and not complete.
The add_block function is supposed to read array data from any block and put it on the grid where my x and y coordinates are.
If there's anyone who knows how to rotate the block, that would be cool too, i know in order to turn something +90 degrees i need to transpose and then revese each row, but it hasn't really worked in earlier tries.
I know i'm not good at explaining but i'll be sure to answer any of your questions.
Thanks in advance! I really want to have a complete picture of how arrays and double for loops interact with eachother, in my head.
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
canvas.width = 300;
canvas.height = 500;
var grid_columns = 10;
var grid_rows = 15;
var grid_cell_size = 10;
var grid = [];
function create_empty_grid(){
for(var i=0;i<grid_columns;i++){
grid[i] = [];
for(var j=0;j<grid_rows;j++){
grid[i][j] = 0;
}
}
// END DOUBLE FOR LOOPS
}
function clear_grid(){
for(var i=0;i<grid_columns;i++){
for(var j=0;j<grid_rows;j++){
grid[i][j] = 0;
}
}
// END DOUBLE FOR LOOPS
}
var x = 0;
var y = 0;
var w = 2;
var h = 3;
var block = [];
block[0] = [
[1,0,0,0],
[1,1,0,0],
[0,1,0,0],
[0,0,0,0]
];
function add_block(num){
var b = block[num];
for(var i=0;i<w;i++){
for(var j=0;j<h;j++){
if(i >= x && j >= y && i <= w && j <= h){
grid[i][j] = b[i][j];
}
}
}
// END DOUBLE FOR LOOPS
}
function draw(){
for(var i=0;i<grid_columns;i++){
for(var j=0;j<grid_rows;j++){
ctx.fillStyle = "black";
if(grid[i][j] === 1){
ctx.fillStyle = "red";
}else if(grid[i][j] === 0){
ctx.fillStyle = "green";
}
ctx.fillRect(i*grid_cell_size,j*grid_cell_size,grid_cell_size-1,grid_cell_size-1);
}
}
// END DOUBLE FOR LOOP
}
function update(){
}
function tick(){
clearRect(0,0,canvas.width,canvas.height);
clear_grid();
draw();
update();
}
create_empty_grid();
add_block(0);
draw();
View in jsfiddle
It seems like you're using i and j to represent "block" coordinates, and
x and y to represent "grid" coordinates. So I think this condition is wrong:
if(i >= x && j >= y && i <= w && j <= h){
grid[i][j] = b[i][j];
}
I think all you really need here is to replace the if statement with something like:
grid[i+x][j+y] = b[i][j];
But as #Arnauld pointed out, there is also a bit of confusion about whether i,j denotes "row, column" or "column, row", and it looks like your usage is opposite from the way the arrays are initialized. In other words, you're using:
grid[row][column] and b[row][column]
but the way you laid out the arrays, it needs to be
grid[column][row] and b[column][row]
So you'll need to make a few adjustments here and there to make the code do what you want.
The "b" arrays were upside-down for some reason so i changed grid[j+x][i+y] = b[j][i] to grid[j+x][i+y] = b[i][j] this displays the full block, but i still can't visualise the arrays or be able to prevent these problems in the future.
Arrays are so confusing to me.
I'll figure it out eventually i guess, but a day has to go by.

How to draw map from pixel array

I'm trying to load a map from a image, I get the hex codes for every pixel on my image, which is put into map[]. The world[] will store 1 or 0 or whatever number for number of sprite on the spritesheet. If the color is #8aff00 then it will store 0 for grass. The map is 16 by 16 pixels, which is my world size. When I try to make the world array, the for statement is working right, there is no errors. I know I have all the data, but this part of my function is not working, it just stops after these for statements:
function createWorld() {
for (var y=0; y < worldHeight; y++) {
for (var x=0; x < worldWidth; x++) {
if (map[pl] == '#8aff00') world[x][y] = 0;
if (map[pl] == '#000000') world[x][y] = 1;
pl+=4;
}
}
alert('about to draw');
draw();
}
The alert never gets called. When I displayed the x and y, it went '0,0' to '1,0' then back to '0,0' for the value x and y. The pl is for which number of the array I want. When I put another for statement to handle that it messed up more. Is there something wrong with the code, if you need more of the code just let me know.
I've managed to build code which is working.
Now it's Your turn to implement it as You need.
worldHeight = 16;
worldWidth = 16;
world = {};
map = ['#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000','#8aff00','#000000']
pl = 0
function draw(){
console.log(world);
}
function createWorld() {
for (var y=0; y < worldHeight; y++) {
for (var x=0; x < worldWidth; x++) {
if (map[pl] == '#8aff00')
world[x] = 0;
world[x][y] = 0;
if (map[pl] == '#000000')
world[x] = 0;
world[x][y] = 1;
pl+=4;
}
}
alert('about to draw');
draw();
}
createWorld();
Cheers.

HTML5 Canvas - how to zoom in the pixels?

How would one zoom in a Canvas to see the pixels? Currently when I try to use the scale() function the image is always antialiased.
The property mozImageSmoothingEnabled seems to work, but it's only for Firefox.
As far as I know any rescaling done by the browser will result in a smooth interpolation.
So as long as you want to do this with JS you will have to let JS do all the work. This means either finding a nice library or writing a function yourself. It could look like this. But I hope it's possible to make it faster. As it's one of the simplest scaling algorithms there are probably many people who thought up improvements to do it even faster.
function resize(ctx, sx, sy, sw, sh, tx, ty, tw, th) {
var source = ctx.getImageData(sx, sy, sw, sh);
var sdata = source.data;
var target = ctx.createImageData(tw, th);
var tdata = target.data;
var mapx = [];
var ratiox = sw / tw, px = 0;
for (var i = 0; i < tw; ++i) {
mapx[i] = 4 * Math.floor(px);
px += ratiox;
}
var mapy = [];
var ratioy = sh / th, py = 0;
for (var i = 0; i < th; ++i) {
mapy[i] = 4 * sw * Math.floor(py);
py += ratioy;
}
var tp = 0;
for (py = 0; py < th; ++py) {
for (px = 0; px < tw; ++px) {
var sp = mapx[px] + mapy[py];
tdata[tp++] = sdata[sp++];
tdata[tp++] = sdata[sp++];
tdata[tp++] = sdata[sp++];
tdata[tp++] = sdata[sp++];
}
}
ctx.putImageData(target, tx, ty);
}
This function would take the rectangle of size (sw,sh) at (sx,sy), resize it to (tw,th) and draw it at (tx,ty).
As far as I know the antialiasing behavior is not defined in the spec and will depend on the browser.
One thing you could try is if you set the canvas's width/height with CSS into for example 300x300 and then give the canvas width and height attributes of 150 and 150, it will appear "zoomed in" by 200%. I'm not sure whether this will trigger the antialiasing, but do give it a shot.

Categories