The Surface Mesh 3D Chart Type
The surface mesh renders a two-dimensional array as a heightmap. In SciChart it's defined by the SurfaceMeshRenderableSeries3D class and provides a number of configurable chart types in SciChart 3D, including:
- dynamic real-time Surfaces (terrains or height maps).
- texturing of surfaces or terrains or height maps.
- Non-uniform or uniform grid spacing.
- Contour mapping or wireframe on terrain or height maps.
Note
Examples of the Surface Mesh 3D Series can be found in the SciChart Android Examples Suite as well as on GitHub:
In the Surface Mesh 3D Series, the data is stored in the UniformGridDataSeries3D<TX,TY,TZ>.
This represents a 2-dimensional grid, typically of type double
.
Some important points which is mandatory to understand while configuring the Surface Meshes:
- The double values which are stored in the UniformGridDataSeries3D<TX,TY,TZ> correspond to the heights on the chart (the
Y-Axis
). They are transformed into chart World Coordinates via the yAxis. - The
Z
andX
Data-Value are defined by the startX, stepX, startZ and stepZ properties on UniformGridDataSeries3D<TX,TY,TZ>. These are transformed into World Coordinates via the xAxis and zAxis respectively. - The Colours on the SurfaceMesh are defined by the MeshColorPalette. More on this in the following sections.
The SurfaceMeshRenderableSeries3D is highly configurable, so please read on the Configuring Surface Mesh 3D Series section.
Create a Surface Mesh Series 3D
In order to create Uniform Surface Mesh 3D - you will need to provide the UniformGridDataSeries3D<TX,TY,TZ> with N x M
array of points.
The above graph is rendered with the following code:
final NumericAxis3D xAxis = new NumericAxis3D();
xAxis.setGrowBy(new DoubleRange(.1, .1));
final NumericAxis3D yAxis = new NumericAxis3D();
yAxis.setVisibleRange(new DoubleRange(0.0, .3));
yAxis.setGrowBy(new DoubleRange(.1, .1));
final NumericAxis3D zAxis = new NumericAxis3D();
zAxis.setGrowBy(new DoubleRange(.1, .1));
final UniformGridDataSeries3D<Double, Double, Double> ds = new UniformGridDataSeries3D<>(Double.class, Double.class, Double.class, COUNT, COUNT);
for (int x = 0; x < COUNT; x++) {
for (int z = 0; z < COUNT; z++) {
final double xVal = 25 * x / COUNT;
final double zVal = 25 * z / COUNT;
final double y = Math.sin(xVal * 0.2) / ((zVal + 1.0) * 2.0);
ds.updateYAt(x, z, y);
}
}
final int[] colors = new int[]{0xFF1D2C6B, 0xFF0000FF, 0xFF00FFFF, 0xFFADFF2F, 0xFFFFFF00, 0xFFFF0000, 0xFF8B0000};
final float[] stops = new float[]{0, .1f, .3f, .5f, .7f, .9f, 1};
final GradientColorPalette palette = new GradientColorPalette(colors, stops);
final SurfaceMeshRenderableSeries3D rs = new SurfaceMeshRenderableSeries3D();
rs.setDataSeries(ds);
rs.setDrawMeshAs(DrawMeshAs.SolidWireframe);
rs.setStroke(0x77228B22);
rs.setContourStroke(0x77228B22);
rs.setStrokeThickness(convertValueToDp(2f));
rs.setDrawSkirt(false);
rs.setMeshColorPalette(palette);
UpdateSuspender.using(surface, () -> {
surface.setXAxis(xAxis);
surface.setYAxis(yAxis);
surface.setZAxis(zAxis);
surface.getRenderableSeries().add(rs);
surface.getChartModifiers().add(createDefaultModifiers3D());
});
Configuring Surface Mesh 3D Series
There are several properties which affect rendering of the SurfaceMeshRenderableSeries3D and those are listed below:
Property | Description |
---|---|
highlight | Changes the lighting algorithm to make cells appear lighter. |
meshColorPalette | defines the MeshColorPalette which is used to calculate color from data value. See the Applying Palettes to the 3D Surface Meshes section for more information. |
meshPaletteMode | Changes how the heightmap is applied. See the Applying Palettes to the 3D Surface Meshes section for more information. |
heightScaleFactor | Applies a constant scaling factor to the heights, e.g. setting to 0 will make the surface mesh flat. |
yOffset | Applies a constant offset heights, e.g. setting to 1 will move the SurfaceMesh 1-Data-Value in the Y-direction . |
The effect of these properties are demonstrated in the images below.
Highlight = 0 | Highlight = 1 |
---|---|
HeightScaleFactor = 0 | meshPaletteMode = HeightMapSolidCells |
Applying Palettes to the 3D Surface Meshes
The SurfaceMeshRenderableSeries3D accepts color palettes via the meshColorPalette property. There are several palettes available out of the box, including the following:
- SolidColorBrushPalette - applies a solid color to all cells in the mesh
- GradientColorPalette - maps a linear gradient to the mesh.
- BrushColorPalette - maps a BrushStyle to the mesh. For example, this can be used to map an image via the TextureBrushStyle type.
- Custom Palette - maps a custom Texture to the mesh.
Read on to learn more about each of these options.
In addition, rendering all of the above palettes can be affected by the MeshPaletteMode. See the Mesh Palette Modes section below for more information.
Mesh Palette Modes
The meshPaletteMode property changes how the palette is applied to the Mesh. Possible MeshPaletteMode are listed in the table below:
Mode | Description |
---|---|
HeightMapInterpolated | the palette is applied in the Y-direction (vertically). |
HeightMapSolidCells | same as HeightMapInterpolated but no interpolation. Use this if you want each cell have a separate colour. |
Textured | palette is applied to the mesh in the XZ plane. Imagine the palette is stretched over the mesh itself. |
TexturedSolidCells | same as Textured but no interpolation. Use this if you want each cell have a separate colour. |
Solid Color Palette
The Solid palette if provided by the SolidColorBrushPalette class. It's quite simple, let's dig into declaration:
final SurfaceMeshRenderableSeries3D rSeries = new SurfaceMeshRenderableSeries3D();
rSeries.setMeshColorPalette(new SolidColorBrushPalette(0xFF006400));
Gradient Color Palette
The GradientColorPalette can be used to map a Gradient Brush set of colors to heights in the Surface Mesh. The mapping is similar to that performed by the Heatmap Series in 2D Charts.
Given the following code:
final int[] colors = new int[]{0xFF1D2C6B, 0xFF0000FF, 0xFF00FFFF, 0xFFADFF2F, 0xFFFFFF00, 0xFFFF0000, 0xFF8B0000};
final float[] stops = new float[]{0, .1f, .3f, .5f, .7f, .9f, 1};
final GradientColorPalette palette = new GradientColorPalette(colors, stops);
Colors are mapped onto Y-values
as follows:
- minimum are drawn with the gradient stop color at offset 0.
- maximum are drawn with the gradient stop color at offset 1.
- All other values are linearly interpolated (including
Y-Values
outside of minimum and maximum values).
It's also possible to specify whether gradient is stepped or not. See code which creates stepped palette below:
final GradientColorPalette palette = new GradientColorPalette(colors, stops, true);
And the difference is showed below:
Linear Gradient | Stepped Gradient (isStepped = YES) |
---|---|
Brush Color Palette
A texture can be applied to the SurfaceMesh and mapped over it in the XZ plane plane by using a combination of the following:
See the code below:
final Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.example_weather_storm);
CustomPointMarker3D pointMarker = new CustomPointMarker3D(bitmap);
pointMarker.setSize(10);
final TextureBrushStyle brushStyle = new TextureBrushStyle(bitmap);
final BrushColorPalette palette = new BrushColorPalette(brushStyle);
final SurfaceMeshRenderableSeries3D rSeries = new SurfaceMeshRenderableSeries3D();
rSeries.setMeshColorPalette(palette);
rSeries.setMeshPaletteMode(MeshPaletteMode.Textured);
rSeries.setMeshColorPaletteSize(new Size(bitmap.getWidth(), bitmap.getHeight()));
Create a Custom Palette
In addition to all of the above, you can create your own custom Color Palette by inheriting MeshColorPalette and overriding the getTexture(int width, int height) method.
For example, see the following code snippet:
class CustomPalette extends MeshColorPalette {
@Override
public Texture2D getTexture(int width, int height) {
final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
try {
return Texture2D.fromBitmap(bitmap);
} finally {
bitmap.recycle();
}
}
}
The palette is applied to a SurfaceMeshRenderableSeries3D as in the above examples.
Note
You might noticed helped extension, which allows to create ITexture2D directly from your Bitmap - fromBitmap(Bitmap bitmap).
Surface Mesh 3D Wireframe and Contours
In SciChart, the 3D Surface Mesh can be configured to draw with Contours and/or Wireframe. That is added optionally ans configured via the drawMeshAs property. See the possible options in the table below:
Option | Surface | Wireframe | Contours |
---|---|---|---|
SolidMesh | + | ||
Wireframe | + | ||
Contours | + | ||
SolidWireframe | + | + | |
SolidWithContours | + | + | |
SolidWireframeWithContours | + | + | + |
Wireframe and Contours can be configured via the following properties:
- contourStrokeThickness - defines the thickness of the contour line.
- contourStroke - defines the stroke color of the contours, which may optionally include opacity.
- contourOffset - defines the offset of contours from
Y-values
, defaults to 0. - contourInterval - defines the
Y-value
intervals between contours. - strokeThickness - defines the thickness of the wireframe line.
- stroke - defines the stroke color of the wireframe, which may optionally include opacity.
Some examples are shown below:
Bare Wireframe | Solid Surface With Contours |
---|---|
Overriding Surface Mesh 3D Specific Cell Colors
In addition to the custom palettes, heightmaps, textures - you can override a specific cell or cells in the SurfaceMeshRenderableSeries3D by using the MetadataProvider API.
For example it can be used for one of the following:
- remove specific cells or mark them as NULL by overriding the cell color to be Transparent.
- mark regions of interest, say certain cells in a value range, or with index must be colored differently.
- with higher resolution meshes, you can change the shape of the mesh to circular (approx) by removing cells outside of a region.
It is showcased in our Surface mesh 3D with Metadata provider example. Let's see a code snippet from it, which shows how to implement the custom MetadataProvider:
class SurfaceMeshMetadataProvider3D extends MetadataProvider3DBase<SurfaceMeshRenderableSeries3D> implements ISurfaceMeshMetadataProvider3D {
SurfaceMeshMetadataProvider3D() {
super(SurfaceMeshRenderableSeries3D.class);
}
@Override
public void updateMeshColors(IntegerValues cellColors) {
final SurfaceMeshRenderPassData3D currentRenderPassData = (SurfaceMeshRenderPassData3D) renderableSeries.getCurrentRenderPassData();
final int countX = currentRenderPassData.countX - 1;
final int countZ = currentRenderPassData.countZ - 1;
cellColors.setSize(currentRenderPassData.getPointsCount());
final int[] items = cellColors.getItemsArray();
for (int x = 0; x < countX; x++) {
for (int z = 0; z < countZ; z++) {
final int index = x * countZ + z;
final int color;
if ((x >= 20 && x <= 26 && z > 0 && z < 47) || (z >= 20 && z <= 26 && x > 0 && x < 47)) {
color = TRANSPARENT;
} else {
color = dataManager.getRandomColor();
}
items[index] = color;
}
}
}
}
Note
For more information about custom MetadataProviders - please refer to the MetadataProvider API article.
Note
Examples of using MetadataProvider API can be found in the SciChart Android Examples Suite as well as on GitHub: