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

moreswing.swing.GridBoundaries.scala Maven / Gradle / Ivy

The newest version!
package moreswing.swing

import scala.swing. { Dimension, Rectangle }

/** Hold rectangles in a grid.
 *
 * @author myst3r10n
 */
trait GridBoundaries {

  /** Minimum bounds size. */
  var minimumBoundsSize = new Dimension(25, 25)

  /** Throw this exception if minimum bounds width exceeded. */
  protected case class MinimumBoundsWidthExceeded(o: Int) extends Exception {

    /** Amount size exceeded. */
    val offset = o

  }

  /** Throw this exception if minimum bounds width is invalid. */
  case object MinimumBoundsWidthInvalid extends Exception

  /** Throw this exception if minimum bounds height exceeded. */
  case class MinimumBoundsHeightExceeded(o: Int) extends Exception {

    /** Amount size exceeded. */
    val offset = o

  }

  /** Throw this exception if minimum bounds height is invalid. */
  case object MinimumBoundsHeightInvalid extends Exception { }

  /** Fit all overlapped bounds and removes empty fields.
   * 
   * @param start The index of the bounds where changed first.
   * @param newBounds The new bounds of the rectangle where changed first.
   * @param boundaries The boundaries without the new changed start bounds.
   *
   * @return The modified rectangles.
   */
  def fitBoundaries(
    start: Int,
    newBounds: Rectangle,
    boundaries: Map[Int, Rectangle]): Map[Int, Rectangle] = {

    // Pass if minimum bounds sizes empty.
    if(minimumBoundsSizes == null) {

      // Pass if minimum bounds width invalid.
      if(minimumBoundsSize.width < 0)
        throw MinimumBoundsWidthInvalid

      // Pass if minimum bounds height invalid.
      else if(minimumBoundsSize.height < 0)
        throw MinimumBoundsHeightInvalid

    // Pass if minimum bounds sizes not empty.
    } else

      // Check whether minimum sizes valid.
      minimumBoundsSizes.foreach { case (i, size) =>

        // Pass if minimum bounds width invalid.
        if(size.width < 0)
          throw MinimumBoundsWidthInvalid

        // Pass if minimum bounds height invalid.
        else if(size.height < 0)
          throw MinimumBoundsHeightInvalid

      }

      // Sync boundaries.
      formerBoundaries = boundaries
      newestBoundaries = boundaries
      newestBoundaries += start -> newBounds

      // Add start bounds to change.
      var changeBoundaries = Seq[Int](start)

      // Mark start bounds as modified.
      var modifiedBoundaries = Map[Int, Rectangle]((changeBoundaries.head, newBounds))

      // Adjustment algorithm.
      while(changeBoundaries.size > 0) try {

        while(changeBoundaries.size > 0) {

          // Pass if left boundary moved.
          if(formerBoundaries(changeBoundaries.head).x !=
            newestBoundaries(changeBoundaries.head).x) {

            // Update all left neighbor boundaries.
            neighbors(changeBoundaries.head, Direction.Left).foreach { neighbor =>

              // New bounds of left neighbor bounds.
              val newNeighborBounds = new Rectangle(
                newestBoundaries(neighbor).x,
                newestBoundaries(neighbor).y,
                newestBoundaries(changeBoundaries.head).x -
                  newestBoundaries(neighbor).x,
                newestBoundaries(neighbor).height)

              // Mark left neighbor bounds as modified.
              modifiedBoundaries += neighbor -> newNeighborBounds

              // Update boundaries with left neighbor bounds.
              newestBoundaries += neighbor -> newNeighborBounds

              // Remember to resize all boundaries around the left neighbor bounds too.
              changeBoundaries = changeBoundaries :+ neighbor

            }

          // Pass if right boundary moved.
          } else if(formerBoundaries(changeBoundaries.head).width !=
            newestBoundaries(changeBoundaries.head).width) {

            // Update right neighbor boundaries.
            neighbors(changeBoundaries.head, Direction.Right).foreach { neighbor =>

              // New bounds of right neighbor bounds.
              val newNeighborBounds = new Rectangle(
                newestBoundaries(changeBoundaries.head).x +
                  newestBoundaries(changeBoundaries.head).width,
                newestBoundaries(neighbor).y,
                newestBoundaries(neighbor).width -
                  ((newestBoundaries(changeBoundaries.head).x +
                  newestBoundaries(changeBoundaries.head).width) -
                  newestBoundaries(neighbor).x),
                newestBoundaries(neighbor).height)

              // Mark right neighbor bounds as modified.
              modifiedBoundaries += neighbor -> newNeighborBounds

              // Update boundaries with right neighbor bounds.
              newestBoundaries += neighbor -> newNeighborBounds

              // Remember to resize all boundaries around the right neighbor bounds too.
              changeBoundaries = changeBoundaries :+ neighbor

            }
          }

          // Pass if top boundary moved.
          if(formerBoundaries(changeBoundaries.head).y !=
            newestBoundaries(changeBoundaries.head).y) {

            // Update top neighbor boundaries.
            neighbors(changeBoundaries.head, Direction.Top).foreach { neighbor =>

              // New bounds of top neighbor bounds.
              val newNeighborBounds = new Rectangle(
                newestBoundaries(neighbor).x,
                newestBoundaries(neighbor).y,
                newestBoundaries(neighbor).width,
                newestBoundaries(changeBoundaries.head).y -
                  newestBoundaries(neighbor).y)

              // Mark top neighbor bounds as modified.
              modifiedBoundaries += neighbor -> newNeighborBounds

              // Update boundaries with top neighbor bounds.
              newestBoundaries += neighbor -> newNeighborBounds

              // Remember to resize all boundaries around the top neighbor bounds too.
              changeBoundaries = changeBoundaries :+ neighbor

            }

          // Pass if bottom boundary moved.
          } else if(formerBoundaries(changeBoundaries.head).height !=
            newestBoundaries(changeBoundaries.head).height) {

            // Update bottom neighbor boundaries.
            neighbors(changeBoundaries.head, Direction.Bottom).foreach { neighbor =>

              // New bounds of bottom neighbor bounds.
              val newNeighborBounds = new Rectangle(
                newestBoundaries(neighbor).x,
                newestBoundaries(changeBoundaries.head).y +
                  newestBoundaries(changeBoundaries.head).height,
                newestBoundaries(neighbor).width,
                newestBoundaries(neighbor).height -
                  ((newestBoundaries(changeBoundaries.head).y +
                  newestBoundaries(changeBoundaries.head).height) -
                  newestBoundaries(neighbor).y))

              // Mark bottom neighbor bounds as modified.
              modifiedBoundaries += neighbor -> newNeighborBounds

              // Update boundaries with bottom neighbor bounds.
              newestBoundaries += neighbor -> newNeighborBounds

              // Remember to resize all boundaries around the bottom neighbor bounds too.
              changeBoundaries = changeBoundaries :+ neighbor

            }
          }

          // Sync former bounds with changed bounds.
          formerBoundaries += changeBoundaries.head -> newestBoundaries(changeBoundaries.head)

          // Mark bounds as changed.
          changeBoundaries = changeBoundaries.drop(1)

        }

      } catch {

        case e: MinimumBoundsWidthExceeded =>

          // Reset changes and add start bounds to change.
          changeBoundaries = Seq[Int](start)

          // Reset former boundaries and sync boundaries again.
          formerBoundaries = boundaries

          // Init patch bounds.
          val patch = new Rectangle(
            newestBoundaries(changeBoundaries.head).x,
            newestBoundaries(changeBoundaries.head).y,
            newestBoundaries(changeBoundaries.head).width,
            newestBoundaries(changeBoundaries.head).height)

          // Patch left boundary of start bounds.
          if(formerBoundaries(changeBoundaries.head).x >
            newestBoundaries(changeBoundaries.head).x) {

            patch.x += e.offset
            patch.width -= e.offset

          // Patch left boundary of start bounds.
          } else if(formerBoundaries(changeBoundaries.head).x <
            newestBoundaries(changeBoundaries.head).x) {

            patch.x -= e.offset
            patch.width += e.offset

          // Patch right boundary of start bounds.
          } else if (formerBoundaries(changeBoundaries.head).width >
            newestBoundaries(changeBoundaries.head).width)
            patch.width += e.offset

          // Patch right boundary of start bounds.
          else if(formerBoundaries(changeBoundaries.head).width <
            newestBoundaries(changeBoundaries.head).width)
            patch.width -= e.offset

          // Reset modified boundaries and mark start bounds as modified.
          modifiedBoundaries = Map[Int, scala.swing.Rectangle]((changeBoundaries.head, patch))

          // Reset newest boundaries and sync boundaries again.
          newestBoundaries = boundaries
          newestBoundaries += changeBoundaries.head -> modifiedBoundaries(changeBoundaries.head)

        case e: MinimumBoundsHeightExceeded =>

          // Reset changes and add start bounds to change.
          changeBoundaries = Seq[Int](start)

          // Reset fromer boundaries and sync boundaries again.
          formerBoundaries = boundaries

          // Init patch bounds.
          val patch = new Rectangle(
            newestBoundaries(changeBoundaries.head).x,
            newestBoundaries(changeBoundaries.head).y,
            newestBoundaries(changeBoundaries.head).width,
            newestBoundaries(changeBoundaries.head).height)

          // Patch top boundary of start bounds.
          if(formerBoundaries(changeBoundaries.head).y >
            newestBoundaries(changeBoundaries.head).y) {

            patch.y += e.offset
            patch.height -= e.offset

          // Patch top boundary of start bounds.
          } else if(formerBoundaries(changeBoundaries.head).y <
            newestBoundaries(changeBoundaries.head).y) {

            patch.y -= e.offset
            patch.height += e.offset

          // Patch bottom boundary of start bounds.
          } else if (formerBoundaries(changeBoundaries.head).height >
            newestBoundaries(changeBoundaries.head).height)
            patch.height += e.offset

          // Patch bottom boundary of start bounds.
          else if(formerBoundaries(changeBoundaries.head).height <
            newestBoundaries(changeBoundaries.head).height)
            patch.height -= e.offset

          // Reset modified boundaries and mark start bounds as modified.
          modifiedBoundaries =
            Map[Int, scala.swing.Rectangle]((changeBoundaries.head, patch))

          // Reset newest boundaries and sync boundaries again.
          newestBoundaries = boundaries
          newestBoundaries +=
            changeBoundaries.head -> modifiedBoundaries(changeBoundaries.head)

      }

    // Removes all minimum boundaries sizes.
    if(minimumBoundsSizes != null)
      minimumBoundsSizes = null

    // Modified boundaries.
    modifiedBoundaries

  }

