Double arrray

I found a decent JS debugger and the first error is a syntax error. I assume its coming from the double array in the grid function. It seems fine to me.

{
  "message": "SyntaxError: missing ; after for-loop condition",
  "filename": "https://stacksnippets.net/js",
  "lineno": 46,
  "colno": 23
}

What I am trying to make with a line buffer though. The programming language couldn’t handle a line buffer and the line algorithm.

<!DOCTYPE html>

<canvas id="cnv" width="500" height="500" style="border: 2px solid black"></canvas>
<script>
  //canvas intialized and run first
  var cnv = document.getElementById('cnv');
  //getElementById() returns an Element object representing the element whose id property matches the specified string
  cnv.addEventListener('click', clickHandler);
  cnv.addEventListener('mousemove', moveHandler);
  //addEventListener() sets up a function to be called whenever the specified event is delivered to the target
  var ctx = cnv.getContext('2d'),
    //method returns a drawing context on the canvas
    line = false,
    //no line at start
    x1 = {},
    y1 = {},
    x2 = {},
    y2 = {},
    counter = 0, //storing point coordinates
    move = 0, //for determining if line is in the same column
    size = 25; //size of boxes

  //functions running in order
  //--------------
  //draw_grid runs at start
  //on event starts, click_event > line > draw_dot > line_buffer > line_close > draw_line > line_algorithm > shift || noshift > detection

  var box = [
    [],
    []
  ];

  function drawGrid() {
    var i = {},
      j = {};
    for (i = 0; i <= 20: i++) { //rows //20 * size = 500
      for (j = 0; j <= 20: j++) { //columns
        box[i.j] = ctx.rect(i * size, j * size, size, size);
      }
    }
  };
  drawGrid();

  function clickHandler(event) {
    if (!line) { //no line, starts line function
      line = new Line(event.layerX, event.layerY);
    } else { //line drawn from first point to second point
      line = line.close(event.layerX, event.layerY);
    }
    if (counter == 0) { //stores first point coordinates
      counter = 1;
      var x1 = event.clientX - ctx.canvas.offsetLeft,
        y1 = event.clientY - ctx.canvas.offsetTop;
    } else { //stores second point coordinates
      counter = 0;
      var x2 = event.clientX - ctx.canvas.offsetLeft,
        y2 = event.clientY - ctx.canvas.offsetTop;
      line_algorithm();
    }
  };

  function moveHandler(event) {
    if (line) { //line buffers to where mouse is
      line.drawBuffer(event.layerX, event.layerY)
    }
  };

  function Line(x, y) {
    var dotSize = 5; //current state is image data of canvas
    this.currentState = ctx.getImageData(0, 0, cnv.width, cnv.height);
    this.x = x;
    this.y = y;
    drawDot.call(this, x, y);

    function resetState() { //resets grid for buffering
      ctx.clearRect(0, 0, cnv.width, cnv.height);
      ctx.putImageData(this.currentState, 0, 0);
    };

    function drawLine(x, y) {
      ctx.beginPath();
      ctx.moveTo(this.x, this.y);
      ctx.lineTo(x, y);
      ctx.stroke();
    };

    function drawDot(x, y) {
      ctx.beginPath();
      ctx.arc(x, y, dotSize, 0, 2 * Math.PI);
      //ctx.arc(x, y, radius, startAngle, endAngle [, anticlockwise]);
      ctx.stroke();
    };
    this.close = function(x, y) {
      resetState.call(this);
      drawLine.call(this, x, y);
      return false;
    };
    this.drawBuffer = function(x, y) {
      resetState.call(this);
      drawDot.call(this, this.x, this.y);
      drawDot.call(this, x, y);
      ctx.setLineDash([5, 10]);
      drawLine.call(this, x, y);
      ctx.setLineDash([]);
    };
  };

  function line_algorithm() {
    var m = ((y1 - y2) / (x1 - x2)); //slope
    var b = (y1 - (m * x1)); //y intercept
    var i = {};
    for (i = 0; i < cnv.width; size++) { //check if line is in same column
      if (x1 >= (i - 1) * size && x1 <= (i * size) && x2 >= (i - 1) * size && x2 <= (i * size)) {
        move = 1; //line is in the same column
      }
    }
    if (move == 1) {
      noshift();
    } else {
      shift();
    }
  };

  function noshift() { //line is in the same column
    move = 0;
    var boxes = "";
    var y3 = Math.Min(y1, y2);
    var x3 = (y3 - b) / m;
    var column = Math.Floor(x3 / size) + 1; //right and left
    for (y3 = Math.Min(y1, y2); y3 <= Math.Max(y1, y2); y3++) {
      //loop starts from smaller y coordinate to bigger y coordinate, inc by 1
      var row = Math.Floor(y3 / size) + 1; //up and down
      detection();
    }
  };

  function shift() { //line crosses more than one column
    var boxes = "";
    for (var x3 = math.Min(x1, x2); x3 <= math.Max(x1, x2); x3++) {
      //loop starts from smaller x coordinate to bigger x coordinate, inc by 1
      var y3 = m * x3 + b, //y = mx + b;
        row = Math.Floor(y3 / size) + 1,
        //math.floor gets lowest possible integer, 5.3 to 5
        column = Math.Floor(x3 / size) + 1;
      //row and column are x and y of  a box hit, use smaller coordinates
      detection();
    }
  };

  function detection() { //hitcheck list string starts off as "" 
    if (boxes.indexOf(row + ":" + column + ",") == -1) {
      //if last hitcheck ISN'T in list of coordinates then box is filled in
      boxes = boxes + row + ":" + column + ","; //adds hitcheck to list    
      ctx.fillStyle = 'rgba(225,225,225,0.5)'; //changes box opacity
      var box[row, column] = ctx.fillRect(25, 72, 32, 32);
    }
  };

