<!DOCTYPE html>
<html>

<head>
  <title>Javascript Clipper Library / Open and closed paths</title>
  <script src="clipper.js"></script>
  <style>
    h3{ margin-bottom:2px}
    body,th,td,input,legend,fieldset,p,b,button,select,textarea {
      font-size: 14px;
      font-family: Arial, Helvetica, sans-serif;
    }
    </style>
  <script>
var subj_lines = [[{"X":34.5,"Y":24},{"X":93,"Y":86},{"X":168,"Y":119},{"X":250,"Y":79},{"X":308,"Y":23}],[{"X":47.5,"Y":196},{"X":55,"Y":170},{"X":68,"Y":152},{"X":84,"Y":133},{"X":103,"Y":118},{"X":124,"Y":107},{"X":150,"Y":97},{"X":168,"Y":95},{"X":189,"Y":96},{"X":215,"Y":101},{"X":232,"Y":109},{"X":251,"Y":119},{"X":269,"Y":134},{"X":285,"Y":151},{"X":296,"Y":167},{"X":303,"Y":185},{"X":305,"Y":196}]];
var clip_polygons = [[{"X":174.37,"Y":190.93},{"X":167.69,"Y":190.88},{"X":154.56,"Y":190.27},{"X":141.76,"Y":189.06},{"X":129.34,"Y":187.3},{"X":117.32,"Y":185},{"X":105.76,"Y":182.19},{"X":94.69,"Y":178.89},{"X":84.15,"Y":175.14},{"X":79.12,"Y":173.09},{"X":15.37,"Y":185.29},{"X":38.27,"Y":147.83},{"X":35.6,"Y":145.29},{"X":30.75,"Y":140.06},{"X":26.52,"Y":134.63},{"X":22.92,"Y":129.02},{"X":19.98,"Y":123.25},{"X":17.74,"Y":117.32},{"X":16.21,"Y":111.26},{"X":15.44,"Y":105.07},{"X":15.37,"Y":101.93},{"X":15.41,"Y":99.63},{"X":15.82,"Y":95.08},{"X":16.63,"Y":90.59},{"X":17.84,"Y":86.18},{"X":20.32,"Y":79.68},{"X":24.96,"Y":71.32},{"X":31,"Y":63.33},{"X":38.34,"Y":55.77},{"X":46.91,"Y":48.66},{"X":56.63,"Y":42.07},{"X":67.42,"Y":36.03},{"X":79.2,"Y":30.59},{"X":91.89,"Y":25.79},{"X":105.41,"Y":21.68},{"X":119.67,"Y":18.3},{"X":134.61,"Y":15.7},{"X":150.14,"Y":13.92},{"X":166.17,"Y":13.01},{"X":174.37,"Y":12.93},{"X":182.57,"Y":13.01},{"X":198.61,"Y":13.92},{"X":214.13,"Y":15.7},{"X":229.07,"Y":18.3},{"X":243.34,"Y":21.68},{"X":256.85,"Y":25.79},{"X":269.54,"Y":30.59},{"X":281.32,"Y":36.03},{"X":292.11,"Y":42.07},{"X":301.83,"Y":48.66},{"X":310.4,"Y":55.76},{"X":317.75,"Y":63.33},{"X":323.78,"Y":71.31},{"X":328.43,"Y":79.67},{"X":330.91,"Y":86.17},{"X":332.11,"Y":90.59},{"X":332.93,"Y":95.08},{"X":333.34,"Y":99.63},{"X":333.37,"Y":101.93},{"X":333.34,"Y":104.23},{"X":332.93,"Y":108.78},{"X":332.11,"Y":113.27},{"X":330.91,"Y":117.69},{"X":328.43,"Y":124.19},{"X":323.78,"Y":132.55},{"X":317.75,"Y":140.54},{"X":310.4,"Y":148.1},{"X":301.83,"Y":155.2},{"X":292.11,"Y":161.8},{"X":281.32,"Y":167.84},{"X":269.54,"Y":173.28},{"X":256.85,"Y":178.08},{"X":243.34,"Y":182.19},{"X":229.07,"Y":185.57},{"X":214.13,"Y":188.17},{"X":198.61,"Y":189.95},{"X":182.57,"Y":190.86}],[{"X":121.74,"Y":155.91},{"X":148.3,"Y":136.11},{"X":101.83,"Y":101.4},{"X":148.3,"Y":66.72},{"X":121.73,"Y":46.88},{"X":48.72,"Y":101.4}],[{"X":228.6,"Y":155.88},{"X":301.63,"Y":101.35},{"X":228.6,"Y":46.88},{"X":202.06,"Y":66.71},{"X":248.54,"Y":101.39},{"X":202.06,"Y":136.06}]];

