protected Rectangle randomRectangle(Point nearP) {
    Rectangle bounds = ctx.getWorldBounds();
    if (nearP == null) nearP = randomPointIn(bounds);

    Range xRange = randomRange(rarely() ? 0 : nearP.getX(), Range.xRange(bounds, ctx));
    Range yRange = randomRange(rarely() ? 0 : nearP.getY(), Range.yRange(bounds, ctx));

    return makeNormRect(
        divisible(xRange.getMin()),
        divisible(xRange.getMax()),
        divisible(yRange.getMin()),
        divisible(yRange.getMax()));
  }
  protected Rectangle computeBoundingBox(Collection<? extends Shape> shapes, SpatialContext ctx) {
    Range xRange = null;
    double minY = Double.POSITIVE_INFINITY;
    double maxY = Double.NEGATIVE_INFINITY;
    for (Shape geom : shapes) {
      Rectangle r = geom.getBoundingBox();

      Range xRange2 = Range.xRange(r, ctx);
      if (xRange == null) {
        xRange = xRange2;
      } else {
        xRange = xRange.expandTo(xRange2);
      }
      minY = Math.min(minY, r.getMinY());
      maxY = Math.max(maxY, r.getMaxY());
    }
    return ctx.makeRectangle(xRange.getMin(), xRange.getMax(), minY, maxY);
  }