public Numerical1DDataDomainValues(TablePerspective data, EDimension main) {
    super(data, main);
    Perspective p = main.select(data.getDimensionPerspective(), data.getRecordPerspective());
    this.groups = Numerical1DMixin.extractGroups(p, this);
    this.isInteger = DataSupportDefinitions.dataClass(EDataClass.NATURAL_NUMBER).apply(data);

    Object desc = getDescription();
    Float min = null, max = null;
    if (desc instanceof NumericalProperties) {
      min = ((NumericalProperties) desc).getMin();
      max = ((NumericalProperties) desc).getMax();
    }
    if (min == null || min.isNaN() || max == null || max.isNaN()) {
      DoubleStatistics stats = createStats(this, groups);
      if (min == null || min.isNaN()) min = (float) stats.getMin();
      if (max == null || max.isNaN()) max = (float) stats.getMax();
    }
    this.mixin = new Numerical1DMixin(this, groups, min.floatValue(), max.floatValue());
  }
  /**
   * @param buffer
   * @param tile
   * @param pool
   * @return
   */
  private Tile createTile(
      FloatBuffer buffer,
      Rectangle tile,
      GL gl,
      Deque<Texture> pool,
      TablePerspective tablePerspective) {
    final VirtualArray recordVA = tablePerspective.getRecordPerspective().getVirtualArray();
    final VirtualArray dimVA = tablePerspective.getDimensionPerspective().getVirtualArray();
    final ATableBasedDataDomain dataDomain = tablePerspective.getDataDomain();
    final int ilast = tile.y + tile.height;
    final int jlast = tile.x + tile.width;

    // fill buffer
    buffer.rewind();
    for (int i = tile.y; i < ilast; ++i) {
      int recordID = recordVA.get(i);
      for (int j = tile.x; j < jlast; ++j) {
        int dimensionID = dimVA.get(j);
        Color color = blockColorer.apply(recordID, dimensionID, dataDomain, false);
        buffer.put(color.getRGBA());
      }
    }

    // load to texture
    buffer.rewind();
    Texture texture;
    if (!pool.isEmpty()) texture = pool.poll();
    else texture = TextureIO.newTexture(GL.GL_TEXTURE_2D);

    TextureData texData = asTextureData(buffer, tile.width, tile.height);
    texture.updateImage(gl, texData);
    gl.glFlush();
    texData.destroy();

    return new Tile(tile, texture);
  }
 /**
  * @param context
  * @return
  */
 private boolean hasTablePerspective(GLElementFactoryContext context) {
   TablePerspective d = context.getData();
   if (d == null) return false;
   if (!DataSupportDefinitions.homogenousColumns.apply(d)) return false;
   return d.getNrDimensions() == 1 || d.getNrRecords() == 1;
 }
  /** @param context */
  public void create(IGLElementContext context, TablePerspective tablePerspective) {
    final GL gl = GLContext.getCurrentGL();

    final int numberOfRecords = tablePerspective.getRecordPerspective().getVirtualArray().size();
    final int numberOfDimensions =
        tablePerspective.getDimensionPerspective().getVirtualArray().size();
    dimension = new Dimension(numberOfDimensions, numberOfRecords);

    if (numberOfDimensions <= 0 || numberOfRecords <= 0) {
      takeDown();
      return;
    }

    final int maxSize = resolveMaxSize(gl);

    boolean needXTiling = numberOfDimensions > maxSize;
    boolean needYTiling = numberOfRecords > maxSize;

    // pool of old texture which we might wanna reuse
    final Deque<Texture> pool = new LinkedList<>();
    for (Tile tile : tiles) {
      pool.add(tile.texture);
    }
    tiles.clear();

    if (!needXTiling && !needYTiling) {
      // single tile
      FloatBuffer buffer =
          FloatBuffer.allocate(numberOfDimensions * numberOfRecords * 4); // w*h*rgba
      Rectangle tile = new Rectangle(0, 0, numberOfDimensions, numberOfRecords);
      tiles.add(createTile(buffer, tile, gl, pool, tablePerspective));
    } else if (needXTiling && !needYTiling) {
      // tile in x direction only
      FloatBuffer buffer = FloatBuffer.allocate(maxSize * numberOfRecords * 4); // w*h*rgba
      // fill full
      int lastTile = numberOfDimensions - maxSize;
      for (int i = 0; i < lastTile; i += maxSize) {
        Rectangle tile = new Rectangle(i, 0, maxSize, numberOfRecords);
        tiles.add(createTile(buffer, tile, gl, pool, tablePerspective));
      }
      { // create rest
        int remaining = numberOfDimensions % maxSize;
        Rectangle tile =
            new Rectangle(numberOfDimensions - remaining, 0, remaining, numberOfRecords);
        tiles.add(createTile(buffer, tile, gl, pool, tablePerspective));
      }
    } else if (!needXTiling && needYTiling) {
      // tile in y direction only
      FloatBuffer buffer = FloatBuffer.allocate(numberOfDimensions * maxSize * 4); // w*h*rgba
      // fill full
      int lastTile = numberOfRecords - maxSize;
      for (int i = 0; i < lastTile; i += maxSize) {
        Rectangle tile = new Rectangle(0, i, numberOfDimensions, maxSize);
        tiles.add(createTile(buffer, tile, gl, pool, tablePerspective));
      }
      { // create rest
        int remaining = numberOfRecords % maxSize;
        Rectangle tile =
            new Rectangle(0, numberOfRecords - remaining, numberOfDimensions, remaining);
        tiles.add(createTile(buffer, tile, gl, pool, tablePerspective));
      }
    } else {
      // tile in both directions
      // tile in y direction only
      FloatBuffer buffer = FloatBuffer.allocate(maxSize * maxSize * 4); // w*h*rgba
      // fill full
      int lastTileR = numberOfRecords - maxSize;
      int lastTileD = numberOfDimensions - maxSize;
      for (int i = 0; i < lastTileR; i += maxSize) {
        for (int j = 0; j < lastTileD; j += maxSize) {
          Rectangle tile = new Rectangle(j, i, maxSize, maxSize);
          tiles.add(createTile(buffer, tile, gl, pool, tablePerspective));
        }
        { // create rest
          int remaining = numberOfDimensions % maxSize;
          Rectangle tile = new Rectangle(numberOfDimensions - remaining, i, remaining, maxSize);
          tiles.add(createTile(buffer, tile, gl, pool, tablePerspective));
        }
      }
      { // last line
        int iremaining = numberOfRecords % maxSize;
        int i = numberOfRecords - iremaining;
        for (int j = 0; j < lastTileD; j += maxSize) {
          Rectangle tile = new Rectangle(j, i, maxSize, iremaining);
          tiles.add(createTile(buffer, tile, gl, pool, tablePerspective));
        }
        { // create rest
          int remaining = numberOfDimensions % maxSize;
          Rectangle tile = new Rectangle(numberOfDimensions - remaining, i, remaining, iremaining);
          tiles.add(createTile(buffer, tile, gl, pool, tablePerspective));
        }
      }
    }

    // free remaining elements in pool
    for (Texture tex : pool) tex.destroy(gl);
    pool.clear();
  }