var scale = 100;
ClipperLib.JS.ScaleUpPaths(subj_lines, scale);
ClipperLib.JS.ScaleUpPaths(clip_polygons, scale);
  
function draw_open_closed(delta, title, combined) {
  cont.innerHTML += "<br><h3>" + title + "</h3>";
  var cpr = new ClipperLib.Clipper();
  cpr.AddPaths(subj_lines, ClipperLib.PolyType.ptSubject, false);
  cpr.AddPaths(clip_polygons, ClipperLib.PolyType.ptClip, true);
  var subject_fillType = ClipperLib.PolyFillType.pftNonZero;
  var clip_fillType = ClipperLib.PolyFillType.pftNonZero;
  var clipTypes = [ClipperLib.ClipType.ctUnion, ClipperLib.ClipType.ctDifference, ClipperLib.ClipType.ctXor, ClipperLib.ClipType.ctIntersection];
  var clipTypesTexts = "Union, Difference, Xor, Intersection";
  var solution_polygons, svg;
  var i;
  for (i = 0; i < clipTypes.length; i++) {
    solution_polytree = new ClipperLib.PolyTree();
    cpr.Execute(clipTypes[i], solution_polytree, subject_fillType, clip_fillType);
    var solution_lines = ClipperLib.Clipper.OpenPathsFromPolyTree(solution_polytree);
    var solution_polygons = ClipperLib.Clipper.ClosedPathsFromPolyTree(solution_polytree);
    if (delta == 0) {
      svg = '<svg width="350" height="222" style="margin-top:10px; margin-right:10px;margin-bottom:10px;background-color:#dddddd">';
      svg += '<path stroke="black" fill="yellow" stroke-width="2" d="' + paths2string(solution_polygons, scale) + '"/>';
      svg += '<path stroke="red" fill="none" stroke-width="2" d="' + lines2string(solution_lines, scale) + '"/>';
      svg += '</svg>';
    }
    else {
      var co = new ClipperLib.ClipperOffset(2, 0.25);
      co.AddPaths(solution_polygons, ClipperLib.JoinType.jtRound, ClipperLib.EndType.etClosedPolygon);
      var solution_polytree = new ClipperLib.PolyTree();
      co.Execute(solution_polytree, delta * scale);
      solution_polygons = ClipperLib.Clipper.PolyTreeToPaths(solution_polytree, solution_polygons);
      if (!combined) co.Clear();
      co.AddPaths(solution_lines,  ClipperLib.JoinType.jtRound, ClipperLib.EndType.etOpenRound);
      var solution_lines_polytree = new ClipperLib.PolyTree();
      co.Execute(solution_lines_polytree, delta * scale);
      solution_lines = ClipperLib.Clipper.PolyTreeToPaths(solution_lines_polytree, solution_lines);
      svg = '<svg width="350" height="222" style="margin-top:10px; margin-right:10px;margin-bottom:10px;background-color:#dddddd">';
      svg += '<path stroke="black" fill="yellow" stroke-width="2" d="' + paths2string(solution_polygons, scale) + '"/>';
      svg += '<path stroke="black" fill="red" stroke-width="2" d="' + paths2string(solution_lines, scale) + '"/>';
      svg += '</svg>';
      //console.log(JSON.stringify(solution_polygons));
      //console.log(JSON.stringify(solution_lines));
    }
    cont.innerHTML += svg;
  }
  cont.innerHTML += "<br>" + clipTypesTexts + "<br>";
}

// Converts Paths to SVG path string
// and scales down the coordinates
function paths2string(paths, scale, isline) {
  var svgpath = "",
    i, j;
  if (!scale) scale = 1;
  for (i = 0; i < paths.length; i++) {
    for (j = 0; j < paths[i].length; j++) {
      if (!j) svgpath += "M";
      else svgpath += "L";
      svgpath += (paths[i][j].X / scale) + ", " + (paths[i][j].Y / scale);
    }
    if (!isline) svgpath += "Z";
  }
  if (svgpath == "") svgpath = "M0,0";
  return svgpath;
}
// converts lines to SVG path string
function lines2string(poly, scale) {
  return paths2string(poly, scale, true);
}
  </script>
</head>

<body onload="cont = document.getElementById('svgcontainer');;
  draw_open_closed(0,'Open and closed, Booleans');
  draw_open_closed(5, 'Open and closed, Positive offset');
  draw_open_closed(5, 'Open and closed, Combined, Positive offset', true);
  draw_open_closed(-5, 'Open and closed, Negative offset')">
  <!--
Boolean test
Offset test
Open/Closed paths test
PolyTree
Area test
Get bounds test

  -->
  <h2>Javascript Clipper Library / Open and closed paths</h2>
  <div id="svgcontainer" style="width:1500px"></div>
</body>

</html>