All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.mdr.ascii.diagram.parser.AsciiEdgeParser.scala Maven / Gradle / Ivy

package com.github.mdr.ascii.diagram.parser

import scala.annotation.tailrec
import com.github.mdr.ascii.common.Point
import com.github.mdr.ascii.common.Direction
import com.github.mdr.ascii.common.Characters._
import com.github.mdr.ascii.common.Direction._
import scala.PartialFunction.cond

trait AsciiEdgeParser { self: DiagramParser ⇒

  @tailrec
  protected final def followAsciiEdge(points: List[Point], direction: Direction): Option[EdgeImpl] = {
    val currentPoint = points.head
    if (!inDiagram(currentPoint))
      return None
    // println("followAsciiEdge: " + points + ", " + direction + ", " + charAt(currentPoint))
    if (isBoxEdge(currentPoint))
      return {
        if (points.size <= 2)
          None // Filter out spurious "edges" to adjacent boxes
        else
          Some(new EdgeImpl(points.reverse))
      }
    val c = charAt(currentPoint)

    val ahead = currentPoint go direction
    val left = currentPoint go direction.turnLeft
    val right = currentPoint go direction.turnRight
    val aheadIsContinuation = isContinuation(ahead, direction)
    val rightIsContinuation = isContinuation(right, direction.turnRight)
    val leftIsContinuation = isContinuation(left, direction.turnLeft)

    if (isCrossing(c) || isAheadArrow(c, direction))
      followAsciiEdge(currentPoint.go(direction) :: points, direction)
    else if (isLeftTurn(c, direction) && points.size > 2 /* ignore immediate turns */ )
      followAsciiEdge(left :: points, direction.turnLeft)
    else if (isRightTurn(c, direction) && points.size > 2 /* ignore immediate turns */ )
      followAsciiEdge(right :: points, direction.turnRight)
    else if (isStraightAhead(c, direction)) {
      if (aheadIsContinuation)
        followAsciiEdge(ahead :: points, direction)
      else if (leftIsContinuation && !rightIsContinuation && !isTurn(left))
        followAsciiEdge(left :: points, direction.turnLeft)
      else if (!leftIsContinuation && rightIsContinuation && !isTurn(right))
        followAsciiEdge(right :: points, direction.turnRight)
      else
        followAsciiEdge(ahead :: points, direction)
    } else if (isOrthogonal(c, direction)) {
      if (leftIsContinuation && !rightIsContinuation)
        followAsciiEdge(left :: points, direction.turnLeft)
      else if (!leftIsContinuation && rightIsContinuation)
        followAsciiEdge(right :: points, direction.turnRight)
      else
        followAsciiEdge(ahead :: points, direction)
    } else if (isLeftArrow(c, direction))
      followAsciiEdge(left :: points, direction.turnLeft)
    else if (isRightArrow(c, direction))
      followAsciiEdge(right :: points, direction.turnRight)
    else
      None
  }

  private def isContinuation(point: Point, direction: Direction): Boolean =
    isBoxEdge(point) || charAtOpt(point).exists { c ⇒
      isStraightAhead(c, direction) || isCrossing(c) || isAheadArrow(c, direction) ||
        isLeftTurn(c, direction) || isRightTurn(c, direction)
    }

  private def isStraightAhead(c: Char, direction: Direction): Boolean = cond(c, direction) {
    case ('-', Right | Left) ⇒ true
    case ('|', Up | Down)    ⇒ true
  }

  private def isOrthogonal(c: Char, direction: Direction): Boolean = isStraightAhead(c, direction.turnRight)

  private def isCrossing(c: Char): Boolean = c == '+'

  private def isTurn(p: Point): Boolean = charAtOpt(p).exists(isTurn)

  private def isTurn(c: Char): Boolean = c == '\\' || c == '/'

  private def isRightTurn(c: Char, direction: Direction): Boolean = cond(c, direction) {
    case ('\\', Left | Right) ⇒ true
    case ('/', Up | Down)     ⇒ true
  }

  private def isLeftTurn(c: Char, direction: Direction): Boolean = isRightTurn(c, direction.turnRight)

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy