Angular Chart with BaseValue Axes

Demonstrates how to create a Angular Chart with BaseValue axes using SciChart.js to build non-linear and adaptive scales

Fullscreen

Edit

 Edit

Docs

drawExample.ts

angular.ts

ExampleDataProvider.ts

theme.ts

Copy to clipboard
Minimise
Fullscreen
1import {
2    AnnotationDragDeltaEventArgs,
3    BaseValueAxis,
4    CoordinateCalculatorBase,
5    CursorModifier,
6    ELabelPlacement,
7    EllipsePointMarker,
8    EXyDirection,
9    FastLineRenderableSeries,
10    MouseWheelZoomModifier,
11    NumberRange,
12    NumericAxis,
13    SciChartSurface,
14    VerticalLineAnnotation,
15    XDataSeries,
16    XyDataSeries,
17    ZoomExtentsModifier,
18    ZoomPanModifier,
19} from "scichart";
20import { ExampleDataProvider } from "../../../ExampleData/ExampleDataProvider";
21import { appTheme } from "../../../theme";
22
23/**
24 * Generate baseValues for a non-linear scale based on a power law
25 * @param visibleRange The visible range to generate values for
26 * @param base The base for the power law (e.g., 10 for powers of 10)
27 * @returns An array of values based on the power law, including zero if in range
28 */
29const generatePowerLawBaseValues = (
30    visibleRange: NumberRange,
31    base: number = 10,
32    minimumPower: number = 1
33): number[] => {
34    const baseValues: number[] = [];
35
36    // Generate negative powers
37    if (visibleRange.min < 0) {
38        const lowPower = Math.ceil(Math.log(Math.abs(visibleRange.min)) / Math.log(base));
39        const max =
40            visibleRange.max >= 0 ? minimumPower : Math.floor(Math.log(Math.abs(visibleRange.max)) / Math.log(base));
41        for (let power = lowPower; power >= minimumPower; power--) {
42            const value = -Math.pow(base, power);
43            baseValues.push(value);
44        }
45    }
46
47    // Add zero if it's within the range
48    if (visibleRange.min <= 0 && visibleRange.max >= 0) {
49        baseValues.push(0);
50    }
51
52    // Generate positive powers
53    if (visibleRange.max > 0) {
54        const highPower = Math.ceil(Math.log(Math.abs(visibleRange.max)) / Math.log(base));
55        const min =
56            visibleRange.min <= 0 ? minimumPower : Math.floor(Math.log(Math.abs(visibleRange.min)) / Math.log(base));
57        for (let power = minimumPower; power <= highPower; power++) {
58            const value = Math.pow(base, power);
59            baseValues.push(value);
60        }
61    }
62    return baseValues;
63};
64
65export const drawExample = async (rootElement: string | HTMLDivElement) => {
66    const { sciChartSurface, wasmContext } = await SciChartSurface.create(rootElement, {
67        theme: appTheme.SciChartJsTheme,
68    });
69
70    const xAxis = new BaseValueAxis(wasmContext, {
71        id: "BaseValueAxis",
72        visibleRange: new NumberRange(-0.1, 10.1),
73        flippedCoordinates: false,
74        labelPrecision: 3,
75        cursorLabelPrecision: 2,
76        baseValues: [0, 1, 2, 3, 4, 4.5, 4.6, 4.7, 4.8, 4.9, 5, 5.1, 5.2, 5.3, 5.4, 5.5, 6, 7, 8, 9, 10],
77        // autoTicks: true,
78        // majorDelta: 10,
79        // minorDelta: 2,
80        // labelProvider: new NumericLabelProvider()
81    });
82    sciChartSurface.xAxes.add(xAxis);
83    const baseXValues = xAxis.getBaseValues() as XDataSeries;
84
85    // Generate baseYValues using power law with base 10
86    const initialVisibleRange = new NumberRange(-500, 500);
87    const powerLawBase = 10;
88    const baseYValueSeries = new XDataSeries(wasmContext, {
89        xValues: generatePowerLawBaseValues(initialVisibleRange, powerLawBase),
90    });
91
92    const yAxis = new BaseValueAxis(wasmContext, {
93        visibleRange: initialVisibleRange,
94        minorsPerMajor: 9,
95        baseValues: baseYValueSeries,
96        autoTicks: false,
97    });
98    yAxis.visibleRangeChanged.subscribe((data) => {
99        baseYValueSeries.clear();
100        baseYValueSeries.appendRange(
101            generatePowerLawBaseValues(yAxis.visibleRange.union(initialVisibleRange), powerLawBase)
102        );
103    });
104
105    yAxis.tickProvider.getMajorTicks = (
106        minorDelta: number,
107        majorDelta: number,
108        visibleRange: NumberRange,
109        coordCalc?: CoordinateCalculatorBase
110    ) => {
111        const ticks = generatePowerLawBaseValues(visibleRange, powerLawBase);
112        return ticks;
113    };
114
115    yAxis.tickProvider.getMinorTicks = (
116        minorDelta: number,
117        majorDelta: number,
118        visibleRange: NumberRange,
119        coordCalc?: CoordinateCalculatorBase
120    ) => {
121        const majors = generatePowerLawBaseValues(visibleRange, powerLawBase);
122        const minors: number[] = [];
123        for (let i = 0; i < majors.length - 1; i++) {
124            const cur = majors[i];
125            const next = majors[i + 1];
126            const mpm = yAxis.minorsPerMajor + (next === 0 || cur === 0 ? 1 : 0);
127            const step = (next - cur) / mpm;
128            for (let j = 1; j < mpm; j++) {
129                minors.push(cur + step * j);
130            }
131        }
132        return minors;
133    };
134
135    sciChartSurface.yAxes.add(yAxis);
136
137    const lineSeries = new FastLineRenderableSeries(wasmContext, {
138        stroke: "white",
139        pointMarker: new EllipsePointMarker(wasmContext, {
140            width: 9,
141            height: 9,
142            strokeThickness: 0,
143            fill: "steelblue",
144            stroke: "LightSteelBlue",
145        }),
146    });
147
148    lineSeries.rolloverModifierProps.tooltipTextColor = "black";
149
150    sciChartSurface.renderableSeries.add(lineSeries);
151
152    const { xValues, yValues } = ExampleDataProvider.getNoisySinewave(1000, 10, 10, 150, 20);
153    lineSeries.dataSeries = new XyDataSeries(wasmContext, { xValues: xValues, yValues });
154
155    // We use a hidden numeric axis synced to the BaseValue x axis to position the vertical line
156    const linearXAxis = new NumericAxis(wasmContext, {
157        isVisible: false,
158        visibleRange: xAxis.visibleRange,
159    });
160    sciChartSurface.xAxes.add(linearXAxis);
161    xAxis.visibleRangeChanged.subscribe((data) => (linearXAxis.visibleRange = data.visibleRange));
162
163    const magnifierAnnotation = new VerticalLineAnnotation({
164        xAxisId: linearXAxis.id,
165        x1: 5,
166        stroke: appTheme.MutedOrange,
167        strokeThickness: 3,
168        labelValue: "Drag Me!",
169        showLabel: true,
170        labelPlacement: ELabelPlacement.Top,
171        isEditable: true,
172        onDrag: (args: AnnotationDragDeltaEventArgs) => {
173            // Update the BaseValues adding more points around the annotation x coordinate, effectively zooming in that area and compressing elsewhere
174            const newBaseValues: number[] = [];
175            for (let x = 0; x <= 10; x++) {
176                if (x < args.sender.x1 - 0.5 || x > args.sender.x1 + 0.5) {
177                    newBaseValues.push(x);
178                } else {
179                    for (let d = -0.5; d <= 0.5; d += 0.1) {
180                        newBaseValues.push(args.sender.x1 + d);
181                    }
182                }
183            }
184            baseXValues.clear();
185            baseXValues.appendRange(newBaseValues);
186        },
187    });
188
189    sciChartSurface.annotations.add(magnifierAnnotation);
190
191    sciChartSurface.chartModifiers.add(
192        new ZoomPanModifier({ xyDirection: EXyDirection.XyDirection, includedXAxisIds: [xAxis.id] })
193    );
194    sciChartSurface.chartModifiers.add(new ZoomExtentsModifier({ includedXAxisIds: [xAxis.id] }));
195    sciChartSurface.chartModifiers.add(new MouseWheelZoomModifier({ includedXAxisIds: [xAxis.id] }));
196    sciChartSurface.chartModifiers.add(new CursorModifier({ showAxisLabels: true }));
197
198    return { sciChartSurface, wasmContext };
199};
200

