Problem 472625 · medium · Level 04 Non-Linear Data Structures

Groups, Moves and Zooms

py-composition · py-protocols · composite objects · geometry

A sticker designer builds pictures out of a few simple shapes. A shape answers three questions: contains(x, y) (is the point inside or on the edge?), bbox() (the bounding box (xmin, ymin, xmax, ymax)) and count() (how many basic shapes it is made of).

Write these classes:

  • Box(x, y, w, h): the rectangle with lower-left corner (x, y), width w and height h.
  • Disc(cx, cy, r): the disc with centre (cx, cy) and radius r.
  • Moved(shape, dx, dy): shape shifted by dx to the right and dy up.
  • Scaled(shape, k): shape stretched by the factor k > 0 about the origin (0, 0) (every point (x, y) of the shape goes to (k*x, k*y)).
  • Cut(shape, hole): the points of shape that are not in hole; its bounding box is shape's.
  • Group(shapes): all the points of any shape in the non-empty list shapes.

Box and Disc count as 1; the other classes count the basic shapes inside them. Any of these can be put inside any other: a group of moved, scaled groups is still a shape. The tests also wrap a shape of their own, Point(x, y) from the setup, which has the same three methods.

The setup's helper render(shape, x0, y0, x1, y1) draws a shape on the whole-number points of a window: one string per row from y1 down to y0, with # for points inside.

Examples

Input:  render(Group([Box(0, 0, 4, 1), Moved(Disc(0, 0, 1), 2, 3)]), 0, 0, 4, 4)
Output: ['..#..',
         '.###.',
         '..#..',
         '#####',
         '#####']

Input:  s = Scaled(Cut(Box(0, 0, 4, 4), Disc(2, 2, 1)), 2)
        s.bbox(), s.count(), s.contains(4, 4), s.contains(1, 7)
Output: ((0, 0, 8, 8), 2, False, True)

Constraints

  • Coordinates, sizes and shifts are whole numbers or halves; scale factors are 0.5, 2 or 4.
  • Shapes are nested at most 30 deep; render windows have at most 41 x 41 points.

Goals

  • Build new shapes out of existing ones instead of writing a subclass for every combination
  • Delegate a question to the wrapped object and adjust the answer
  • Make wrappers and groups work with any object that has the right methods
Starting Python…