// // simple ascii-art program (mostly for testing bounding-box function) // // preliminary version, with some additions: // prints a "frame" around the drawing area // draws each rectangle with a different character // draws bounding box (outline) // import scala.io.StdIn._ // 2D array of Char to use as display area val Rows = 12 val Columns = 40 val screen = Array.fill(Rows,Columns)(' ') // list of rectangles, in reverse order of addition var rectangles = List[(Int,Int,Int,Int)]() // main processing loop -- prompt, get command, and do what user says val prompt = """enter one of the following: r for new rectangle d to display q to quit""" var in = getInput() while (in != "q") { if (in == "r") { newRectangle() } else if (in == "d") { display() } else { println("error!") } in = getInput() } // prompt for and get next "command" def getInput() : String = { println() println(prompt) readLine } // prompt for and get information for rectangle, and add to "rectangles" list def newRectangle() { println("enter coordinate of top left, then width and height") val y = readInt val x = readInt val w = readInt val h = readInt // FIXME? check that values are non-negative and not too big for display? rectangles = (y,x,w,h) :: rectangles } // "draw" rectangles, etc. def display() { // add rectangles to display (reversing order) // "zipWithIndex" adds to each element an index, which we // will use to display each rectangle with a different character rectangles.reverse.zipWithIndex.foreach(pair => { val (rect, index) = pair val ch = ('a'+(index % 26)).toChar println("filling " + rect + " with " + ch) fillRectangle(rect, ch) }) // compute bounding box if (!rectangles.isEmpty) { var bb = rectangles.head rectangles.tail.foreach(r => { bb = bbox(bb, r) } ) println("bounding box " + bb) drawRectangle(bb, '*') } else { println("no bounding box since no rectangles") } // print display (with an enclosing "frame") val horizontalEdge = "+" + "-"*Columns + "+" println(horizontalEdge) for (r <- 0 until Rows) { print("|") for (c <-0 until Columns) { print(screen(r)(c)) } print("|") println() } println(horizontalEdge) } // add one rectangle to display def fillRectangle(rect:(Int,Int,Int,Int), ch:Char) { val (y,x,w,h) = rect for (r <- x until x+h) { for (c <- y until y+w) { screen(r)(c) = ch } } } // add outline of one rectangle to display def drawRectangle(rect:(Int,Int,Int,Int), ch:Char) { val (y,x,w,h) = rect // top and bottom edges for (c <- y until y+w) { screen(x)(c) = ch screen(x+h-1)(c) = ch } // left and right edges for (r <- x until x+h) { screen(r)(y) = ch screen(r)(y+w-1) = ch } } // // function to compute bounding box of two rectangles // (copy-and-paste from bbox.scala) // // rectangles are represented using the "graphics convention": // (y, x, width, height) // where (y, x) is the top left corner in the graphics coordinate system, // in which the y axis is horizontal and runs left to right // and the x axis is vertical and runs top to bottom // def bbox(rect1:(Int,Int,Int,Int), rect2:(Int,Int,Int,Int)) : (Int,Int,Int,Int) = { // pull out components of the two input rectangles val (y1, x1, w1, h1) = rect1 val (y2, x2, w2, h2) = rect2 // find top left corner of bounding box val y = y1 min y2 val x = x1 min x2 // find bottom right corner of bounding box val yBR = (y1 + w1) max (y2 + w2) val xBR = (x1 + h1) max (x2 + h2) // find width, height val w = yBR - y val h = xBR - x // return bounding box as rectangle (y, x, w, h) }