ReCreateJS/txtjs

View on GitHub
src/Path.ts

Summary

Maintainability
F
2 wks
Test Coverage
F
58%
export enum PathFit {
  Rainbow,
  Stairstep
}

export interface PathPoint {
  x: number;
  y: number;
  rotation?: number;
  offsetX?: number;
}

export enum PathAlign {
  Center,
  Right,
  Left
}

export default class Path {
  private pathElement: SVGPathElement = null;
  path: string = null;
  start = 0;
  center: number = null;
  end: number = null;
  angles: any[] = null;
  flipped = false;
  fit: PathFit = PathFit.Rainbow;
  align: PathAlign = PathAlign.Center;
  length: number = null;
  realLength: number = null;
  closed = false;
  clockwise = true;

  constructor(
    path: string,
    start = 0,
    end: number = null,
    flipped = false,
    fit: PathFit = PathFit.Rainbow,
    align: PathAlign = PathAlign.Center
  ) {
    this.path = path;
    this.start = start;
    this.align = align;
    this.end = end;
    this.flipped = flipped;
    this.fit = fit;
    this.update();
  }

  update() {
    this.pathElement = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "path"
    ) as SVGPathElement;
    this.pathElement.setAttributeNS(null, "d", this.path);
    this.length = this.pathElement.getTotalLength();
    this.closed = this.path.toLowerCase().indexOf("z") != -1;
    const pointlength = this.length / 10;
    const points = [];

    points.push(this.getRealPathPoint(0));
    points.push(this.getRealPathPoint(pointlength));
    points.push(this.getRealPathPoint(pointlength * 2));
    points.push(this.getRealPathPoint(pointlength * 3));
    points.push(this.getRealPathPoint(pointlength * 4));
    points.push(this.getRealPathPoint(pointlength * 5));
    points.push(this.getRealPathPoint(pointlength * 6));
    points.push(this.getRealPathPoint(pointlength * 7));
    points.push(this.getRealPathPoint(pointlength * 8));
    points.push(this.getRealPathPoint(pointlength * 9));
    points.push(this.getRealPathPoint(pointlength * 10));

    const clock =
      (points[1].x - points[0].x) * (points[1].y + points[0].y) +
      (points[2].x - points[1].x) * (points[2].y + points[1].y) +
      (points[3].x - points[2].x) * (points[3].y + points[2].y) +
      (points[4].x - points[3].x) * (points[4].y + points[3].y) +
      (points[5].x - points[4].x) * (points[5].y + points[4].y) +
      (points[6].x - points[5].x) * (points[6].y + points[5].y) +
      (points[7].x - points[6].x) * (points[7].y + points[6].y) +
      (points[8].x - points[7].x) * (points[8].y + points[7].y) +
      (points[9].x - points[8].x) * (points[9].y + points[8].y) +
      (points[10].x - points[9].x) * (points[10].y + points[9].y);
    if (clock > 0) {
      this.clockwise = false;
    } else {
      this.clockwise = true;
    }

    if (this.end == null) {
      this.end = this.length;
    }
    if (this.closed == false) {
      if (this.flipped == false) {
        if (this.start > this.end) {
          this.realLength = this.start - this.end;
          this.center = this.start - this.realLength / 2;
        } else {
          this.realLength = this.end - this.start;
          this.center = this.start + this.realLength / 2;
        }
      } else {
        if (this.start > this.end) {
          this.realLength = this.start - this.end;
          this.center = this.start - this.realLength / 2;
        } else {
          this.realLength = this.end - this.start;
          this.center = this.start + this.realLength / 2;
        }
      }
    } else if (this.clockwise == false) {
      if (this.flipped == false) {
        if (this.start > this.end) {
          this.realLength = this.start - this.end;
          this.center = this.end + this.realLength / 2;
        } else {
          this.realLength = this.start + this.length - this.end;
          this.center = this.end + this.realLength / 2;
          if (this.center > this.length) {
            this.center = this.center - this.length;
          }
        }
      } else {
        if (this.start > this.end) {
          this.realLength = this.end + this.length - this.start;
          this.center = this.start + this.realLength / 2;
          if (this.center > this.length) {
            this.center = this.center - this.length;
          }
        } else {
          this.realLength = this.end - this.start;
          this.center = this.start + this.realLength / 2;
        }
      }
    } else {
      if (this.flipped == false) {
        if (this.start > this.end) {
          this.realLength = this.end + this.length - this.start;
          this.center = this.start + this.realLength / 2;
          if (this.center > this.length) {
            this.center = this.center - this.length;
          }
        } else {
          this.realLength = this.end - this.start;
          this.center = this.start + this.realLength / 2;
        }
      } else {
        if (this.start > this.end) {
          this.realLength = this.start - this.end;
          this.center = this.end + this.realLength / 2;
        } else {
          this.realLength = this.start + this.length - this.end;
          this.center = this.end + this.realLength / 2;
          if (this.center > this.length) {
            this.center = this.center - this.length;
          }
        }
      }
    }
  }

  getRealPathPoint(distance: number): PathPoint {
    if (distance > this.length) {
      return this.pathElement.getPointAtLength(distance - this.length);
    } else if (distance < 0) {
      return this.pathElement.getPointAtLength(distance + this.length);
    } else {
      return this.pathElement.getPointAtLength(distance);
    }
  }

  getPathPoint(
    distance: number,
    characterLength = 0,
    charOffset = 0
  ): PathPoint {
    distance = distance * 0.99;
    characterLength = characterLength * 0.99;

    let point1: PathPoint;
    let point2: PathPoint;
    let position: number;
    let direction = true;
    let realStart = 0;

    if (this.closed == false) {
      if (this.flipped == false) {
        if (this.start > this.end) {
          if (this.align == PathAlign.Left) {
            realStart = this.start;
          } else if (this.align == PathAlign.Center) {
            realStart = this.start - (this.realLength - characterLength) / 2;
          } else if (this.align == PathAlign.Right) {
            realStart = this.start - this.realLength - characterLength;
          }
          position = realStart - distance;
          direction = false;
        } else {
          if (this.align == PathAlign.Left) {
            realStart = this.start;
          } else if (this.align == PathAlign.Center) {
            realStart = this.start + (this.realLength - characterLength) / 2;
          } else if (this.align == PathAlign.Right) {
            realStart = this.start + this.realLength - characterLength;
          }
          position = realStart + distance;
        }
      } else {
        if (this.start > this.end) {
          if (this.align == PathAlign.Left) {
            realStart = this.start;
          } else if (this.align == PathAlign.Center) {
            realStart = this.start - (this.realLength - characterLength) / 2;
          } else if (this.align == PathAlign.Right) {
            realStart = this.start - this.realLength - characterLength;
          }
          position = realStart - distance;
          direction = false;
        } else {
          if (this.align == PathAlign.Left) {
            realStart = this.start;
          } else if (this.align == PathAlign.Center) {
            realStart = this.start + (this.realLength - characterLength) / 2;
          } else if (this.align == PathAlign.Right) {
            realStart = this.start + this.realLength - characterLength;
          }
          position = realStart - distance;
        }
      }
    } else if (this.clockwise == false) {
      if (this.flipped == false) {
        if (this.start > this.end) {
          if (this.align == PathAlign.Left) {
            realStart = this.start;
          } else if (this.align == PathAlign.Center) {
            realStart = this.start - (this.realLength - characterLength) / 2;
          } else if (this.align == PathAlign.Right) {
            realStart = this.start - this.realLength - characterLength;
          }

          position = realStart - distance;
          direction = false;
        } else {
          if (this.align == PathAlign.Left) {
            realStart = this.start;
            position = realStart - distance;
          } else if (this.align == PathAlign.Center) {
            realStart = this.start - (this.realLength - characterLength) / 2;
            position = realStart - distance;
          } else if (this.align == PathAlign.Right) {
            realStart = this.start - this.realLength - characterLength;
            position = realStart - distance;
          }

          if (position < 0) {
            position = position + this.length;
          }
          direction = false;
        }
      } else {
        if (this.start > this.end) {
          if (this.align == PathAlign.Left) {
            realStart = this.start;
            position = realStart + distance;
          } else if (this.align == PathAlign.Center) {
            realStart = this.start + (this.realLength - characterLength) / 2;
            position = realStart + distance;
          } else if (this.align == PathAlign.Right) {
            realStart = this.start + this.realLength - characterLength;
            position = realStart + distance;
          }

          if (position > this.length) {
            position = position - this.length;
          }
        } else {
          if (this.align == PathAlign.Left) {
            realStart = this.start;
          } else if (this.align == PathAlign.Center) {
            realStart = this.start + (this.realLength - characterLength) / 2;
          } else if (this.align == PathAlign.Right) {
            realStart = this.start + this.realLength - characterLength;
          }
          position = realStart + distance;
        }
      }
    } else {
      if (this.flipped == false) {
        if (this.start > this.end) {
          if (this.align == PathAlign.Left) {
            realStart = this.start;
            position = realStart - distance;
          } else if (this.align == PathAlign.Center) {
            realStart = this.start - (this.realLength - characterLength) / 2;
            position = realStart - distance;
          } else if (this.align == PathAlign.Right) {
            realStart = this.start - this.realLength - characterLength;
            position = realStart - distance;
          }

          if (position < 0) {
            position = position + this.length;
          }
          direction = false;
        } else {
          if (this.align == PathAlign.Left) {
            realStart = this.start;
          } else if (this.align == PathAlign.Center) {
            realStart = this.start - (this.realLength - characterLength) / 2;
          } else if (this.align == PathAlign.Right) {
            realStart = this.start - this.realLength - characterLength;
          }
          position = realStart - distance;
          direction = false;
        }
      } else {
        if (this.start > this.end) {
          if (this.align == PathAlign.Left) {
            realStart = this.start;
          } else if (this.align == PathAlign.Center) {
            realStart = this.start + (this.realLength - characterLength) / 2;
          } else if (this.align == PathAlign.Right) {
            realStart = this.start + this.realLength - characterLength;
          }
          position = realStart + distance;
        } else {
          if (this.align == PathAlign.Left) {
            realStart = this.start;
            position = realStart + distance;
          } else if (this.align == PathAlign.Center) {
            realStart = this.start + (this.realLength - characterLength) / 2;
            position = realStart + distance;
          } else if (this.align == PathAlign.Right) {
            realStart = this.start + this.realLength - characterLength;
            position = realStart + distance;
          }

          if (position > this.length) {
            position = position - this.length;
          }
        }
      }
    }

    point1 = this.getRealPathPoint(position);
    const segment = this.pathElement.pathSegList.getItem(
      this.pathElement.getPathSegAtLength(position)
    ).pathSegType;

    if (
      segment == 4 &&
      !direction &&
      this.pathElement.getPathSegAtLength(position) !=
        this.pathElement.getPathSegAtLength(position - charOffset)
    ) {
      const pp0 = this.getRealPathPoint(position);
      const pp1 = this.getRealPathPoint(position - charOffset);
      const ppc = this.pathElement.pathSegList.getItem(
        this.pathElement.getPathSegAtLength(position) - 1
      );
      const d0 = Math.sqrt(
        Math.pow(pp0.x - ppc["x"], 2) + Math.pow(pp0.y - ppc["y"], 2)
      );

      const d1 = Math.sqrt(
        Math.pow(pp1.x - ppc["x"], 2) + Math.pow(pp1.y - ppc["y"], 2)
      );

      if (d0 > d1) {
        point1 = pp0;
        point2 = { x: ppc["x"], y: ppc["y"] };

        let rot12 =
          (Math.atan((point2.y - point1.y) / (point2.x - point1.x)) * 180) /
          Math.PI;
        if (point1.x > point2.x) {
          rot12 = rot12 + 180;
        }

        if (rot12 < 0) {
          rot12 = rot12 + 360;
        }
        if (rot12 > 360) {
          rot12 = rot12 - 360;
        }

        point1.rotation = rot12;
        return point1;
      } else {
        point1 = { x: ppc["x"], y: ppc["y"] };
        point1.offsetX = -d0;
        point1["next"] = true;
        return point1;
      }
    }

    if (direction) {
      point2 = this.getRealPathPoint(position + charOffset);
    } else {
      point2 = this.getRealPathPoint(position - charOffset);
    }

    let rot12 =
      (Math.atan((point2.y - point1.y) / (point2.x - point1.x)) * 180) /
      Math.PI;

    if (point1.x > point2.x) {
      rot12 = rot12 + 180;
    }

    if (rot12 < 0) {
      rot12 = rot12 + 360;
    }
    if (rot12 > 360) {
      rot12 = rot12 - 360;
    }

    point1.rotation = rot12;
    return point1;
  }
}