The SubCharts API allows to place 1..N child SciChartSurfaces within a parent chart (Charts within charts). It is possible to have multiple sub-charts within the main SciChartSurface.
What is the Benefit of the SubCharts API?
SubCharts is an extremely powerful API that allows you to create dashboards or chart groups that share a single SciChartSurface, single WebGL canvas and engine instance.
- With the SubCharts API you can create a single chart with several nested chart panels, enabling you to create components of single dashboards with one SciChartSurface parent that you can re-use throughout your app.
- It's possible to dynamically position and resize SubCharts on the main SciChartSurface, so you could create dynamic multi-panel charts such as in the financial industry or industrial process monitoring industry.
- SubCharts are a huge performance boost when many charts are used. For example, 100 charts created using SciChartSurface.create() will be far slower than 100 charts added to a single SciChartSurfaces using SubCharts.
It's possible with SubCharts to create very dashboards and re-usable multi-chart configurations, multi-chart groups while maintaining very high performance due to the WebGL canvas & context sharing nature of this API.
SubCharts API could be applied to display separate charts simultaneously on a single root element. The API allows to set a position, size , and styling of a sub-chart. Additionally it is possible to add custom HTML elements to the chart, which would be positioned accordingly to the chart layout flow.
Overview of the SubCharts API
A sub-chart is represented by the SciChartSubSurface class, which inherits SciChartSurface. Similarly to SciChartSurface it has its own Axes, Modifiers, Renderable Series and Annotations.
You can add a sub-chart to a SciChartSurface by calling sciChartSurface.addSubChart(options: I2DSubSurfaceOptions). This returns a SciChartSubSurface instance.
| addSubChart function |
Copy Code
|
|---|---|
sciChartSurface.addSubChart(options?: I2DSubSurfaceOptions): SciChartSubSurface |
|
I2DSubSurfaceOptions may be passed to addSubChart. This type has the following properties:
| I2DSubSurfaceOptions type |
Copy Code
|
|---|---|
export interface I2DSubSurfaceOptions extends I2DSurfaceOptions { /** * A rectangle defining the position and size of a subchart. * If {@link coordinateMode} is Relative (the default) then the values give the size as a proportion of the parent div, and all properties must be between 0 and 1 inclusive. * If {@link coordinateMode} is DataValue, values will be converted to coordinates using {@link parentXAxisId} and {@link parentYAxisId}. Subchart will be clpped to the parent SeriesViewRect * Can only be set if this is a subChart. See {@link addSubChart} */ position?: Rect; /** An id or div element that will wrap the subchart. This can contain top, left, bottom and right section divs. The chart will shrink to fit the sections */ subChartContainerId?: string | HTMLDivElement; /** Whether other surfaces, including the parent, will be visible underneath this surface */ isTransparent?: boolean; /** Sets if the subchart is visible, allowing you to hide a subchart without removing it from the parent surface */ isVisible?: boolean; /** * Sets additional absolute padding between the SciChartSubSurface and its parent, in order top, right, bottom, left * {@link subPosition} is applied first, then this padding is added. */ subChartPadding?: Thickness; /** * Gets or sets the {@link ECoordinateMode} used when calculating the actual position based on the {@link subPosition} * Default Relative */ coordinateMode?: ECoordinateMode; /** * Sets the AxisId used to determing which X Axis should be used when calculating the actual position based on the {@link subPosition} * if {@link coordinateMode} is DataValue */ parentXAxisId?: string; /** * Sets the AxisId used to determing which Y Axis should be used when calculating the actual position based on the {@link subPosition} * if {@link coordinateMode} is DataValue */ parentYAxisId?: string; /** * Gets or sets scale property for all sections */ sectionScale?: number; } |
|
The return value of addSubChart is a SciChartSubSurface class. This inherits SciChartSurface but has additional properties, which can be found below.
| SciChartSubSurface type |
Copy Code
|
|---|---|
/** * @summary The {@link SciChartSubSurface} is the surface created within another surface * @description * It can be added using {@link SciChartSurface.addSubChart} method. * * To update the positioning of the {@link SciChartSubSurface}, use {@link SciChartSubSurface.subPosition}; * also you can call {@link SciChartSubSurface.updateSubLayout} to refresh the layout of the sub-surface. * @remarks * It is not possible to have more than one level of nested sub-surfaces. */ export class SciChartSubSurface extends SciChartSurface { public readonly isSubSurface: boolean = true; public readonly subChartContainer: HTMLDivElement; public topSectionClass = "top-section"; public leftSectionClass = "left-section"; public bottomSectionClass = "bottom-section"; public rightSectionClass = "right-section"; /** * Creates an instance of the {@link SciChartSubSurface} * @param webAssemblyContext The {@link TSciChart | SciChart 2D WebAssembly Context} containing native methods and * access to our WebGL2 Engine and WebAssembly numerical methods * @param options optional parameters of type {@link ISciChartSubSurfaceOptions} used to configure the {@link SciChartSubSurface} */ constructor(webAssemblyContext: TSciChart, options?: ISciChartSubSurfaceOptions) { super(webAssemblyContext, options); } /** Whether other surfaces, including the parent, will be visible underneath this surface */ public isTransparent: boolean; /** Gets or sets additional absolute padding between the SciChartSubSurface and its parent, in order top, right, bottom, left * {@link subPosition} is applied first, then this padding is added */ public subChartPadding: Thickness; /** Gets or sets the {@link ECoordinateMode} used when calculating the actual position based on the {@link subPosition} */ public coordinateMode: ECoordinateMode; /** Gets or sets the parent chart XAxisId used to determine which X Axis should be used when calculating the actual position */ public parentXAxisId: string; /** Gets or sets the parent chart YAxisId used to determine which Y Axis should be used when calculating the actual position */ public parentYAxisId: string; /** A rectangle defining the position and size of a subchart. * If {@link coordinateMode} is Relative (the default) then the values give the size as a proportion of the parent div, and all properties must be between 0 and 1 inclusive. * If {@link coordinateMode} is DataValue, values will be converted to coordinates using {@link parentXAxisId} and {@link parentYAxisId}. Subchart will be clpped to the parent SeriesViewRect * Can only be set if this is a subChart. See {@link addSubChart} */ public subPosition: Rect; /** Gets or sets if the subchart is visible, allowing you to hide a subchart without removing it from the parent surface */ public isVisible: boolean; /** Gets or sets scale property for all sections * It is necessary if the scale transformation is being used for html areas around the subchart * For example, style = { width: "50%", transform: scale(2), transformOrigin: 'left top' } */ public sectionScale: number; /** Recalculate the position of the subChart. Call if you update the size of html elements in the wrapper */ public updateSubLayout(isDrawing = false): void {} /** The parent SciChartSurface, if this is a subChart. */ public get parentSurface(): SciChartSurface { return this.parentSurfaceProperty; } /** Gets the Surface Type. See {@link ESurfaceType} for list of values */ public get surfaceType(): ESurfaceType { return ESurfaceType.SciChartSurfaceType; } /** Changes the Viewport Size of the {@link SciChartSurfaceBase} */ public changeViewportSize(pixelWidth: number, pixelHeight: number): void {} /** Gets the sub-chart container */ public getSubChartContainer(): HTMLDivElement { return this.subChartContainer; } /** Gets the sub-chart rect on the parent surface, accounting for viewport size and padding */ public getSubChartRect(): Rect { return new Rect(0, 0, 1, 1); } /** Deletes native (WebAssembly) memory used by this type, after which it cannot be used */ public delete(clearHtml: boolean = true): void {} /** Serializes the current {@link SciChartSubSurface} to a JSON definition. See {@link ISubChartDefinition} */ public toJSON(excludeData = false): ISubChartDefinition { return {} as ISubChartDefinition; } } |
|
Basic Subchart Example (Charts within Charts)
Let's demonstrate a simple setup, where we define a sub-chart on a surface. For this we will start from defining a surface with some axes on it. On the surface we will create a sub-chart on a specified area.
In the example above we create a SciChartSurface as normal and add some series to it. Then, we call SciChartSurface.addSubChart function, which contains a position property. The property defines a structure for specifying coordinates and sizes of a sub-chart. By default, the coordinates and size are treated as ratio values in range from 0 to 1, with a canvas viewport used as a base.
Next, we add some series to both the parent SciChartSurface and the SciChartSubSurface. We subscribe to visibleRangeChanged on the parent SciChartSurface x, y axis and use that to update a BoxAnnotation in the SciChartSubSurface.
By creating this example, we have created a 2D viewport overview using the Sub-charts API, placing a child SciChartSubSurface inside a parent SciChartSurface, and subscribed to interactivity on the parent chart. The Sub-chart shows all the data and where you have zoomed in on the parent chart, allowing you to get a view into your current position (zoom level and range) on the chart.
<div id="scichart-root"></div>
body {
margin: 0;
}
#scichart-root {
width: 100%;
height: 100vh;
}
const xValues = [];
const yValues = [];
const yValues1 = [];
const yValues2 = [];
for (let i = 0; i < 100; i++) {
xValues.push(i);
yValues.push(0.2 * Math.sin(i * 0.1) - Math.cos(i * 0.01));
yValues1.push(0.1 * Math.sin(i * 0.3) - Math.cos(i * 0.02));
yValues2.push(0.1 * Math.sin(i * 0.7) - Math.cos(i * 0.03));
}
async function simpleSubChart(divElementId) {
// #region ExampleA
// Demonstrates how to use the Sub-Charts API to create child charts in a parent chart
const {
SciChartSurface,
NumericAxis,
FastLineRenderableSeries,
XyDataSeries,
SciChartJsNavyTheme,
Rect,
ECoordinateMode,
ZoomPanModifier,
ZoomExtentsModifier,
MouseWheelZoomModifier,
BoxAnnotation,
NumberRange,
} = SciChart;
// or, for npm, import { SciChartSurface, ... } from "scichart"
// Function to add series to chart. This will be re-used for the parent and sub-charts
const addSeries = (sciChartSurface, stroke, x, y) => {
sciChartSurface.renderableSeries.add(
new FastLineRenderableSeries(wasmContext, {
stroke,
strokeThickness: 5,
dataSeries: new XyDataSeries(wasmContext, {
xValues: x,
yValues: y,
}),
opacity: sciChartSurface.isSubSurface ? 0.5 : 1,
})
);
};
// Create a parent (regular) SciChartSurface which will contain the sub-chart
const { wasmContext, sciChartSurface } = await SciChartSurface.create(
divElementId,
{
theme: new SciChartJsNavyTheme(),
}
);
// Create X,Y axis on the parent chart and programmatically zoom into part of the data
sciChartSurface.xAxes.add(
new NumericAxis(wasmContext, {
growBy: new NumberRange(0.1, 0.1),
})
);
sciChartSurface.yAxes.add(
new NumericAxis(wasmContext, {
growBy: new NumberRange(0.1, 0.1),
})
);
// Create a series on the parent chart
addSeries(sciChartSurface, "#FF6600", xValues, yValues);
addSeries(sciChartSurface, "#ae418d", xValues, yValues1);
addSeries(sciChartSurface, "#47bde6", xValues, yValues2);
// Add some interactivity to the parent chart
sciChartSurface.chartModifiers.add(new ZoomPanModifier());
sciChartSurface.chartModifiers.add(new MouseWheelZoomModifier());
sciChartSurface.chartModifiers.add(new ZoomExtentsModifier());
// Add a Sub-Charts to the main surface. This will display a rectangle showing the current zoomed in area on the parent chart
const subChart1 = sciChartSurface.addSubChart({
// Properties from I2DSubSurfaceOptions affect positioning and rendering of the subchart
position: new Rect(0.02, 0.02, 0.4, 0.4),
isTransparent: false,
isVisible: true,
coordinateMode: ECoordinateMode.Relative,
// However all properties from I2DSurfaceOptions are available
viewportBorder: { border: 3, color: "#77777777" },
backgroundColor: "#333",
title: "2D Overview with Sub-Charts",
titleStyle: { fontSize: 16, color: "#eeeeee77" },
});
// Add x,y axis to the subchart
subChart1.xAxes.add(new NumericAxis(wasmContext, { isVisible: false }));
subChart1.yAxes.add(new NumericAxis(wasmContext, { isVisible: false }));
addSeries(subChart1, "#FF6600", xValues, yValues);
addSeries(subChart1, "#ae418d", xValues, yValues1);
addSeries(subChart1, "#47bde6", xValues, yValues2);
// Add a BoxAnnotation to the SubChart
const boxAnnotation = new BoxAnnotation({
fill: "#FF660033",
stroke: "#FF6600",
strokeThickness: 2,
opacity: 0.5,
});
subChart1.annotations.add(boxAnnotation);
// On parent chart zoom, pan, update the box annotation on the subchart
sciChartSurface.xAxes.get(0).visibleRangeChanged.subscribe((args) => {
boxAnnotation.x1 = args.visibleRange.min;
boxAnnotation.x2 = args.visibleRange.max;
});
sciChartSurface.yAxes.get(0).visibleRangeChanged.subscribe((args) => {
boxAnnotation.y1 = args.visibleRange.min;
boxAnnotation.y2 = args.visibleRange.max;
});
// On the parent chart, programmatically zoom into a region
setTimeout(() => {
sciChartSurface.xAxes
.get(0)
.animateVisibleRange(new NumberRange(30, 70), 1000);
sciChartSurface.yAxes
.get(0)
.animateVisibleRange(new NumberRange(-0.4, 0.4), 1000);
}, 1000);
// #endregion
const { TextAnnotation, EHorizontalAnchorPoint } = SciChart; // Add a watermark to explain what's going on
sciChartSurface.annotations.add(
new TextAnnotation({
x1: 0.5,
y1: 0.5,
text: "Mousewheel/Drag the parent chart to update the subchart view rect",
horizontalAnchorPoint: EHorizontalAnchorPoint.Center,
xCoordinateMode: ECoordinateMode.Relative,
yCoordinateMode: ECoordinateMode.Relative,
opacity: 0.5,
fontSize: 20,
})
);
}
simpleSubChart("scichart-root");
async function builderExample(divElementId) {
// #region ExampleB
// Demonstrates how to create a line chart with SciChart.js using the Builder API
const {
chartBuilder,
ESeriesType,
EAxisType,
EThemeProviderType,
Rect,
ECoordinateMode,
EAnnotationType,
NumberRange,
EChart2DModifierType,
} = SciChart;
// or, for npm, import { chartBuilder, ... } from "scichart"
const { wasmContext, sciChartSurface } = await chartBuilder.build2DChart(
divElementId,
{
surface: { theme: { type: EThemeProviderType.Dark } },
// Main chart definition is here
xAxes: { type: EAxisType.NumericAxis },
yAxes: { type: EAxisType.NumericAxis },
series: [
{
type: ESeriesType.LineSeries,
xyData: {
xValues,
yValues: yValues1,
},
options: {
stroke: "#0066FF",
strokeThickness: 5,
},
},
],
modifiers: [
{ type: EChart2DModifierType.ZoomPan },
{ type: EChart2DModifierType.ZoomExtents },
{ type: EChart2DModifierType.MouseWheelZoom },
],
// Subchart definition is here
subCharts: [
{
surface: {
// Properties from I2DSubSurfaceOptions affect positioning and rendering of the subchart
position: new Rect(0.02, 0.02, 0.4, 0.4),
isTransparent: false,
isVisible: true,
coordinateMode: ECoordinateMode.Relative,
// However all properties from I2DSurfaceOptions are available
viewportBorder: { border: 3, color: "#77777777" },
backgroundColor: "#333",
title: "2D Overview with Sub-Charts",
titleStyle: { fontSize: 16, color: "#eeeeee77" },
},
// Define the x,y axis on Subchart
xAxes: { type: EAxisType.NumericAxis, options: { isVisible: false } },
yAxes: { type: EAxisType.NumericAxis, options: { isVisible: false } },
// Define the series on Subchart
series: [
{
type: ESeriesType.LineSeries,
xyData: {
xValues,
yValues: yValues1,
},
options: {
stroke: "#0066FF",
strokeThickness: 5,
},
},
],
annotations: [
{
type: EAnnotationType.RenderContextBoxAnnotation,
options: {
fill: "#FF660033",
stroke: "#FF6600",
strokeThickness: 2,
opacity: 0.5,
},
},
],
},
],
}
);
// On parent chart zoom, pan, update the box annotation on the subchart
const subChartBoxAnnotation = sciChartSurface.subCharts
.at(0)
.annotations.get(0);
sciChartSurface.xAxes.get(0).visibleRangeChanged.subscribe((args) => {
subChartBoxAnnotation.x1 = args.visibleRange.min;
subChartBoxAnnotation.x2 = args.visibleRange.max;
});
sciChartSurface.yAxes.get(0).visibleRangeChanged.subscribe((args) => {
subChartBoxAnnotation.y1 = args.visibleRange.min;
subChartBoxAnnotation.y2 = args.visibleRange.max;
});
// On the parent chart, programmatically zoom into a region
setTimeout(() => {
sciChartSurface.xAxes
.get(0)
.animateVisibleRange(new NumberRange(30, 70), 1000);
sciChartSurface.yAxes
.get(0)
.animateVisibleRange(new NumberRange(-0.4, 0.4), 1000);
}, 1000);
// #endregion
}
// Uncomment this to use the builder example //builderExample("scichart-root");
Try zooming the example with mouse-drag, panning, mousewheel and double-click to reset zoom to see the effect on the BoxAnnotation in the SciChartSubSurface.
SubCharts with the Builder API
It is also possible to create a sub-chart via Builder API. For this pass an array of ISubChartDefinition via ISciChart2DDefinition.subCharts property.
For example, the following snippet will give us the same result as Basic Example setup: