Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to determine if a coordinate is inside SVG Close Path?

I have designed a Duval's Triangle ( a diagnostic tool ), on SVG, cosisting of Segments (closed Paths) of different colors. The diagnostic result will be a coordinate. Need to detect the resultant coordinate is in which Closed Path ?

Like in the following case the diagnostic result is a RED DOT. Need to detect the close path i.e in this case : D2 enter image description here

like image 896
Fawad Mueed Avatar asked Mar 21 '19 17:03

Fawad Mueed


2 Answers

You have several options:

  • SVG 2 has the isPointInFill() method. Which you can call on each shape to see if a point is within the fill of a path. However I believe only Chrome and Opera have implemented this new feature of SVG2.

    https://developer.mozilla.org/en-US/docs/Web/API/SVGGeometryElement/isPointInFill

  • You can draw your SVG to a Canvas and use the isPointInPath() method.

    https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/isPointInPath

  • You could draw the SVG to a Canvas and use the getImageData() method to get the colour of that particular pixel.

    https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/getImageData

  • And of course you can also do it numerically by calculating it yourself in some way.

like image 190
Paul LeBeau Avatar answered Oct 30 '22 06:10

Paul LeBeau


This is using Paul LeBeau's answer.

1.

First a demo where you check if the point is in a certain path, the #c path in this case. Please read the comments in my code.

// creating a new SVG point
let point = svg.createSVGPoint()
point.x = 300;
point.y = 300;
//checking if the point is in the path c
console.log(c.isPointInFill(point));
svg{border:1px solid}
<svg id="svg" viewBox="0 0 606.731 526.504"  width="200" >
<polygon id="a" fill="#D9D1E0" stroke="#020202" stroke-miterlimit="10" points="300.862,1.001 0.862,526.001 605.862,526.001 "/>
<polygon id="b" fill="#926BB5" stroke="#020202" stroke-miterlimit="10" points="289.576,19.681 442.34,283.546 411.092,343.437 
	515.705,526.001 428.453,525.716 337.314,365.138 385.054,280.945 262.668,66.555 "/>
<polygon id="c" fill="#8ED5ED" stroke="#020202" stroke-miterlimit="10" points="334.4,193.005 384.186,280.946 337.315,364.272 
	428.453,525.716 142.019,525.716 "/>  
<circle cx="300" cy="300" r="5" fill="red" />
</svg>

2.

A second demo where I'm drawing the polygons on a canvas and using the method isPointInPath() of the context: Please read the comments in my code.

let ctx = canv.getContext("2d");
canv.width = 606.731;
canv.height = 526.504;
// the main triangle
let duval = [300.862, 1.001, 0.862, 526.001, 605.862, 526.001];
// the arrays of points for the 2 polygons inside the main triangle
let rys = [
  [
    289.576,
    19.681,
    442.34,
    283.546,
    411.092,
    343.437,
    515.705,
    526.001,
    428.453,
    525.716,
    337.314,
    365.138,
    385.054,
    280.945,
    262.668,
    66.555
  ],
  [
    334.4,
    193.005,
    384.186,
    280.946,
    337.315,
    364.272,
    428.453,
    525.716,
    142.019,
    525.716
  ]
];

// drawing the polygons
drawPoly(duval, "#D9D1E0");
drawPoly(rys[0], "#926BB5");
drawPoly(rys[1], "#8ED5ED");


// the point to check
let p = { x: 300, y: 300 };
drawPoint(p);

// looping through the array of shapes to check if the point is in path
for (let i = 0; i < rys.length; i++) {
  // draw again the polygon without stroking or filling
  drawPoly(rys[i]);
  //chect if the point is in path
  if (ctx.isPointInPath(p.x, p.y)) {
    // do something
    console.log(i);
    // if found break the loop
    break;
  }
}


// a function to draw a polygon from an array
function drawPoly(ry, color) {
  ctx.fillStyle = color;
  ctx.beginPath();
  ctx.moveTo(ry[0], ry[1]);
  for (let i = 2; i < ry.length; i += 2) {
    ctx.lineTo(ry[i], ry[i + 1]);
  }
  ctx.closePath();
  if (color) {
    ctx.fill();
    ctx.stroke();
  }
}

function drawPoint(p) {
  ctx.fillStyle = "red";
  ctx.beginPath();
  ctx.arc(p.x, p.y, 5, 0, 2 * Math.PI);
  ctx.fill();
}
canvas{border:1px solid}
<canvas id="canv"></canvas>

3

This time using the getImageData() method to get the colour of the pixel at the point. The code is similar to the one in the previous example

let ctx = canv.getContext("2d");
canv.width = 606.731;
canv.height = 526.504;

let duval = [300.862, 1.001, 0.862, 526.001, 605.862, 526.001];

let rys = [
  [
    289.576,
    19.681,
    442.34,
    283.546,
    411.092,
    343.437,
    515.705,
    526.001,
    428.453,
    525.716,
    337.314,
    365.138,
    385.054,
    280.945,
    262.668,
    66.555
  ],
  [
    334.4,
    193.005,
    384.186,
    280.946,
    337.315,
    364.272,
    428.453,
    525.716,
    142.019,
    525.716
  ]
];

drawPoly(duval, "#D9D1E0");
drawPoly(rys[0], "#926BB5");
drawPoly(rys[1], "#8ED5ED");



function drawPoly(ry, color) {
  ctx.fillStyle = color;
  ctx.beginPath();
  ctx.moveTo(ry[0], ry[1]);
  for (let i = 2; i < ry.length; i += 2) {
    ctx.lineTo(ry[i], ry[i + 1]);
  }
  ctx.closePath();
  if (color) {
    ctx.fill();
    ctx.stroke();
  }
}


// HERE BEGINS THE IMPORTANT PART

let imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);

let p = { x: 300, y: 300 };
// mark the point with an empty circle
ctx.beginPath();
ctx.arc(p.x,p.y,5,0,2*Math.PI);
ctx.stroke();

// the index of the point p in the imgData.data array
let index = (p.y*imgData.width + p.x)*4;
//the red,green and blue components of the color of the pixel at the index
let r = imgData.data[index];
let g = imgData.data[index + 1];
let b = imgData.data[index + 2];

//test the color
test.style.background = `rgb(${r},${g},${b})`;
canvas{border:1px solid}
#test{width:50px; height:50px; border:1px solid;}
<canvas id="canv"></canvas>
<div id="test"></div>
like image 28
enxaneta Avatar answered Oct 30 '22 07:10

enxaneta