Chapter 3 : Drawables for charts

Surfaces

There are several ways to build a surface:

  • providing its mathematical form, with an X and Y range.
  • providing input points that should be used to generate the surface polygons.
  • providing input polygons that define the surface.

Surface defined by a function

The simplest way of providing data to plot is to implement a Mapper, which basically represents a mathematical function to be used by a GridBuilder. The mapper generates the input coordinates that allow a Tessellator to build polygons :

mappers

This sequence is wrapped in the SurfaceBuilder class, which is demonstrated in the surface tutorial.

This sequence can be customized by any other strategy as soon as one is finally able to build a collection of Drawable object.

Mappers

A Mapper is an abstract class that requires its method f(double x, double y) to be implemented. The most simple mapper implementation may implement the mathematical function as follow:

Mapper mapper = new Mapper(){
  public double f(double x, double y) {
    return Math.sin(x)*Math.cos(y);
  }
};

Note that although a Mapper let you use double types for x, y and z, the Coord3d structure used by all drawables holds float values. The reason is that float precision is largely sufficient for the purpose of chart rendering, and uses less memory for a large number of Coord3d.

Example Mapper based on an image content

The BufferedImageMapper is a Mapper implementation returning z values computed from the pixel colors of an image, where each {x,y} is a pixel index.

public double f(double x, double y) {
  if (x == Double.NaN || y == Double.NaN)
    return Double.NaN;
  int rbg = image.getRGB((int) x, (maxRow) - ((int) y));
  float red   = (float) ((rbg >> 16) & 0xFF) / 255.0f;
  float green = (float) ((rbg >> 8) & 0xFF) / 255.0f;
  float blue  = (float) ((rbg) & 0xFF)     / 255.0f;
  return ((double) ((red * 0.3f) + (green * 0.59f) + (blue * 0.11f)));
}

chromatogram

See the ChromatogramDemo in package demos.surface.big.

Example Mapper based on surface regression

Svm3d is an extension allowing to define mappers through a set of training points. The green points on the picture below form a training set for a SVM regression model. Once this model is trained, it is able to return a Z value for any point in {X,Y} space:

public double f(double x, double y) {
  return svm.apply(x, y)[0];
}

svm3d

This mapper can be seen as an alternative as using the later mentioned Delaunay tessellator. SvmMapper has the advantage of delivering surface with a user choosen grid with possible high resolution and smooth curves. However it might require spending time on SVM parameter tuning.

Grids

A Grid is able to use a Mapper to generate 3d coordinates. Their actual implementation can generate any mesh as soon as their exist a Tessellator able to handle their output. In the example below, we generate points standing on an orthogonal grid, for an identical X and Y range, with 50 grid steps:

Range xrange = new Range(-150, 150);
Range yrange = new Range(-150, 150);
int steps = 50;
OrthonormalGrid grid = new OrthonormalGrid(xrange, steps, yrange, steps);

Surface defined by input points

Tessellation

Tessellation is the process of creating polygons out of a set of input coordinates. Jzy3d supports several Tessellator strategies as explained hereafter.

Orthonormal tesselation
  • OrthonormalTessellator is able to build surface polygons assuming the input data represents nodes of an orthogonal mesh.
  • RingTessellator is an extention of OrthonormalTesselator that allows “cutting” a surface according to a min and max radius.

Tesselating a surface based on the preceding mapper and grid is simply achieved by calling:

Shape surface = new SurfaceBuilder().orthonormal(grid, mapper);

or

Shape surface = new SurfaceBuilder().ring(grid, mapper, 20f, 50f);

ring

See BookTesselationRing

One may also build surfaces dedicated to large datasets. These are built using CompileableComposite, a drawable object able to compile itself as an OpenGL display list, and then make use of this prebuilt GL code for all following rendering:

Shape surface = new SurfaceBuilder().orthonormalBig(grid, mapper);

See ChromatogramDemo

Delaunay: a constraint free surface tessellator

One may require to build a surface using a random set of points. For this use case, a Delaunay tesselator has been implemented using JDT to compute the tesselation of an unordered set of points.

Delaunay algorithm, which details won’t be covered here, is able to compute a good triangulation for points given as follow:

See IncrementalDelaunayDemo in the demos.surface.delaunay package.

delaunay

Surface defined by polygons

You can build any shape by defining raw polygons by yourself as shown in the bellow code:

double [][] mesh = new double[][];// 3x3
List<Polygon> polygons = new ArrayList<Polygon>();
for(int i = 0; i < mesh.length -1; i++){
  for(int j = 0; j < mesh[i].length -1; j++){
    Polygon polygon = new Polygon();
    polygon.add(new Point(new Coord3d(i, j, mesh[i][j]) ));
    polygon.add(new Point(new Coord3d(i, j+1, mesh[i][j+1]) ));
    polygon.add(new Point(new Coord3d(i+1, j+1, mesh[i+1][j+1]) ));
    polygon.add(new Point(new Coord3d(i+1, j, mesh[i+1][j]) ));
    polygons.add(polygon);
  }
}
Shape surface = new Shape(polygons);

It is important to have only one Shape referencing a given list of polygons. Indeed, each polygon point holds its own color data, so when a Shape is modified by a color tool, each polygon’s point has its color field updated.

buildSurface

See BuildSurfaceDemo

Scatters

Scatters are simple drawables coming in several flavors:

  • Scatter: a scatter plot made of a single color, using Coord3d[] and Color[] as data model
  • MultiColorScatter: a scatter plot painted by a Colormap, using Coord3d[] as data model
  • MultiColorScatterList: a scatter plot painted by a Colormap, using List<Coord3d> as data model

The below code let you create a simple RGB scatter:

Coord3d[] points = new Coord3d[size];
Color[] colors = new Color[size];

for(int  i=0; i<size; i++){
  x = (float) Math.random() - 0.5f;
  y = (float) Math.random() - 0.5f;
  z = (float) Math.random() - 0.5f;
  a = 0.25f;
  points[i] = new Coord3d(x, y, z);
  colors[i] = new Color(x, y, z, a);
}
Scatter scatter = new Scatter(points, colors);

scatter

See ScatterDemo, ScatterVBODemo, MultiColorScatterDemo, Scatter2D_EmulGLDemo, ScatterSphereDemo, ScatterCubeDemo

Histograms & bar charts

Bar charts can be build easily using HistogramBar, which is basically a Composite made of one Tube and two closing Disk. The below code shows how to create drawable bars:

HistogramBar bar = new HistogramBar();
bar.setData(new Coord3d(x, y, 0), height, 10, Color.random());
bar.setWireframeDisplayed(false);

histo

See HistogramDemo, HistogramLightDemo