Asha
Asha

Reputation: 79

How to map multi dimensional array to a rectangular grid in html canvas?

There is a rectangular grid which will be of size 12 by 12,in which each corner will be clickable, that is 6*3 cells from each corner of 12*12 grid (single click on any the 4 corners should select 6*3 cells).

I have a created multi dimensional array of size 12*12 which will be represented by 0. And depending on the corner user chooses that corresponding 6*3 cells will turn into 1. How to display this concept through html canvas?That is how to represent this array in the form of a grid, also i want the corner of the grid selected ,that is those cells which is turning in to 1 to be represented in a different color.

addBed = (x,y) => {let test = Array(12).fill(0).map(x => Array(12).fill(0));                    

  let bedX = 3, bedY = 6; // BED AREA = 6*3                                                     
  let dx = 1, dy = 1;                                                                           
  let endX = bedX, endY = bedY;                                                                 
  const roomX = 11, roomY = 11                                                                  
  if(x === roomX) {                                                                             
    dx = -1                                                                                     
    endX = roomX-bedX                                                                           
  }                                                                                             
  if (y === roomY) {                                                                            
    dy = -1                                                                                     
    endY = roomY-bedY                                                                           
  }                                                                                             
  for(let i = x; dx === 1 ? i < endX : i > endX; i += dx) {                                     
    for(let j = y; dy === 1 ? j < endY: j > endY; j += dy) {                                    
      test[i][j] = 1;                                                                           
    }                                                                                           
  }                                                                                             

  console.log(test)                                                                             
  // this.setState({ testMap: test });                                                          
}                                                                                               

addBed(0,0);        // this will make the first corner to be turned to 1

Upvotes: 1

Views: 978

Answers (1)

enxaneta
enxaneta

Reputation: 33044

This is how I would do it: I would use a unidimensional array of cells.

I would create also the array of the clickable cells with the index in the cells array and the corresponding extended rect to be drawn on click.

Please read the comments in my code.

let ctx = room.getContext("2d");
let cw = room.width = 300;//the width of the canvas
let ch = room.height = 300;//the height of the canvas

let size = 25;//the size of every cell
let rows = 12;//number of rows
let cols = 12;//number of columns

// initiate the cells array
let cells = new Array(cols*rows);

// the clickable cells: the index in the cells array and the extended rect position (x,y) and size (w,h)
let clickble = [
  {index:0, rect:{x:0,y:0,w:size*3,h:size*6}},
  {index:11, rect:{x:300-size*3,y:0,w:size*3,h:size*6}},
  {index:12*11, rect:{x:0,y:300-size*6,w:size*3,h:size*6}},
  {index:12*12-1, rect:{x:300-size*3,y:300-size*6,w:size*3,h:size*6}}
]


// fill the cells array with values
 for (y = 0; y <= rows; y++) {
    for (x = 0; x < cols; x++) {
      let index = x + y * cols;
      let cell = {}
      cell.x = x*size;
      cell.y = y*size;
      
      cells[index] = cell;
    }
 }
//draw every cell in the grid of cells
cells.forEach((c,i)=>{
  ctx.beginPath();
  ctx.strokeRect(c.x,c.y,size,size);  
})


// mark the cells clickble
clickble.forEach(c=>{ctx.fillRect(cells[c.index].x,cells[c.index].y,size,size);})


// when clicking on the canvas
room.addEventListener("click",(evt)=>{
  //get the mouse position
  let m = oMousePos(room, evt);
  
  for(let i = 0; i < clickble.length; i++ ){
    let cell =  cells[clickble[i].index];
    
    ctx.beginPath();
    //get the extended rect
    ctx.rect(cell.x,cell.y,size,size);
    // if the click happened inside one of the clickable cells
    if (ctx.isPointInPath(m.x, m.y)){
      let rect =  clickble[i].rect;
      // draw the extended rect
      ctx.beginPath();
      ctx.fillStyle = "red";
      ctx.fillRect(rect.x,rect.y,rect.w,rect.h);
      //breack the loop. No need to search further
      break;}
    
  }
})



// a function to detect the mouse position on the canvas
function oMousePos(canvas, evt) {
  var ClientRect = canvas.getBoundingClientRect();
	return { 
	x: Math.round(evt.clientX - ClientRect.left),
	y: Math.round(evt.clientY - ClientRect.top)
}
}
<canvas id="room"></canvas>

UPDATE

The OP is commenting:

I need all the 4 corners, but it depends on the user to choose which corner to be clicked, after clicking one corner, he will not be able to click another corner.

In this case to the previous code I'm adding a global variable let clicked = false; : no corner was clicked yet.

When the user is clicking one corner clicked = true;and no other corner can be clicked.

if (ctx.isPointInPath(m.x, m.y)){
      clicked = true;
................

because on click happens only if(!clicked)

room.addEventListener("click",(evt)=>{
  if(!clicked){...........}

let ctx = room.getContext("2d");
let cw = room.width = 300;
let ch = room.height = 300;

let size = 25;
let rows = 12;
let cols = 12;


let clicked = false;


let cells = new Array(cols*rows)
// the clickables cells: the index in the cells array and the extended rect position (x,y) and size (w,h)
let clickbles = [
  {index:0, rect:{x:0,y:0,w:size*3,h:size*6}},
  {index:11,rect:{x:300-size*3,y:0,w:size*3,h:size*6}},
  {index:12*11,rect:{x:0,y:300-size*6,w:size*3,h:size*6}},
  {index:12*12-1,rect:{x:300-size*3,y:300-size*6,w:size*3,h:size*6}}
]


//draw the grid of cells
 for (y = 0; y <= rows; y++) {
    for (x = 0; x < cols; x++) {
      let index = x + y * cols;
      let cell = {}
      cell.x = x*size;
      cell.y = y*size;
      
      cells[index] = cell;
    }
 }

cells.forEach((c,i)=>{
  ctx.beginPath();
  ctx.strokeRect(c.x,c.y,size,size);  
})


// mark the cells clickbles
clickbles.forEach(c=>{ctx.fillRect(cells[c.index].x,cells[c.index].y,size,size);})


// when clicking on the canvas
room.addEventListener("click",(evt)=>{
  
  
  if(!clicked){
  //get the mouse position
  let m = oMousePos(room, evt);
  
  for(let i = 0; i < clickbles.length; i++ ){
    let cell =  cells[clickbles[i].index];
    let rect =  clickbles[i].rect;
    ctx.beginPath();
    ctx.rect(cell.x,cell.y,size,size);
    // if the click happened inside one of the clickables cells
    if (ctx.isPointInPath(m.x, m.y)){
      clicked = true;
      
      // draw the extended rect
      ctx.beginPath();
      ctx.fillStyle = "red";
      ctx.fillRect(rect.x,rect.y,rect.w,rect.h);
      //breack the loop. No need to search further
      break;}
  }
  }
})

function oMousePos(canvas, evt) {
  var ClientRect = canvas.getBoundingClientRect();
	return { 
	x: Math.round(evt.clientX - ClientRect.left),
	y: Math.round(evt.clientY - ClientRect.top)
}
}
<canvas id="room"></canvas>

I hope this is what you were asking

Upvotes: 1

Related Questions