@Override
  public RoofHookPoint[] getRoofHookPoints(int pNumber, DormerRow dormerRow, int dormerRowNum) {
    Vector2d v = new Vector2d(this.v1);

    MinMax polygonMinMaxY = findMinMaxY(this.polygon);
    if (polygonMinMaxY == null) {
      // XXX
      polygonMinMaxY = new MinMax(0, 1d);
    }

    v.scale(1d / (pNumber + 1d));

    Point2d p = new Point2d(this.p1);

    RoofHookPoint[] ret = new RoofHookPoint[pNumber];
    for (int i = 0; i < pNumber; i++) {
      p.add(v);

      MinMax minMaxY = limitZToPolygon(p.x);

      double z = calcRowPosition(minMaxY, dormerRow, dormerRowNum);

      //            double z = minMaxY.getMin();

      double y = this.plane.calcYOfPlane(p.x, -z);

      Point3d pp = new Point3d(p.x, y, -z);

      double b = minMaxY.getMax() - z; // (minMaxY.getMax() - minMaxY.getMin()) - z;

      RoofHookPoint hook = new RoofHookPoint(pp, Math.toRadians(0), b, Math.toRadians(0));

      ret[i] = hook;
    }

    return ret;
  }
  /**
   * TODO: chunkNumericPK definition.
   *
   * @param table
   * @param columns
   * @param chunkSize
   * @throws ReplicatorException
   * @throws InterruptedException
   */
  private void chunkNumericPK(Table table, String[] columns, long chunkSize)
      throws ReplicatorException, InterruptedException {
    // Retrieve PK range
    MinMax minmax = retrieveMinMaxCountPK(connection, table);

    if (minmax != null) {
      if (logger.isDebugEnabled())
        logger.debug(
            "Min = "
                + minmax.getMin()
                + " -- Max = "
                + minmax.getMax()
                + " -- Count = "
                + minmax.getCount());

      if (minmax.getCount() <= chunkSize)
        // Get the whole table at once
        chunks.put(new NumericChunk(table, columns));
      else {
        // Share the joy among threads,
        // if primary key is evenly distributed
        if (!minmax.isDecimal()) {
          long gap = (Long) minmax.getMax() - (Long) minmax.getMin();
          long blockSize = chunkSize * gap / minmax.getCount();

          long nbBlocks = gap / blockSize;
          if (gap % blockSize > 0) nbBlocks++;

          long start = (Long) minmax.getMin() - 1;
          long end;
          do {
            end = start + blockSize;
            if (end > (Long) minmax.getMax()) end = (Long) minmax.getMax();
            NumericChunk e = new NumericChunk(table, start, end, columns, nbBlocks);
            chunks.put(e);
            start = end;
          } while (start < (Long) minmax.getMax());
        } else {
          BigInteger start =
              ((BigDecimal) minmax.getMin())
                  .setScale(0, RoundingMode.FLOOR)
                  .toBigInteger()
                  .subtract(BigInteger.valueOf(1));

          BigInteger max =
              ((BigDecimal) minmax.getMax()).setScale(0, RoundingMode.CEILING).toBigInteger();

          BigInteger gap = max.subtract(start);

          BigInteger blockSize =
              gap.multiply(BigInteger.valueOf(chunkSize))
                  .divide(BigInteger.valueOf(minmax.getCount()));

          long nbBlocks = gap.divide(blockSize).longValue();

          if (!gap.remainder(blockSize).equals(BigInteger.ZERO)) {
            nbBlocks++;
            blockSize =
                gap.divide(BigInteger.valueOf(nbBlocks))
                    .add(
                        gap.remainder(blockSize).equals(BigInteger.ZERO)
                            ? BigInteger.ZERO
                            : BigInteger.ONE);
          }
          BigInteger end;
          do {
            end = start.add(blockSize);
            if (end.compareTo(
                    (((BigDecimal) minmax.getMax()).setScale(0, RoundingMode.CEILING))
                        .toBigInteger())
                == 1)
              end =
                  (((BigDecimal) minmax.getMax()).setScale(0, RoundingMode.CEILING)).toBigInteger();

            NumericChunk e = new NumericChunk(table, start, end, columns, nbBlocks);
            chunks.put(e);
            start = end;
          } while (start.compareTo(
                  (((BigDecimal) minmax.getMax()).setScale(0, RoundingMode.CEILING)).toBigInteger())
              == -1);
        }
      }
    } else {
      // table is empty or does not have a
      // good candidate as a PK for chunking.
      // Fall back to limit method
      chunks.put(new NumericChunk(table, columns));
    }
  }
 private double calcRowPosition(MinMax minMaxY, DormerRow dormerRow, int dormerRowNum) {
   double row = (dormerRow.getRowNum() - 1) / (double) dormerRowNum;
   return minMaxY.getMin() + (minMaxY.getMax() - minMaxY.getMin()) * row;
 }