BaseValue Axes – Angular

Overview

This example demonstrates how to use BaseValueAxis in an Angular application to create non-linear and adaptive chart axes using SciChart.js. BaseValue axes decouple numeric values from physical spacing, allowing developers to reshape the axis scale while preserving accurate numeric interaction.

Technical Implementation

The chart is hosted inside an Angular component using the scichart-angular integration. A custom initialization function creates a SciChartSurface and configures BaseValueAxis instances for both the X and Y axes.

The X-axis uses explicitly defined base values, while the Y-axis dynamically generates base values using a power-law algorithm. Custom tick providers are implemented to control major and minor tick placement, enabling precise logarithmic-style scaling.

A draggable vertical annotation dynamically modifies the X-axis base values, effectively expanding the scale around the annotation while compressing other regions. This illustrates how BaseValue axes can be used to implement interactive magnification and adaptive scaling techniques.

Features and Capabilities

Non-Linear Axis Control: Build logarithmic, exponential, or irregular scales without relying on fixed log axes.

Dynamic Data-Driven Scaling: Base values can be updated in response to user interaction or data changes.

Accurate Interaction Model: Cursor labels, zooming, and annotations remain consistent with the underlying numeric values.

Enterprise-Grade Performance: SciChart’s WebAssembly engine ensures smooth rendering and interaction even with complex axis logic.

Integration and Best Practices

This example follows best practices for integrating advanced axis behavior into Angular applications, including safe lifecycle management and dynamic axis updates. Developers building engineering, financial, or scientific dashboards can use this pattern as a foundation. For more information, refer to the SciChart Angular documentation and the BaseValue Axis documentation.

SciChart Ltd, 16 Beaufort Court, Admirals Way, Docklands, London, E14 9XL.