docs: public void put(double w, double h, T data) {
      if(LOG.isDebuggingFinest()) {
        LOG.finest("Add: " + w + "x" + h);
      }
      final int cols = widths.size();
      final int rows = heights.size();
  
      int bestsx = -1;
      int bestsy = -1;
      int bestex = cols - 1;
      int bestey = -1;
      double bestwi;
      double besthi;
      double bestinc;
      // Baseline: grow by adding to the top or to the right.
      {
        double i1 = computeIncreaseArea(w, Math.max(0, h - theight));
        double i2 = computeIncreaseArea(Math.max(0, w - twidth), h);
        if(i1 < i2) {
          bestwi = w;
          besthi = Math.max(0, h - theight);
          bestinc = i1;
        }
        else {
          bestwi = Math.max(0, w - twidth);
          besthi = h;
          bestinc = i2;
        }
      }
      // Find position with minimum increase
      for(int sy = 0; sy < rows; sy++) {
        for(int sx = 0; sx < cols; sx++) {
          if(usage.get(sy).get(sx) != null) {
            continue;
          }
          // Start with single cell
          double avw = widths.get(sx);
          double avh = heights.get(sy);
          int ex = sx;
          int ey = sy;
          while(avw < w || avh < h) {
            // Grow width first
            if(avw / avh < w / h) {
              if(avw < w && ex + 1 < cols) {
                boolean ok = true;
                // All unused?
                for(int y = sy; y <= ey; y++) {
                  if(usage.get(y).get(ex + 1) != null) {
                    ok = false;
                  }
                }
                if(ok) {
                  ex += 1;
                  avw += widths.get(ex);
                  continue;
                }
              }
              if(avh < h && ey + 1 < rows) {
                boolean ok = true;
                // All unused?
                for(int x = sx; x <= ex; x++) {
                  if(usage.get(ey + 1).get(x) != null) {
                    ok = false;
                  }
                }
                if(ok) {
                  ey += 1;
                  avh += heights.get(ey);
                  continue;
                }
              }
            }
            else { // Grow height first
              if(avh < h && ey + 1 < rows) {
                boolean ok = true;
                // All unused?
                for(int x = sx; x <= ex; x++) {
                  if(usage.get(ey + 1).get(x) != null) {
                    ok = false;
                  }
                }
                if(ok) {
                  ey += 1;
                  avh += heights.get(ey);
                  continue;
                }
              }
              if(avw < w && ex + 1 < cols) {
                boolean ok = true;
                // All unused?
                for(int y = sy; y <= ey; y++) {
                  if(usage.get(y).get(ex + 1) != null) {
                    ok = false;
                  }
                }
                if(ok) {
                  ex += 1;
                  avw += widths.get(ex);
                  continue;
                }
              }
            }
            break;
          }
          // Good match, or extension possible?
          if(avw < w && ex < cols - 1) {
            continue;
          }
          if(avh < h && ey < rows - 1) {
            continue;
          }
          // Compute increase:
          double winc = Math.max(0.0, w - avw);
          double hinc = Math.max(0.0, h - avh);
          double inc = computeIncreaseArea(winc, hinc);
  
          if(LOG.isDebuggingFinest()) {
            LOG.debugFinest("Candidate: " + sx + "," + sy + " - " + ex + "," + ey + ": " + avw + "x" + avh + " " + inc);
          }
          if(inc < bestinc) {
            bestinc = inc;
            bestsx = sx;
            bestsy = sy;
            bestex = ex;
            bestey = ey;
            bestwi = w - avw;
            besthi = h - avh;
          }
          if(inc == 0) {
            // Can't find better
            // TODO: try to do less splitting maybe?
            break;
          }
        }
        assert assertConsistent();
      }
      if(LOG.isDebuggingFinest()) {
        LOG.debugFinest("Best: " + bestsx + "," + bestsy + " - " + bestex + "," + bestey + " inc: " + bestwi + "x" + besthi + " " + bestinc);
      }
      // Need to increase the total area
      if(bestinc > 0) {
        assert(bestex == cols - 1 || bestey == rows - 1);
        double inc = Math.max(bestwi, besthi * ratio);
        resize(inc);
  
        // Resubmit
        put(w, h, data);
        return;
      }
      // Need to split a column.
      // TODO: find best column to split. Currently: last
      if(bestwi < 0.0) {
        splitCol(bestex, -bestwi);
        bestwi = 0.0;
      }
      // Need to split a row.
      // TODO: find best row to split. Currently: last
      if(besthi < 0.0) {
        splitRow(bestey, -besthi);
        besthi = 0.0;
      }
      for(int x = bestsx; x <= bestex; x++) {
        for(int y = bestsy; y <= bestey; y++) {
          usage.get(y).set(x, data);
        }
      }
      double xpos = 0.0;
      double ypos = 0.0;
      {
        for(int x = 0; x < bestsx; x++) {
          xpos += widths.get(x);
        }
        for(int y = 0; y < bestsy; y++) {
          ypos += heights.get(y);
        }
      }
      map.put(data, new double[] { xpos, ypos, w, h });
      if(LOG.isDebuggingFinest()) {
        logSizes();
      }
    }