The basic algorithm would go like this:
shapes.tail.foldLeft(boundingBox(shapes.head)) {
case (box, shape) if box contains shape => box
case (box, shape) if shape contains box => shape
case (box, shape) => boxBounding(box, shape)
}
Now you have to write contains
and boxBounding
, which is a pure algorithms problem more than a language problem.
If the shapes all had the same center, implementing contains
would be easier. It would go like this:
abstract class Shape { def contains(s: Shape): Boolean }
case class Rectangle(width: Int, height: Int) extends Shape {
def contains(s: Shape): Boolean = s match {
case Rectangle(w2, h2) => width >= w2 && height >= h2
case Location(x, y, s) => // not the same center
case Circle(radius) => width >= radius && height >= radius
case Group(shapes @ _*) => shapes.forall(this.contains(_))
}
}
case class Location(x: Int, y: Int, shape: Shape) extends Shape {
def contains(s: Shape): Boolean = // not the same center
}
case class Circle(radius: Int) extends Shape {
def contains(s: Shape): Boolean = s match {
case Rectangle(width, height) => radius >= width && radius >= height
case Location(x, y) => // not the same center
case Circle(r2) => radius >= r2
case Group(shapes @ _*) => shapes.forall(this.contains(_))
}
}
case class Group(shapes: Shape*) extends Shape {
def contains(s: Shape): Boolean = shapes.exists(_ contains s)
}
As for boxBounding
, which takes two shapes and combine them, it will usually be a rectangle, but can be a circle under certain circunstances. Anyway, it is pretty straight-forward, once you have the algorithm figured out.