toShapes method

List<Shape> toShapes(
  1. bool isCCW,
  2. bool noHoles
)

Implementation

List<Shape> toShapes(bool isCCW, bool noHoles) {
  toShapesNoHoles(inSubpaths) {
    List<Shape> shapes = [];

    for (var i = 0, l = inSubpaths.length; i < l; i++) {
      var tmpPath = inSubpaths[i];

      var tmpShape = Shape(null);
      tmpShape.curves = tmpPath.curves;

      shapes.add(tmpShape);
    }

    return shapes;
  }

  isPointInsidePolygon(inPt, inPolygon) {
    var polyLen = inPolygon.length;

    // inPt on polygon contour => immediate success    or
    // toggling of inside/outside at every single! intersection point of an edge
    //  with the horizontal line through inPt, left of inPt
    //  not counting lowerY endpoints of edges and whole edges on that line
    var inside = false;
    for (var p = polyLen - 1, q = 0; q < polyLen; p = q++) {
      var edgeLowPt = inPolygon[p];
      var edgeHighPt = inPolygon[q];

      var edgeDx = edgeHighPt.x - edgeLowPt.x;
      var edgeDy = edgeHighPt.y - edgeLowPt.y;

      if (Math.abs(edgeDy) > Math.epsilon) {
        // not parallel
        if (edgeDy < 0) {
          edgeLowPt = inPolygon[q];
          edgeDx = -edgeDx;
          edgeHighPt = inPolygon[p];
          edgeDy = -edgeDy;
        }

        if ((inPt.y < edgeLowPt.y) || (inPt.y > edgeHighPt.y)) continue;

        if (inPt.y == edgeLowPt.y) {
          if (inPt.x == edgeLowPt.x) return true; // inPt is on contour ?
          // continue;				// no intersection or edgeLowPt => doesn't count !!!

        } else {
          var perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y);
          if (perpEdge == 0) return true; // inPt is on contour ?
          if (perpEdge < 0) continue;
          inside = !inside; // true intersection left of inPt

        }
      } else {
        // parallel or collinear
        if (inPt.y != edgeLowPt.y) continue; // parallel
        // edge lies on the same horizontal line as inPt
        if (((edgeHighPt.x <= inPt.x) && (inPt.x <= edgeLowPt.x)) ||
            ((edgeLowPt.x <= inPt.x) && (inPt.x <= edgeHighPt.x))) {
          return true;
        } // inPt: Point on contour !
        // continue;

      }
    }

    return inside;
  }

  var isClockWise = ShapeUtils.isClockWise;

  var subPaths = this.subPaths;
  if (subPaths.isEmpty) return [];

  if (noHoles == true) return toShapesNoHoles(subPaths);

  var solid, tmpPath, tmpShape;
  List<Shape> shapes = [];

  if (subPaths.length == 1) {
    tmpPath = subPaths[0];
    tmpShape = Shape(null);
    tmpShape.curves = tmpPath.curves;
    shapes.add(tmpShape);
    return shapes;
  }

  var holesFirst = !isClockWise(subPaths[0].getPoints());
  holesFirst = isCCW ? !holesFirst : holesFirst;

  // console.log("Holes first", holesFirst);

  var betterShapeHoles = [];
  var newShapes = [];
  var newShapeHoles = [];
  var mainIdx = 0;
  var tmpPoints;

  // newShapes[ mainIdx ] = null;
  listSetter(newShapes, mainIdx, null);

  // newShapeHoles[ mainIdx ] = [];
  listSetter(newShapeHoles, mainIdx, []);

  for (var i = 0, l = subPaths.length; i < l; i++) {
    tmpPath = subPaths[i];
    tmpPoints = tmpPath.getPoints();
    solid = isClockWise(tmpPoints);
    solid = isCCW ? !solid : solid;

    if (solid) {
      if ((!holesFirst) && (newShapes[mainIdx] != null)) mainIdx++;

      // newShapes[ mainIdx ] = { "s": Shape(null), "p": tmpPoints };
      listSetter(newShapes, mainIdx, {"s": Shape(null), "p": tmpPoints});

      newShapes[mainIdx]["s"].curves = tmpPath.curves;

      if (holesFirst) mainIdx++;
      // newShapeHoles[ mainIdx ] = [];
      listSetter(newShapeHoles, mainIdx, []);

      //console.log('cw', i);

    } else {
      newShapeHoles[mainIdx].add({"h": tmpPath, "p": tmpPoints[0]});

      //console.log('ccw', i);

    }
  }

  // only Holes? -> probably all Shapes with wrong orientation
  if (newShapes.isEmpty || newShapes[0] == null) {
    return toShapesNoHoles(subPaths);
  }

  if (newShapes.length > 1) {
    var ambiguous = false;
    var toChange = 0;

    for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) {
      // betterShapeHoles[ sIdx ] = [];
      listSetter(betterShapeHoles, sIdx, []);
    }

    for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) {
      var sho = newShapeHoles[sIdx];

      for (var hIdx = 0; hIdx < sho.length; hIdx++) {
        var ho = sho[hIdx];
        var holeUnassigned = true;

        for (var s2Idx = 0; s2Idx < newShapes.length; s2Idx++) {
          if (isPointInsidePolygon(ho["p"], newShapes[s2Idx]["p"])) {
            if (sIdx != s2Idx) toChange++;
            if (holeUnassigned) {
              holeUnassigned = false;
              betterShapeHoles[s2Idx].add(ho);
            } else {
              ambiguous = true;
            }
          }
        }

        if (holeUnassigned) {
          betterShapeHoles[sIdx].add(ho);
        }
      }
    }
    if (toChange > 0 && ambiguous == false) {
      newShapeHoles = betterShapeHoles;
    }
  }

  var tmpHoles;

  for (var i = 0, il = newShapes.length; i < il; i++) {
    tmpShape = newShapes[i]["s"];
    shapes.add(tmpShape);
    tmpHoles = newShapeHoles[i];

    for (var j = 0, jl = tmpHoles.length; j < jl; j++) {
      tmpShape.holes.add(tmpHoles[j]["h"]);
    }
  }

  //console.log("shape", shapes);

  return shapes;
}