getStrokeOutlineTracks function

Map<String, List<Vec>> getStrokeOutlineTracks(
  1. List<StrokePoint> strokePoints, [
  2. StrokeOptions? options
])

Implementation

Map<String, List<Vec>> getStrokeOutlineTracks(
  List<StrokePoint> strokePoints, [
  StrokeOptions? options,
]) {
  options ??= StrokeOptions();
  final double size = options.size;
  final double smoothing = options.smoothing;

  if (strokePoints.isEmpty || size <= 0) {
    return {'left': [], 'right': []};
  }

  final StrokePoint firstStrokePoint = strokePoints[0];
  final StrokePoint lastStrokePoint = strokePoints[strokePoints.length - 1];

  final double totalLength = lastStrokePoint.runningLength;

  final double minDistance = pow(size * smoothing, 2).toDouble();

  final List<Vec> leftPts = [];
  final List<Vec> rightPts = [];

  Vec prevVector = strokePoints[0].vector;

  Vec pl = strokePoints[0].point;
  Vec pr = pl;

  Vec tl = pl;
  Vec tr = pr;

  bool isPrevPointSharpCorner = false;

  for (int i = 0; i < strokePoints.length; i++) {
    final StrokePoint strokePoint = strokePoints[i];
    final Vec point = strokePoint.point;
    final Vec vector = strokePoint.vector;

    final double prevDpr = vector.dpr(prevVector);
    final Vec nextVector =
        (i < strokePoints.length - 1)
            ? strokePoints[i + 1].vector
            : strokePoints[i].vector;

    final double nextDpr =
        i < strokePoints.length - 1 ? nextVector.dpr(vector) : 1;

    final bool isPointSharpCorner = prevDpr < 0 && !isPrevPointSharpCorner;
    final bool isNextPointSharpCorner = nextDpr < 0.2;

    if (isPointSharpCorner || isNextPointSharpCorner) {
      if (nextDpr > -0.62 &&
          totalLength - strokePoint.runningLength > strokePoint.radius) {
        // Draw a "soft" corner
        final Vec offset = prevVector.clone().mul(strokePoint.radius);
        final double cpr = prevVector.clone().cpr(nextVector);

        if (cpr < 0) {
          tl = Vec.addVec(point, offset);
          tr = Vec.subVec(point, offset);
        } else {
          tl = Vec.subVec(point, offset);
          tr = Vec.addVec(point, offset);
        }

        leftPts.add(tl);
        rightPts.add(tr);
      } else {
        // Draw a "sharp" corner
        final Vec offset = prevVector.clone().mul(strokePoint.radius).per();
        final Vec start = Vec.subVec(strokePoint.input, offset);

        for (double step = 1 / 13, t = 0; t < 1; t += step) {
          tl = Vec.rotWithVec(start, strokePoint.input, fixedPi * t);
          leftPts.add(tl);

          tr = Vec.rotWithVec(start, strokePoint.input, fixedPi + fixedPi * -t);
          rightPts.add(tr);
        }
      }

      pl = tl;
      pr = tr;

      if (isNextPointSharpCorner) {
        isPrevPointSharpCorner = true;
      }

      continue;
    }

    isPrevPointSharpCorner = false;

    if (strokePoint == firstStrokePoint || strokePoint == lastStrokePoint) {
      final Vec offset = vector.per().mul(strokePoint.radius);
      leftPts.add(Vec.subVec(point, offset));
      rightPts.add(Vec.addVec(point, offset));

      continue;
    }

    final Vec nextVectorLerped = Vec.lerpVec(nextVector, vector, nextDpr);
    final Vec offset = nextVectorLerped.per().mul(strokePoint.radius);

    tl = Vec.subVec(point, offset);

    if (i <= 1 || Vec.dist2Between(pl, tl) > minDistance) {
      leftPts.add(tl);
      pl = tl;
    }

    tr = Vec.addVec(point, offset);

    if (i <= 1 || Vec.dist2Between(pr, tr) > minDistance) {
      rightPts.add(tr);
      pr = tr;
    }

    prevVector = vector;
  }

  return {'left': leftPts, 'right': rightPts};
}