JSFiddle - React, Tailwind, and code Playground

by duopixel

HTML

<label for="tolerance">Tolerance</label>
<input type="range" min="0" max="100" id="tolerance" value="10" />


<svg width="480" height="320" xmlns="http://www.w3.org/2000/svg" id="stage">
 <!-- Created with Method Draw - http://github.com/duopixel/Method-Draw/ -->
  <path d="m39,96c0,-30.38674 15.66298,-55 35,-55c19.33701,0 35,24.61326 35,55c0,30.38674 -15.66299,55 -35,55c-19.33702,0 -35,-24.61326 -35,-55z" stroke-width="5" stroke="#000" fill="#fff"/>
  <path d="m156,91.5c0,-16.85083 13.64917,-30.5 30.5,-30.5c16.85083,0 30.5,13.64917 30.5,30.5c0,16.85083 -13.64917,30.5 -30.5,30.5c-16.85083,0 -30.5,-13.64917 -30.5,-30.5z" stroke-width="5" stroke="#000" fill="#fff"/>
  <path d="m262,100.5c0,-4.14365 3.35635,-7.5 7.5,-7.5c4.14365,0 7.5,3.35635 7.5,7.5c0,4.14365 -3.35635,7.5 -7.5,7.5c-4.14365,0 -7.5,-3.35635 -7.5,-7.5z" stroke-width="5" stroke="#000" fill="#fff"/>
  <path id="svg_4" d="m350,51c41.01221,0 54,-2.37355 54,46c0,48.37355 -36,36 -58,36c-22,0 4,-82 4,-82z" stroke-width="5" stroke="#000" fill="#fff"/>
  <path id="svg_5" d="m96,194c23,-1 39,21 39,40c0,19 -16,38 -39,39c-23,1 -38,-20 -39,-40c-1,-20 16,-38 39,-39z" stroke-width="5" stroke="#000" fill="#fff"/>
  <path  d="m197,192l66,0l0,66l-66,0l0,-66z" stroke-opacity="null" stroke-width="5" stroke="#000" fill-opacity="null" fill="#fff"/>
    
    
  <path id="svg_8" d="m152,199l-15,-7l-7,-15l0,-20l10,-11l15,-7l13,1l10,5l7,9" stroke-width="5.5" stroke="#000" fill="#fff"/>
  <path stroke="#000" id="svg_12" d="m310.215485,281c-52.982697,-35 -21.363983,-68 15.38208,-27c36.746033,41 32.473236,90 53.837219,34c21.364014,-56 33.32782,-19 33.32782,-19c0,0 -5.12735,44 17.94577,19c23.07309,-25 -0.854584,-59 -0.854584,-59" stroke-width="5.5" fill="none"/>
  <path id="svg_10" d="m246,45c-3,-24 37,-45 56,-17c19,28 -14,58 -40,46" stroke-width="5.5" stroke="#000" fill="#fff"/>
  <path id="svg_11" d="m265,142c19,-24 40,-38 49,-5c9,33 22,53 -17,36c-39,-17 -44,0 -44,0"  stroke-width="5.5" stroke="#000" fill="#fff"/>
</svg>

CSS

body {
  margin: 20px;
}

JavaScript

var svg = document.getElementById("stage")
var xmlns = svg.getAttribute('xmlns');
var tolerance = 10;

var toleranceEl = document.getElementById("tolerance");
toleranceEl.addEventListener("change", findCircles, false);



function findCircles(e){
  if (typeof e==="undefined") var e = {value: 10};
  var paths = document.getElementsByTagName("path");
  var previous_matches = document.querySelectorAll(".match");
 
  _.each(previous_matches, function(match) {svg.removeChild(match)});
  var matches = _.filter(paths, function(path){return getCircleFromPath(path, e.value)});
    console.log(matches);
    _.each(matches, paintMatch);
}
         
function paintMatch(el){
  el.setAttribute("stroke", "#f99")
  el.setAttribute("stroke-width", "2")
  el.setAttribute("fill", "none")
  el.setAttribute("class", "match")
  svg.appendChild(el);
}
         
function getCircleFromPath(path, tolerance) {

  if (path.tagName != "path") return false;
  
  tolerance /= 100;    
  var pathLength = path.getTotalLength();
  var bbox = path.getBBox();
  var cx = bbox.x + bbox.width/2;
  var cy = bbox.y + bbox.height/2;
  var r = (bbox.height + bbox.width)/4;
  tolerance *= r;  
    
  for (var i=0; i< pathLength; i+=5) {
     var pos = path.getPointAtLength(i);
     var distance = Math.sqrt(Math.pow(pos.x - cx, 2) + Math.pow(pos.y - cy, 2));
     if (distance > (r+tolerance) || distance < (r-tolerance)) return false;
  }
  
  var coords = {
      startX: path.getPointAtLength(0).x,
      endX: path.getPointAtLength(pathLength).x, 
      startY: path.getPointAtLength(0).y,
      endY: path.getPointAtLength(pathLength).y
  }
    
  if (coords.startX == coords.endX && coords.startY == coords.endY) {
    return buildElement("circle", {"cx": cx, "cy": cy, "r": r})
  }  
    
  else {
    var startAngle = Math.atan2(coords.startY-cy, coords.startX-cx) * 180 / Math.PI;
    var endAngle = Math.atan2(coords.endY-cy, coords.endX-cx) * 180 / Math.PI;
    return buildElement("path", {"d":...