convert method

dynamic convert()

Implementation

convert() {
  var numPoints = points.length;

  if (numPoints < 2) return 0;

  var isClosed = points[0].equals(points[numPoints - 1]);

  var previousPoint = points[0];
  var nextPoint;

  var strokeWidth2 = style["strokeWidth"] / 2;

  var deltaU = 1 / (numPoints - 1);

  bool innerSideModified = false;
  bool joinIsOnLeftSide = false;
  bool isMiter = false;
  bool initialJoinIsOnLeftSide = false;
  num u1 = 0;

  // Get initial left and right stroke points
  getNormal(points[0], points[1], tempV2_1).multiplyScalar(strokeWidth2);
  lastPointL.copy(points[0]).sub(tempV2_1);
  lastPointR.copy(points[0]).add(tempV2_1);
  point0L.copy(lastPointL);
  point0R.copy(lastPointR);

  for (var iPoint = 1; iPoint < numPoints; iPoint++) {
    currentPoint = points[iPoint];

    // Get next point
    if (iPoint == numPoints - 1) {
      if (isClosed) {
        // Skip duplicated initial point
        nextPoint = points[1];
      } else {
        nextPoint = null;
      }
    } else {
      nextPoint = points[iPoint + 1];
    }

    // Normal of previous segment in tempV2_1
    var normal1 = tempV2_1;
    getNormal(previousPoint, currentPoint, normal1);

    tempV2_3.copy(normal1).multiplyScalar(strokeWidth2);
    currentPointL.copy(currentPoint).sub(tempV2_3);
    currentPointR.copy(currentPoint).add(tempV2_3);

    u1 = u0 + deltaU;

    innerSideModified = false;

    if (nextPoint != null) {
      // Normal of next segment in tempV2_2
      getNormal(currentPoint, nextPoint, tempV2_2);

      tempV2_3.copy(tempV2_2).multiplyScalar(strokeWidth2);
      nextPointL.copy(currentPoint).sub(tempV2_3);
      nextPointR.copy(currentPoint).add(tempV2_3);

      joinIsOnLeftSide = true;
      tempV2_3.subVectors(nextPoint, previousPoint);
      if (normal1.dot(tempV2_3) < 0) {
        joinIsOnLeftSide = false;
      }

      if (iPoint == 1) initialJoinIsOnLeftSide = joinIsOnLeftSide;

      tempV2_3.subVectors(nextPoint, currentPoint);
      tempV2_3.normalize();
      var dot = Math.abs(normal1.dot(tempV2_3));

      // If path is straight, don't create join
      if (dot != 0) {
        // Compute inner and outer segment intersections
        var miterSide = strokeWidth2 / dot;
        tempV2_3.multiplyScalar(-miterSide);
        tempV2_4.subVectors(currentPoint, previousPoint);
        tempV2_5.copy(tempV2_4).setLength(miterSide).add(tempV2_3);
        innerPoint.copy(tempV2_5).negate();
        var miterLength2 = tempV2_5.length();
        var segmentLengthPrev = tempV2_4.length();
        tempV2_4.divideScalar(segmentLengthPrev);
        tempV2_6.subVectors(nextPoint, currentPoint);
        var segmentLengthNext = tempV2_6.length();
        tempV2_6.divideScalar(segmentLengthNext);
        // Check that previous and next segments doesn't overlap with the innerPoint of intersection
        if (tempV2_4.dot(innerPoint) < segmentLengthPrev && tempV2_6.dot(innerPoint) < segmentLengthNext) {
          innerSideModified = true;
        }

        outerPoint.copy(tempV2_5).add(currentPoint);
        innerPoint.add(currentPoint);

        isMiter = false;

        if (innerSideModified) {
          if (joinIsOnLeftSide) {
            nextPointR.copy(innerPoint);
            currentPointR.copy(innerPoint);
          } else {
            nextPointL.copy(innerPoint);
            currentPointL.copy(innerPoint);
          }
        } else {
          // The segment triangles are generated here if there was overlapping

          makeSegmentTriangles(u1);
        }

        switch (style["strokeLineJoin"]) {
          case 'bevel':
            makeSegmentWithBevelJoin(joinIsOnLeftSide, innerSideModified, u1, u1);

            break;

          case 'round':

            // Segment triangles

            createSegmentTrianglesWithMiddleSection(joinIsOnLeftSide, innerSideModified, u1);

            // Join triangles

            if (joinIsOnLeftSide) {
              makeCircularSector(currentPoint, currentPointL, nextPointL, u1, 0);
            } else {
              makeCircularSector(currentPoint, nextPointR, currentPointR, u1, 1);
            }

            break;

          case 'miter':
          case 'miter-clip':
          default:
            var miterFraction = (strokeWidth2 * style["strokeMiterLimit"]) / miterLength2;

            if (miterFraction < 1) {
              // The join miter length exceeds the miter limit

              if (style["strokeLineJoin"] != 'miter-clip') {
                makeSegmentWithBevelJoin(joinIsOnLeftSide, innerSideModified, u1, u1);
                break;
              } else {
                // Segment triangles

                createSegmentTrianglesWithMiddleSection(joinIsOnLeftSide, innerSideModified, u1);

                // Miter-clip join triangles

                if (joinIsOnLeftSide) {
                  tempV2_6.subVectors(outerPoint, currentPointL).multiplyScalar(miterFraction).add(currentPointL);
                  tempV2_7.subVectors(outerPoint, nextPointL).multiplyScalar(miterFraction).add(nextPointL);

                  addVertex(currentPointL, u1, 0);
                  addVertex(tempV2_6, u1, 0);
                  addVertex(currentPoint, u1, 0.5);

                  addVertex(currentPoint, u1, 0.5);
                  addVertex(tempV2_6, u1, 0);
                  addVertex(tempV2_7, u1, 0);

                  addVertex(currentPoint, u1, 0.5);
                  addVertex(tempV2_7, u1, 0);
                  addVertex(nextPointL, u1, 0);
                } else {
                  tempV2_6.subVectors(outerPoint, currentPointR).multiplyScalar(miterFraction).add(currentPointR);
                  tempV2_7.subVectors(outerPoint, nextPointR).multiplyScalar(miterFraction).add(nextPointR);

                  addVertex(currentPointR, u1, 1);
                  addVertex(tempV2_6, u1, 1);
                  addVertex(currentPoint, u1, 0.5);

                  addVertex(currentPoint, u1, 0.5);
                  addVertex(tempV2_6, u1, 1);
                  addVertex(tempV2_7, u1, 1);

                  addVertex(currentPoint, u1, 0.5);
                  addVertex(tempV2_7, u1, 1);
                  addVertex(nextPointR, u1, 1);
                }
              }
            } else {
              // Miter join segment triangles

              if (innerSideModified) {
                // Optimized segment + join triangles

                if (joinIsOnLeftSide) {
                  addVertex(lastPointR, u0, 1);
                  addVertex(lastPointL, u0, 0);
                  addVertex(outerPoint, u1, 0);

                  addVertex(lastPointR, u0, 1);
                  addVertex(outerPoint, u1, 0);
                  addVertex(innerPoint, u1, 1);
                } else {
                  addVertex(lastPointR, u0, 1);
                  addVertex(lastPointL, u0, 0);
                  addVertex(outerPoint, u1, 1);

                  addVertex(lastPointL, u0, 0);
                  addVertex(innerPoint, u1, 0);
                  addVertex(outerPoint, u1, 1);
                }

                if (joinIsOnLeftSide) {
                  nextPointL.copy(outerPoint);
                } else {
                  nextPointR.copy(outerPoint);
                }
              } else {
                // Add extra miter join triangles

                if (joinIsOnLeftSide) {
                  addVertex(currentPointL, u1, 0);
                  addVertex(outerPoint, u1, 0);
                  addVertex(currentPoint, u1, 0.5);

                  addVertex(currentPoint, u1, 0.5);
                  addVertex(outerPoint, u1, 0);
                  addVertex(nextPointL, u1, 0);
                } else {
                  addVertex(currentPointR, u1, 1);
                  addVertex(outerPoint, u1, 1);
                  addVertex(currentPoint, u1, 0.5);

                  addVertex(currentPoint, u1, 0.5);
                  addVertex(outerPoint, u1, 1);
                  addVertex(nextPointR, u1, 1);
                }
              }

              isMiter = true;
            }

            break;
        }
      } else {
        // The segment triangles are generated here when two consecutive points are collinear

        makeSegmentTriangles(u1);
      }
    } else {
      // The segment triangles are generated here if it is the ending segment

      makeSegmentTriangles(u1);
    }

    if (!isClosed && iPoint == numPoints - 1) {
      // Start line endcap
      addCapGeometry(points[0], point0L, point0R, joinIsOnLeftSide, true, u0);
    }

    // Increment loop variables

    u0 = u1;

    previousPoint = currentPoint;

    lastPointL.copy(nextPointL);
    lastPointR.copy(nextPointR);
  }

  if (!isClosed) {
    // Ending line endcap
    addCapGeometry(currentPoint, currentPointL, currentPointR, joinIsOnLeftSide, false, u1);
  } else if (innerSideModified) {
    // Modify path first segment vertices to adjust to the segments inner and outer intersections

    var lastOuter = outerPoint;
    var lastInner = innerPoint;

    if (initialJoinIsOnLeftSide != joinIsOnLeftSide) {
      lastOuter = innerPoint;
      lastInner = outerPoint;
    }

    if (joinIsOnLeftSide) {
      if (isMiter || initialJoinIsOnLeftSide) {
        lastInner.toArray(vertices, 0 * 3);
        lastInner.toArray(vertices, 3 * 3);

        if (isMiter) {
          lastOuter.toArray(vertices, 1 * 3);
        }
      }
    } else {
      if (isMiter || !initialJoinIsOnLeftSide) {
        lastInner.toArray(vertices, 1 * 3);
        lastInner.toArray(vertices, 3 * 3);

        if (isMiter) {
          lastOuter.toArray(vertices, 0 * 3);
        }
      }
    }
  }

  return numVertices;

  // -- End of algorithm
}