But this is an excellent example of where Shape can have concrete code in it though. is_inside is true iff the shape's bounds are inside the rectangle. So assuming you have a means to test rectangle bounds:
Now you don't need to implement cookie cutter is_inside methods everywhere, but rather the simpler get_bounds(). In fact, you can add is_inside after all your shapes are implemented and it will just work, as long as they have get_bounds.
Maybe you want to add some sanity checking to make sure bounds never have negative width, so you implement get_bounds() in Shape, and implement get_left(), get_right(), get_top(), get_bottom() in the child classes. Now you don't have to add cookie-cutter assert(left < right) stuff everywhere, just in one place.
Other similar ideas: if you write an "intersects(Point)" method on each shape, then you can pull a concrete implementation of sampling-based area approximation up to the base Shape class, and leave analytical area calculation to the children. This is useful if you are working with distance-field, parametric or noisy shapes, for example.
Maybe you want to add some sanity checking to make sure bounds never have negative width, so you implement get_bounds() in Shape, and implement get_left(), get_right(), get_top(), get_bottom() in the child classes. Now you don't have to add cookie-cutter assert(left < right) stuff everywhere, just in one place.
Other similar ideas: if you write an "intersects(Point)" method on each shape, then you can pull a concrete implementation of sampling-based area approximation up to the base Shape class, and leave analytical area calculation to the children. This is useful if you are working with distance-field, parametric or noisy shapes, for example.