  /** Fit all overlapped rectangles and removes empty fields.
   * 
   * @param start The index of the rectangle where changed first.
   * @param newBounds The new bounds of the rectangle where changed first.
   * @param rectangles The bounds of rectangles without the new bounds of the first changed.
   * @param minimumBoundsSizes The minimum sizes of boundaries.
   *
   * @return The modified rectangles.
   */
  def fitBoundaries(
    start: Int,
    newBounds: Rectangle,
    boundaries: Map[Int, Rectangle],
    minimumBoundsSizes: Map[Int, Dimension]): Map[Int, Rectangle] = {

    this.minimumBoundsSizes = minimumBoundsSizes
    fitBoundaries(start, newBounds, boundaries)

  }


  /** Direction of the neighbors. */
  protected object Direction extends Enumeration {

    /** Is left neighbor. */
    val Left = Value("Left")

    /** Is right neighbor. */
    val Right = Value("Right")

    /** Is top neighbor. */
    val Top = Value("Top")

    /** Is bottom neighbor. */
    val Bottom = Value("Bottom")

  }

  /** Neighboring boundaries of a central bounds.
   *
   * @param central The central bounds.
   * @param direction The direction of its neighbors.
   */
  protected def neighbors(central: Int, direction: Direction.Value): Seq[Int] = {

    // found neighbor boundaries.
    var found = Seq[Int]()

    // Central bounds.
    val centralBounds = newestBoundaries(central)

    // Central bounds previously.
    val centralBoundsAgo = formerBoundaries(central)

    // Central minimum size.
    val centralMinimumSize = if(minimumBoundsSizes == null) minimumBoundsSize else minimumBoundsSizes(central)

    // Find all neighbors bounds.
    newestBoundaries.foreach { case (neighbor, neighborBounds) =>

      // Direction filter
      direction match {

        // Find left bounds.
        case Direction.Left =>

          // Search criterion for a left neighbor bounds.
          if(!(neighborBounds.y + neighborBounds.height <= centralBounds.y) &&
            neighborBounds.x + neighborBounds.width == centralBoundsAgo.x &&
            !(centralBounds.y + centralBounds.height <= neighborBounds.y)) {

            // If minimum width of left neighbor bounds exceeded, throw exception.
            if(centralBounds.x - neighborBounds.x < centralMinimumSize.width)
              throw MinimumBoundsWidthExceeded((centralMinimumSize.width -
                (centralBounds.x - neighborBounds.x)))

            // Add found left neighbor bounds.
            found = found :+ neighbor

          }

        // Find right bounds.
        case Direction.Right =>

          // Search criterion for a right neighbor bounds.
          if(!(neighborBounds.y + neighborBounds.height <= centralBounds.y) &&
            centralBoundsAgo.x + centralBoundsAgo.width == neighborBounds.x &&
            !(centralBounds.y + centralBounds.height <= neighborBounds.y)) {

            // If minimum width of right neighbor bounds exceeded, throw exception.
            if(neighborBounds.width - ((centralBounds.x + centralBounds.width) -
              neighborBounds.x) < centralMinimumSize.width)
              throw MinimumBoundsWidthExceeded((centralMinimumSize.width -
                (neighborBounds.width -
                ((centralBounds.x + centralBounds.width) - neighborBounds.x))))

            // Add found right neighbor bounds.
            found = found :+ neighbor

          }

        // Find top bounds.
        case Direction.Top =>

          // Search criterion for a top neighbor bounds.
          if(neighborBounds.y + neighborBounds.height == centralBoundsAgo.y &&
             !(neighborBounds.x + neighborBounds.width <= centralBounds.x) &&
             !(centralBounds.x + centralBounds.width <= neighborBounds.x)) {

            // If minimum height of top neighbor bounds exceeded, throw exception.
            if(centralBounds.y - neighborBounds.y < centralMinimumSize.height)
              throw MinimumBoundsHeightExceeded(centralMinimumSize.height - (centralBounds.y - neighborBounds.y))

            // Add found top neighbor bounds.
            found = found :+ neighbor

          }

        // Find bottom bounds.
        case Direction.Bottom =>

          // Search criteria for a bottom neighbor bounds.
          if(!(neighborBounds.x + neighborBounds.width <= centralBounds.x) &&
             !(centralBounds.x + centralBounds.width <= neighborBounds.x) &&
             centralBoundsAgo.y + centralBoundsAgo.height == neighborBounds.y) {

            // If minimum height of bottom neighbor bounds exceeded, throw exception.
            if(neighborBounds.height -
              ((centralBounds.y + centralBounds.height) - neighborBounds.y) < centralMinimumSize.height)
              throw MinimumBoundsHeightExceeded((centralMinimumSize.height -
                (neighborBounds.height - ((centralBounds.y + centralBounds.height) - neighborBounds.y))))

            // Add found bottom neighbor bounds.
            found = found :+ neighbor

          }
      }
    }

    found

  }


  /** Former boundaries. */
  private var formerBoundaries: Map[Int, Rectangle] = null

  /** Newest boundaries. */
  private var newestBoundaries: Map[Int, Rectangle] = null

  /** Minimum bounds sizes. */
  private var minimumBoundsSizes: Map[Int, Dimension] = null

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy