简体   繁体   中英

Drawing Hexagon on Canvas, testing mouseclick event vs Hexagon

Im drawing a hexagon-based grid on the canvas.

Each hexagon is an object that holds the 6 points in x/y coordinates. Each hexagon object also holds its X/Y columns/row index.

var canvas = document.getElementById("can");
    canvas.width = 200;
    canvas.height = 200;    
var ctx = canvas.getContext("2d");

var grid = []; // array that holds the Hex
var globalOffset = 30 // not important, just for smoother display atm

  function Point(x, y) {
    this.x = x;
    this.y = y;
  }

  function Hex(x, y, size) {
    this.size = 20;
    this.x = x;
    this.y = y;
    this.points = [];
    this.id = [];

    this.create = function(x, y) {
      var offSetX = (size / 2 * x) * -1
      var offSetY = 0;

      if (x % 2 == 1) {
        offSetY = Math.sqrt(3) / 2 * this.size;
      }

      var center = new Point(
        x * this.size * 2 + offSetX + globalOffset,
        y * Math.sqrt(3) / 2 * this.size * 2 + offSetY + globalOffset
      )

      this.midPoint = center;

      this.id[0] = x;
      this.id[1] = y;

      for (var i = 0; i < 6; i++) {
        var degree = 60 * i;
        var radian = Math.PI / 180 * degree;

        var point = new Point(
          center.x + size * Math.cos(radian),
          center.y + size * Math.sin(radian)
        )

        this.points.push(point);
      }
    }

    this.create(x, y);
  }
}



//Determine where was clicked
canvas.addEventListener("click", function(e) {

  var rect = canvas.getBoundingClientRect();

  var pos = {
    x: e.clientX - rect.left,
    y: e.clientY - rect.top
  }


  document.getElementById("pos").innerHTML = "click on: " + pos.x + " " + pos.y;
});


// Creating Hexagons, setting up their center point, pushing them into Grid.
function init() {
  for (var i = 0; i < 5; i++) {
    for (var j = 0; j < 4; j++) {
      var hex = new Hex(i, j, 20);
      grid.push(hex)
    }
  }

  //for each Hex in Grid, draw the Hex
  for (var hex in grid) {
    var item = grid[hex];
    ctx.beginPath();
    ctx.moveTo(item.points[0].x, item.points[0].y);

    for (var k = 1; k < item.points.length; k++) {
      ctx.lineTo(item.points[k].x, item.points[k].y);
    }

    ctx.closePath();
    ctx.stroke();

    var text = item.id;
    ctx.fillStyle = "black";
    ctx.fillText(text, item.midPoint.x - 7, item.midPoint.y - item.size / 2.2);

  }

When clicking on the canvas i want to determine if i clicked a hex or not, and if i did, which hex (by column/row). Its math problem.

How can i do this ?

fully working example here: http://codepen.io/anon/pen/RrMzKy?editors=1111

If you treat the hexagon centres as if they were the centres of circles, the clicked hexagon is the one whose centre is closest to the click. (It should be possible to optimise this without testing the distance to every possible cell).

To account for the incomplete coverage, assume that there are more (invisible) hexagons in an additional ring surrounding the visible ones.

If one of those is chosen, or if the distance is greater than the circle radius, then the click was not on a visible hexagon.

Somewhat based on a refactoring of your own proposed code, and avoiding the two loops since the only gain is elimination of a single sqrt function:

Grid.prototype.getHexAt = function(pos) {

    var closest = null;
    var min = Infinity;

    grid.hexes.forEach(function(hex) {
        var dx = hex.center.x - pos.x;
        var dy = hex.center.y - pos.y;
        var distance = Math.sqrt(v.x * v.x + v.y * v.y);

        if (distance < hex.size && distance < min) {
            min = distance;
            closest = hex;
        }
    });

    return closest;   // may return null
}

Interestingly, Alnitak's suggested was a rather good because a Hexagon is kinda a Circle indeed. This is my function. Compare mousevent position x/y to each Hexagons circle and the Hexagon innate size (width/height). Is mouseposition x/y + hexagon size is close to the hexagon centre, you probably clicked this hexagon. Still test against all Hexagon. If you are "close" to more than one, get a vector distance of mouse course x/y vs all valid hexagons circles. The hexagon with the shortest vector is the one you clicked.

Grid.prototype.getHexAt = function(pos){
    var inRange = [];
    var closest = null;

    for (var hex in grid.hexes) {
        var item = grid.hexes[hex];     
        var center = item.center;

        if (center.x + item.size > pos.x && center.x - item.size < pos.x) {
            if (center.y + item.size > pos.y && center.y - item.size < pos.y) {
                inRange.push(item);
            }
        }
    }   

    if (inRange.length > 1) {
        var pick = null;
        var dist = null;

        for (var i = 0; i < inRange.length; i++) {
            var vector = {
                x: inRange[i].center.x - pos.x,
                y: inRange[i].center.y - pos.y
            };

            if (vector.x < 0) {
                vector.x *= -1;
            }
            if (vector.y < 0) {
                vector.y *= -1;
            }

            if (pick == null || vector.x + vector.y < dist) {
                pick = inRange[i];
                dist = vector.x + vector.y;             
            }
        }

        closest = pick;
    }
    else {
        closest = inRange[0];
    }

    return closest;
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM