JSFiddle - React, Tailwind, and code Playground

CSS

rect {
  fill: none;
  pointer-events: all;
}

.hull {
  fill: steelblue;
  stroke: steelblue;
  stroke-width: 1px;
  stroke-linejoin: round;
}

circle {
  fill: white;
  stroke: black;
  stroke-width: 1.5px;
}

JavaScript

var width = 960,
    height = 500;

var randomX = d3.random.normal(width / 2, 60),
    randomY = d3.random.normal(height / 2, 60),
    vertices = d3.range(100).map(function() { return [randomX(), randomY()]; });

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

svg.append("rect")
    .attr("width", width)
    .attr("height", height);

var hull = svg.append("path")
    .attr("class", "hull");

var circle = svg.selectAll("circle");

redraw();

function redraw() {
  hull.datum(d3.geom.hull(vertices)).attr("d", function(d) { return "M" + d.join("L") + "Z"; });
  circle = circle.data(vertices);
  circle.enter().append("circle").attr("r", 3);
  circle.attr("transform", function(d) { return "translate(" + d + ")"; });
}

function isInside(point) {
    var c = svg.insert("circle", "path.hull")
        .attr("r", 1)
        .attr("cx", point[0])
        .attr("cy", point[1]);
    
    var bounds = c.node().getBoundingClientRect();
    
    var atPoint = document.elementFromPoint(bounds.left, bounds.top);
    var inside = atPoint == c.node() ? false : true;
    
    c.remove();
    
    return inside;
}

var testPoint1 = [1,1]; // outside
var testPoint2 = [width/2, height/2]; // inside
var testPoint3 = hull.datum()[0]; // on edge

console.log(isInside(testPoint1));
console.log(isInside(testPoint2));
console.log(isInside(testPoint3));