</script>

Let’s use some tools to help simplify the process. JSBeautifier helps to ensure that the formatting is all consistent, and JSLint is one of the toughest and best linters out there.

After telling JSLint to tolerate the single quotes and vars being used to define multiple variables, we easily find the problem.

      for (i = 0; i <= 20: i++) { //rows //20 * size = 500
          for (j = 0; j <= 20: j++) { //columns

The colons must be semicolons instead.

2 Likes

thank you, i didnt realize i used colons, jslint seems like a good debugger.
is there anything else i need to check or add besides checking single quote strings and multiple variables

so i fixed the for loop and ran the code in flint and the first error i get is a syntax error in the canvas tag where a ‘/’ is expected, i tripled checked the tag and compared it to the examples on w3school and the code seems fine. so i added html, head and body tags because thats what i thought caused the error and now a ‘/’ is expected in
the title tag
why am i getting that error? i really hate javascript

<!DOCTYPE html>

<html lang="en">

<head>
    <meta charset="UTF-8" />
    <title>Grid</title>
</head>

<body>

    <canvas id="cnv" width="500" height="500" style="border:2px solid black;"></canvas>
    <script>
        //canvas intialized and run first
        var cnv = document.getElementById('cnv');
        //getElementById() returns an Element object representing the element whose id property matches the specified string
        cnv.addEventListener('click', clickHandler);
        cnv.addEventListener('mousemove', moveHandler);
        //addEventListener() sets up a function to be called whenever the specified event is delivered to the target
        var ctx = cnv.getContext('2d'),
            //method returns a drawing context on the canvas
            line = false,
            //no line at start
            x1 = {},
            y1 = {},
            x2 = {},
            y2 = {},
            counter = 0, //storing point coordinates
            move = 0, //for determining if line is in the same column
            size = 25; //size of boxes

        //functions running in order
        //--------------
        //draw_grid runs at start
        //on event starts, click_event > line > draw_dot > line_buffer > line_close > draw_line > line_algorithm > shift || noshift > detection

        var box = [
            [],
            []
        ];

        function drawGrid() {
            var i = {},
                j = {};
            for (i = 0; i <= 20; i++) { //rows //20 * size = 500
                for (j = 0; j <= 20; j++) { //columns
                    box[i.j] = ctx.rect(i * size, j * size, size, size);
                }
            }
        };
        drawGrid();

        function clickHandler(event) {
            if (!line) { //no line, starts line function
                line = new Line(event.layerX, event.layerY);
            } else { //line drawn from first point to second point
                line = line.close(event.layerX, event.layerY);
            }
            if (counter == 0) { //stores first point coordinates
                counter = 1;
                var x1 = event.clientX - ctx.canvas.offsetLeft,
                    y1 = event.clientY - ctx.canvas.offsetTop;
            } else { //stores second point coordinates
                counter = 0;
                var x2 = event.clientX - ctx.canvas.offsetLeft,
                    y2 = event.clientY - ctx.canvas.offsetTop;
                line_algorithm();
            }
        };

        function moveHandler(event) {
            if (line) { //line buffers to where mouse is
                line.drawBuffer(event.layerX, event.layerY)
            }
        };

        function Line(x, y) {
            var dotSize = 5; //current state is image data of canvas
            this.currentState = ctx.getImageData(0, 0, cnv.width, cnv.height);
            this.x = x;
            this.y = y;
            drawDot.call(this, x, y);

            function resetState() { //resets grid for buffering
                ctx.clearRect(0, 0, cnv.width, cnv.height);
                ctx.putImageData(this.currentState, 0, 0);
            };

            function drawLine(x, y) {
                ctx.beginPath();
                ctx.moveTo(this.x, this.y);
                ctx.lineTo(x, y);
                ctx.stroke();
            };

            function drawDot(x, y) {
                ctx.beginPath();
                ctx.arc(x, y, dotSize, 0, 2 * Math.PI);
                //ctx.arc(x, y, radius, startAngle, endAngle [, anticlockwise]);
                ctx.stroke();
            };
            this.close = function(x, y) {
                resetState.call(this);
                drawLine.call(this, x, y);
                return false;
            };
            this.drawBuffer = function(x, y) {
                resetState.call(this);
                drawDot.call(this, this.x, this.y);
                drawDot.call(this, x, y);
                ctx.setLineDash([5, 10]);
                drawLine.call(this, x, y);
                ctx.setLineDash([]);
            };
        };

        function line_algorithm() {
            var m = ((y1 - y2) / (x1 - x2)); //slope
            var b = (y1 - (m * x1)); //y intercept
            var i = {};
            for (i = 0; i < cnv.width; size++) { //check if line is in same column
                if (x1 >= (i - 1) * size && x1 <= (i * size) && x2 >= (i - 1) * size && x2 <= (i * size)) {
                    move = 1; //line is in the same column
                }
            }
            if (move == 1) {
                noshift();
            } else {
                shift();
            }
        };

        function noshift() { //line is in the same column
            move = 0;
            var boxes = "";
            var y3 = Math.Min(y1, y2);
            var x3 = (y3 - b) / m;
            var column = Math.Floor(x3 / size) + 1; //right and left
            for (y3 = Math.Min(y1, y2); y3 <= Math.Max(y1, y2); y3++) {
                //loop starts from smaller y coordinate to bigger y coordinate, inc by 1
                var row = Math.Floor(y3 / size) + 1; //up and down
                detection();
            }
        };

        function shift() { //line crosses more than one column
            var boxes = "";
            for (var x3 = math.Min(x1, x2); x3 <= math.Max(x1, x2); x3++) {
                //loop starts from smaller x coordinate to bigger x coordinate, inc by 1
                var y3 = m * x3 + b, //y = mx + b;
                    row = Math.Floor(y3 / size) + 1,
                    //math.floor gets lowest possible integer, 5.3 to 5
                    column = Math.Floor(x3 / size) + 1;
                //row and column are x and y of  a box hit, use smaller coordinates
                detection();
            }
        };

        function detection() { //hitcheck list string starts off as "" 
            if (boxes.indexOf(row + ":" + column + ",") == -1) {
                //if last hitcheck ISN'T in list of coordinates then box is filled in
                boxes = boxes + row + ":" + column + ","; //adds hitcheck to list    
                ctx.fillStyle = 'rgba(225,225,225,0.5)'; //changes box opacity
                var box[row, column] = ctx.fillRect(25, 72, 32, 32);
            }
        };
    </script>

</body>

</html>

JSLint is only for JavaScript code. HTML code is not dealt with there.

When I run your updated code I get a console error that says:
SyntaxError: unexpected token: ‘[’

Here’s the line that it’s happening at:

                var box[row, column] = ctx.fillRect(25, 72, 32, 32);

What do you think is going wrong there?

Processing through the code with JSLint, the single quotes and var statements were easily dealt with.

After that though, the x1,y1,x2,y2 variables that are initialized up top are re-initialized again later on, when they are intended to update those initial ones, so the var statement is removed from those latter ones.

    if (counter == 0) { //stores first point coordinates
        counter = 1;
        // var x1 = event.clientX - ctx.canvas.offsetLeft;
        x1 = event.clientX - ctx.canvas.offsetLeft;
        // var y1 = event.clientY - ctx.canvas.offsetTop;
        y1 = event.clientY - ctx.canvas.offsetTop;
    } else { //stores second point coordinates
        counter = 0;
        // var x2 = event.clientX - ctx.canvas.offsetLeft;
        x2 = event.clientX - ctx.canvas.offsetLeft;
        // var y2 = event.clientY - ctx.canvas.offsetTop;
        y2 = event.clientY - ctx.canvas.offsetTop;
        line_algorithm();
    }

The j++ index increment statements are replaced by increment operators instead:

    // for (i = 0; i <= 20; i++) { //rows //20 * size = 500
    for (i = 0; i <= 20; i += 1) { //rows //20 * size = 500
        // for (j = 0; j <= 20; j++) { //columns
        for (j = 0; j <= 20; j += 1) { //columns
...
    // for (i = 0; i < cnv.width; size++) { //check if line is in same column
    for (i = 0; i < cnv.width; size += 1) { //check if line is in same column
...
    // for (y3 = Math.Min(y1, y2); y3 <= Math.Max(y1, y2); y3++) {
    for (y3 = Math.Min(y1, y2); y3 <= Math.Max(y1, y2); y3 += 1) {
...
    for (var x3 = math.Min(x1, x2); x3 <= math.Max(x1, x2); x3++) {
    for (var x3 = math.Min(x1, x2); x3 <= math.Max(x1, x2); x3 += 1) {

Semicolon troubles were also taken care of, for example with this code:

    function moveHandler(event) {
        if (line) { //line buffers to where mouse is
         // line.drawBuffer(event.layerX, event.layerY)
            line.drawBuffer(event.layerX, event.layerY);
        }
 // };
    }

The this keyword is not needed for currentState, as scope makes the variable accessible by all functions inside of the Line function.

    // this.currentState = ctx.getImageData(0, 0, cnv.width, cnv.height);
    var currentState = ctx.getImageData(0, 0, cnv.width, cnv.height);
...
        // ctx.putImageData(this.currentState, 0, 0);
        ctx.putImageData(currentState, 0, 0);

The same applies too to the this.x and this.y variables as well, for they are available to all functions inside of the Line function:

function Line(x, y) {
    // this.x = x;
    // this.y = y;
    // drawDot.call(this, x, y);
    drawDot(x, y);

The same can be done to the rest of the code that uses the this keyword. No wonder you were hating javascript - there are good and bad ways of doing things, and you were mired in some really bad ways.

We’re also told to not assign variables inside of a for loop:

    var column = Math.Floor(x3 / size) + 1; //right and left
    for (y3 = Math.Min(y1, y2); y3 <= Math.Max(y1, y2); y3 += 1) {
        //loop starts from smaller y coordinate to bigger y coordinate, inc by 1
        var row = Math.Floor(y3 / size) + 1; //up and down
        detection();
    }

I recommend that you pass row,column to the detection function instead.

    var row = 0;
    var column = Math.Floor(x3 / size) + 1; //right and left
    for (y3 = Math.Min(y1, y2); y3 <= Math.Max(y1, y2); y3 += 1) {
        //loop starts from smaller y coordinate to bigger y coordinate, inc by 1
        row = Math.Floor(y3 / size) + 1; //up and down
        detection(row, column);
    }
...
        var row = Math.Floor(y3 / size) + 1;
        //math.floor gets lowest possible integer, 5.3 to 5
        var column = Math.Floor(x3 / size) + 1;
        //row and column are x and y of  a box hit, use smaller coordinates
        detection(row, column);
...
function detection(row, column) { //hitcheck list string starts off as ""
    ...
}

Oh yes, nested arrays are accessed one array index at a time too, and box has already been defined so var isn’t used there either.

        // var box[row, column] = ctx.fillRect(25, 72, 32, 32);
        box[row][column] = ctx.fillRect(25, 72, 32, 32);

What else is left? The shift() function declares lots of variables inside of the for loop which needs to be fixed:

    var x3 = 0;
    var y3 = 0;
    var row = 0;
    var column = 0;
    // for (var x3 = math.Min(x1, x2); x3 <= math.Max(x1, x2); x3 += 1) {
    for (x3 = math.Min(x1, x2); x3 <= math.Max(x1, x2); x3 += 1) {
        //loop starts from smaller x coordinate to bigger x coordinate, inc by 1
        // var y3 = m * x3 + b; //y = mx + b;
        y3 = m * x3 + b; //y = mx + b;
        // var row = Math.Floor(y3 / size) + 1;
        row = Math.Floor(y3 / size) + 1;
        //math.floor gets lowest possible integer, 5.3 to 5
        // var column = Math.Floor(x3 / size) + 1;
        column = Math.Floor(x3 / size) + 1;
        //row and column are x and y of  a box hit, use smaller coordinates
        detection(row, column);
    }

The detection function is out of scope, so we’ll move it up above the functions that use it:

function detection(row, column) { //hitcheck list string starts off as ""
    ...
}
function noshift() { //line is in the same column
    ...
}
function shift() { //line crosses more than one column
    ...
}
// function detection(row, column) { //hitcheck list string starts off as ""
//     ...
// }

And JSLint is finally able to get to the end of the code doing its checks, which brings us to the next stage of cleaning up the code.


Reducing this list to zero items.

I’ll take care of them one at a time in the next post.

1 Like

Undeclared document

This is because jslint doesn’t assume that the code is being run in a web browser. We can easily tell it that by placing a directive at the top of the code:

/*jslint browser */

If we’re using a local version of JSLint to lint our code, that directive can be placed in a local config file instead, but for now it’s at the top of the code.

Click handler out of scope.

That’s an init section of code, which belongs below the functions.

// other functions are above here
function clickHandler(event) {
    ...
}
cnv.addEventListener("click", clickHandler);
cnv.addEventListener("mousemove", moveHandler);
//addEventListener() sets up a function to be called whenever the specified event is delivered to the target

This function needs a “use strict” pragma.

It’s possible to put "use strict" in all such functions, but a better solution is to use it once at an upper level instead. That’s best done by using an Immediately Invoked Function Expression (called an IIFE), which also helps to protect other code from the effects of your own too.

(function iife() {
    "use strict";
    // rest of code in here
}());

Unexpected ‘for’

JSLint prefers to not use for statements when better solutions are at hand. Here’s the existing code:

        var i = {};
        var j = {};
        for (i = 0; i <= 20; i += 1) { //rows //20 * size = 500
            for (j = 0; j <= 20; j += 1) { //columns
                box[i.j] = ctx.rect(i * size, j * size, size, size);
            }
        }

To work it needs to be [i][j] instead of [i.j], and instead of using a for loop we can use the array forEach method instead.

        box.forEach(function (row, i, box) {
            row.forEach(function (ignore, j) {
                box[i][j] = ctx.rect(i * size, j * size, size, size);
            });
        });

We just need the box array to be 20x20 to start with now.

Normally with JSLint an array of a defined size is created with:

    var items = new Array(20).fill(0);

If we try that with a nested array though we end up with trouble:

    // problem code, do not use
    var box = new Array(20).fill(
        new Array(20).fill(0)
    );

The problem with the above code is that each row will point to the same exact array, so when you update the first item of a row, every row ends up with that same exact item.

What we can do instead is to map over each row, replacing it with a new array of our desired size.

    var box = new Array(20).fill([])
        .map(function () {
            return new Array(20).fill(0);
        });

And, if we use the ES6 arrow notation, the code then becomes as easy as the following:

    var box = new Array(20).fill([])
        .map(() => new Array(20).fill(0));

‘drawDot’ is out of scope.

The function is used before it exists. While function declarations are immune to this issue, refactoring them to function expressions results in the problem occurring, so it’s safest to have functions appear first before the code that uses them.

This just means pushing the call to drawDot() down to the end of the Line function.

    function Line(x, y) {
        var dotSize = 5; //current state is image data of canvas
        var currentState = ctx.getImageData(0, 0, cnv.width, cnv.height);
        // drawDot(x, y);

        // functions defined here

        drawDot(x, y);
    }

Unexpected ‘this’.

The this keyword causes too many confusions when it comes to understanding scope. Replace this with Line and the code is easier to understand.

        // this.close = function (x, y) {
        Line.close = function (x, y) {
        ...
        // this.drawBuffer = function (x, y) {
        Line.drawBuffer = function (x, y) {

Unexpected ‘for’.

When there isn’t a clearly defined array to work with, for statements can be returned back to a while loop instead:

        // var i = {};
        var i = 0;
        // for (i = 0; i < cnv.width; size += 1) { //check if line is in same column
        while (i < cnv.width) { //check if line is in same column
            if (x1 >= (i - 1) * size && x1 <= (i * size) && x2 >= (i - 1) * size && x2 <= (i * size)) {
                move = 1; //line is in the same column
            }
            size += 1;
        }

Did you notice though that the for loop (and the while loop) will never end, because the condition is checking i, but i never changes.

As size is initialized to a set value earlier on, I’m going to update the increment from size to i until that can be investigated further.

            // size += 1;
            i += 1;

The m and b variables defined in that function don’t seem to be used for anything yet either, which requires further investigation too.

Expected ‘===’ and instead saw ‘==’.

The double equals results in too many unexpected things matching, so the triple equals fixes those problems.

        // if (move == 1) {
        if (move === 1) {

I’ve updated other occurences of double equals in the code too.

Out of scope

Each of these out of scope issues are dealt with by moving the function up above where it is called.

    function noshift() { //line is in the same column
        ...
    }
    function shift() { //line crosses more than one column
        ...
    }
    function line_algorithm() {
        ...
        if (move === 1) {
            noshift();
        } else {
            shift();
        }
    }
    // function noshift() { //line is in the same column
    //     ...
    // }
    // function shift() { //line crosses more than one column
    //     ...
    // }

Expected ‘new’ before ‘Min’.

The min math method is lowercase.

        // var y3 = Math.Min(y1, y2);
        var y3 = Math.min(y1, y2);

And the same goes for the max method too.

Undeclared ‘b’.

    function noshift() { //line is in the same column
        ...
        var x3 = (y3 - b) / m;
        ...
    }

There are a number of potential solutions to this issue. We could move the noshift() and shift() functions inside of the line_algorithm() function so that they share the same scope, or we could pass the b and m variables as arguments to the function. The latter option is the better choice here.

    function noshift(m, b) { //line is in the same column
        ...
    }
    function shift(m, b) { //line crosses more than one column
        ...
    }
...
        if (move === 1) {
            noshift(m, b);
        } else {
            shift(m, b);
        }

Expected ‘new’ before ‘Floor’.

It’s all lowercase for floor here too, for example:

            // row = Math.Floor(y3 / size) + 1;
            row = Math.floor(y3 / size) + 1;

Unexpected ‘for’.

All of the other for loops should be turned into while loops instead. For example:

        // for (y3 = Math.min(y1, y2); y3 <= Math.max(y1, y2); y3 += 1) {
        while (y3 <= Math.max(y1, y2)) {
            //loop starts from smaller y coordinate to bigger y coordinate, inc by 1
            row = Math.floor(y3 / size) + 1; //up and down
            detection(row, column);
            y3 += 1;
        }

Undeclared ‘boxes’.

This is the last issue being complained about. What is boxes used for?

    var size = 25; //size of boxes
...
        if (boxes.indexOf(row + ":" + column + ",") === -1) {
            //if last hitcheck ISN'T in list of coordinates then box is filled in
            boxes = boxes + row + ":" + column + ","; //adds hitcheck to list
...
        var boxes = "";

It looks like boxes is just a string, which should be defined up by the size variable.

    var boxes = "";
    var size = 25; //size of boxes

Because boxes is now defined, those other statements that empty the string shouldn’t use var.

    function noshift(m, b) { //line is in the same column
        move = 0;
        // var boxes = "";
        boxes = "";
        ...
    }
    function shift(m, b) { //line crosses more than one column
        // var boxes = "";
        boxes = "";
        ...
    }

And JSLint reports that there are no more structural problems with the code.

Here’s what the updated JavaScript code looks like:

/*jslint browser */
(function iife() {
    "use strict";
    //canvas intialized and run first
    var cnv = document.getElementById("cnv");
    //getElementById() returns an Element object representing the element whose id property matches the specified string

    var ctx = cnv.getContext("2d"); //method returns a drawing context on the canvas
    var line = false; //no line at start
    var x1 = {};
    var y1 = {};
    var x2 = {};
    var y2 = {};
    var counter = 0; //storing point coordinates
    var move = 0; //for determining if line is in the same column
    var boxes = "";
    var size = 25; //size of boxes

    //functions running in order
    //--------------
    //draw_grid runs at start
    //on event starts, click_event > line > draw_dot > line_buffer > line_close > draw_line > line_algorithm > shift || noshift > detection

    var box = new Array(20).fill([])
        .map(() => new Array(20).fill(0));

    function drawGrid() {
        box.forEach(function (row, i, box) {
            row.forEach(function (ignore, j) {
                box[i][j] = ctx.rect(i * size, j * size, size, size);
            });
        });
    }
    drawGrid();

    function moveHandler(event) {
        if (line) { //line buffers to where mouse is
            line.drawBuffer(event.layerX, event.layerY);
        }
    }

    function Line(x, y) {
        var dotSize = 5; //current state is image data of canvas
        var currentState = ctx.getImageData(0, 0, cnv.width, cnv.height);

        function resetState() { //resets grid for buffering
            ctx.clearRect(0, 0, cnv.width, cnv.height);
            ctx.putImageData(currentState, 0, 0);
        }

        function drawLine(x, y) {
            ctx.beginPath();
            ctx.moveTo(x, y);
            ctx.lineTo(x, y);
            ctx.stroke();
        }

        function drawDot(x, y) {
            ctx.beginPath();
            ctx.arc(x, y, dotSize, 0, 2 * Math.PI);
            //ctx.arc(x, y, radius, startAngle, endAngle [, anticlockwise]);
            ctx.stroke();
        }
        Line.close = function (x, y) {
            resetState();
            drawLine(x, y);
            return false;
        };
        Line.drawBuffer = function (x, y) {
            resetState();
            drawDot(x, y);
            drawDot(x, y);
            ctx.setLineDash([5, 10]);
            drawLine(x, y);
            ctx.setLineDash([]);
        };

        drawDot(x, y);
    }

    function detection(row, column) { //hitcheck list string starts off as ""
        if (boxes.indexOf(row + ":" + column + ",") === -1) {
            //if last hitcheck ISN'T in list of coordinates then box is filled in
            boxes = boxes + row + ":" + column + ","; //adds hitcheck to list
            ctx.fillStyle = "rgba(225,225,225,0.5)"; //changes box opacity
            box[row][column] = ctx.fillRect(25, 72, 32, 32);
        }
    }

    function noshift(m, b) { //line is in the same column
        move = 0;
        boxes = "";
        var y3 = Math.min(y1, y2);
        var x3 = (y3 - b) / m;
        var row = 0;
        var column = Math.floor(x3 / size) + 1; //right and left
        while (y3 <= Math.max(y1, y2)) {
            //loop starts from smaller y coordinate to bigger y coordinate, inc by 1
            row = Math.floor(y3 / size) + 1; //up and down
            detection(row, column);
            y3 += 1;
        }
    }

    function shift(m, b) { //line crosses more than one column
        boxes = "";
        var x3 = Math.min(x1, x2);
        var y3 = 0;
        var row = 0;
        var column = 0;
        while (x3 <= Math.max(x1, x2)) {
            //loop starts from smaller x coordinate to bigger x coordinate, inc by 1
            y3 = m * x3 + b; //y = mx + b;
            row = Math.floor(y3 / size) + 1;
            //math.floor gets lowest possible integer, 5.3 to 5
            column = Math.floor(x3 / size) + 1;
            //row and column are x and y of  a box hit, use smaller coordinates
            detection(row, column);
            x3 += 1;
        }
    }

    function line_algorithm() {
        var m = ((y1 - y2) / (x1 - x2)); //slope
        var b = (y1 - (m * x1)); //y intercept
        var i = 0;
        while (i < cnv.width) { //check if line is in same column
            if (x1 >= (i - 1) * size && x1 <= (i * size) && x2 >= (i - 1) * size && x2 <= (i * size)) {
                move = 1; //line is in the same column
            }
            i += 1;
        }
        if (move === 1) {
            noshift(m, b);
        } else {
            shift(m, b);
        }
    }

    function clickHandler(event) {
        if (!line) { //no line, starts line function
            line = new Line(event.layerX, event.layerY);
        } else { //line drawn from first point to second point
            line = line.close(event.layerX, event.layerY);
        }
        if (counter === 0) { //stores first point coordinates
            counter = 1;
            x1 = event.clientX - ctx.canvas.offsetLeft;
            y1 = event.clientY - ctx.canvas.offsetTop;
        } else { //stores second point coordinates
            counter = 0;
            x2 = event.clientX - ctx.canvas.offsetLeft;
            y2 = event.clientY - ctx.canvas.offsetTop;
            line_algorithm();
        }
    }

    cnv.addEventListener("click", clickHandler);
    cnv.addEventListener("mousemove", moveHandler);
    //addEventListener() sets up a function to be called whenever the specified event is delivered to the target
}());

Now that the linting has dealt with a majority of the issues, it’s just a matter now of getting the code to work…

2 Likes

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.