From 56bb51d1c8cf0c389dda47f4a96f45dd9e2ef138 Mon Sep 17 00:00:00 2001 From: Igor Cuckovic Date: Sun, 22 Feb 2026 20:36:48 +0100 Subject: [PATCH] SmithChart --- .../src/components/AppRouter/examplePaths.ts | 7 +- Examples/src/components/AppRouter/examples.ts | 1 + .../ModifyAxisBehavior/SmithChart/README.md | 68 ++ .../ModifyAxisBehavior/SmithChart/angular.ts | 15 + .../SmithChart/drawExample.ts | 634 ++++++++++++++++++ .../SmithChart/exampleInfo.tsx | 66 ++ .../ModifyAxisBehavior/SmithChart/index.tsx | 11 + .../SmithChart/smith-chart.jpg | Bin 0 -> 43248 bytes .../ModifyAxisBehavior/SmithChart/vanilla.ts | 19 + 9 files changed, 818 insertions(+), 3 deletions(-) create mode 100644 Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/README.md create mode 100644 Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/angular.ts create mode 100644 Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/drawExample.ts create mode 100644 Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/exampleInfo.tsx create mode 100644 Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/index.tsx create mode 100644 Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/smith-chart.jpg create mode 100644 Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/vanilla.ts diff --git a/Examples/src/components/AppRouter/examplePaths.ts b/Examples/src/components/AppRouter/examplePaths.ts index 52ef139ff..95f7aa0d1 100644 --- a/Examples/src/components/AppRouter/examplePaths.ts +++ b/Examples/src/components/AppRouter/examplePaths.ts @@ -14,7 +14,6 @@ export default [ "../Examples/Charts2D/AxisLabelCustomization/MultiLineLabels", "../Examples/Charts2D/AxisLabelCustomization/RotatedLabels", "../Examples/Charts2D/BasicChartTypes/BandSeriesChart", - "../Examples/Charts2D/v4Charts/HeatmapOverMap", "../Examples/Charts2D/BasicChartTypes/BubbleChart", "../Examples/Charts2D/BasicChartTypes/CandlestickChart", "../Examples/Charts2D/BasicChartTypes/ColumnChart", @@ -60,13 +59,14 @@ export default [ "../Examples/Charts2D/Filters/PercentageChange", "../Examples/Charts2D/Filters/TrendMARatio", "../Examples/Charts2D/Legends/ChartLegendsAPI", - "../Examples/Charts2D/ModifyAxisBehavior/DiscontinuousDateAxisComparison", "../Examples/Charts2D/ModifyAxisBehavior/BaseValueAxes", "../Examples/Charts2D/ModifyAxisBehavior/CentralAxes", + "../Examples/Charts2D/ModifyAxisBehavior/DiscontinuousDateAxisComparison", "../Examples/Charts2D/ModifyAxisBehavior/DrawBehindAxes", "../Examples/Charts2D/ModifyAxisBehavior/LogarithmicAxis", "../Examples/Charts2D/ModifyAxisBehavior/MultipleXAxes", "../Examples/Charts2D/ModifyAxisBehavior/SecondaryYAxes", + "../Examples/Charts2D/ModifyAxisBehavior/SmithChart", "../Examples/Charts2D/ModifyAxisBehavior/StaticAxis", "../Examples/Charts2D/ModifyAxisBehavior/VerticalCharts", "../Examples/Charts2D/ModifyAxisBehavior/VerticallyStackedAxes", @@ -128,6 +128,7 @@ export default [ "../Examples/Charts2D/v4Charts/AnimatedColumns", "../Examples/Charts2D/v4Charts/BoxPlotChart", "../Examples/Charts2D/v4Charts/GanttChart", + "../Examples/Charts2D/v4Charts/HeatmapOverMap", "../Examples/Charts2D/v4Charts/HeatmapRectangle", "../Examples/Charts2D/v4Charts/HistogramChart", "../Examples/Charts2D/v4Charts/LinearGauges", @@ -168,5 +169,5 @@ export default [ "../Examples/FeaturedApps/ShowCases/OilAndGasDashboard", "../Examples/FeaturedApps/ShowCases/PopulationPyramid", "../Examples/FeaturedApps/ShowCases/ServerTrafficDashboard", - "../Examples/FeaturedApps/ShowCases/WebsocketBigData", + "../Examples/FeaturedApps/ShowCases/WebsocketBigData" ]; diff --git a/Examples/src/components/AppRouter/examples.ts b/Examples/src/components/AppRouter/examples.ts index f4c6daa11..312a77e9a 100644 --- a/Examples/src/components/AppRouter/examples.ts +++ b/Examples/src/components/AppRouter/examples.ts @@ -193,6 +193,7 @@ export const MENU_ITEMS_2D: TMenuItem[] = [ EXAMPLES_PAGES.chart2D_modifyAxisBehavior_SecondaryYAxes, EXAMPLES_PAGES.chart2D_modifyAxisBehavior_VerticalCharts, EXAMPLES_PAGES.chart2D_modifyAxisBehavior_CentralAxes, + EXAMPLES_PAGES.chart2D_modifyAxisBehavior_SmithChart, EXAMPLES_PAGES.chart2D_modifyAxisBehavior_StaticAxis, EXAMPLES_PAGES.chart2D_modifyAxisBehavior_VerticallyStackedAxes, EXAMPLES_PAGES.chart2D_modifyAxisBehavior_LogarithmicAxis, diff --git a/Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/README.md b/Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/README.md new file mode 100644 index 000000000..c02b6aea5 --- /dev/null +++ b/Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/README.md @@ -0,0 +1,68 @@ +# Central Axes (Central Axes Layout Example) + +## Overview + +This example demonstrates how to customize the axes layout in SciChart.js by placing the axes in the center of the chart. The example creates a chart with central axes that cross at the data value (0,0) (oscilloscope style) and includes implementations for React, Vanilla JavaScript/TypeScript, and Angular. + +## Technologies Used + +- SciChart.js (core charting library) +- SciChart Angular Component +- SciChart React Component +- TypeScript and JavaScript + +## Code Explanation + +The heart of the example is the `drawExample` function (provided in both JavaScript and TypeScript versions). This function creates a SciChart Surface and configures a custom layout manager (CentralAxesLayoutManager) to display the x- and y-axes centrally. Key points include: + +- **Chart Creation:** A SciChart Surface is created with a specified theme. +- **Custom Axis Layout:** The `CentralAxesLayoutManager` is instantiated with options to position the horizontal and vertical axes at data value 0, ensuring that the axes pan with the chart. +- **Axis Configuration:** The x-axis and y-axis are configured as inner axes by setting `isInnerAxis` to true. This positions them in the center of the chart. Additional styling (like label color and axis borders) is applied. +- **Data Series and Annotation:** A fast line renderable series is added that draws a butterfly curve generated by a helper function. Furthermore, a text annotation is added to describe the chart’s functionality. +- **Interaction Modifiers:** Standard chart interaction modifiers (zoom pan, mouse wheel zoom, and zoom extents) are included to allow users to interact with the chart. + +The example also includes framework-specific files: + +- **Angular:** The `angular.ts` file sets up a standalone Angular component that uses the SciChart Angular component and binds its `drawExample` function. +- **React:** The `index.tsx` file presents a React component which uses the SciChart React wrapper to initialize the chart with the `drawExample` function. +- **Vanilla:** The `vanilla.js` and `vanilla.ts` files demonstrate how to create and dispose of the chart in a plain JavaScript/TypeScript environment. + +## Customization + +Key configuration options in this example include: + +- **Axis Positioning:** You can modify the options passed to the `CentralAxesLayoutManager` to change the axis alignment. By default, the axes are set to cross at (0,0) using the data value coordinate mode, but commented code shows how to use relative coordinate modes as well. +- **Animation:** The fast line renderable series uses a fade animation with a duration of 500ms, which can be adjusted as needed. +- **Styling:** Colors for axis labels, axis borders, and the renderable series stroke are set based on a theme. These can be customized to match your desired appearance. + +## Running the Example + +To run any example from the SciChart.JS.Examples repository, follow these steps: + +1. **Clone the Repository**: Download the entire repository to your local machine using Git: + + ```bash + git clone https://github.com/ABTSoftware/SciChart.JS.Examples.git + ``` + +2. **Navigate to the Examples Directory**: Change into the `Examples` folder: + + ```bash + cd SciChart.JS.Examples/Examples + ``` + +3. **Install Dependencies**: Install the necessary packages using npm: + + ```bash + npm install + ``` + +4. **Run the Development Server**: Start the development server to view and interact with the examples: + + ```bash + npm run dev + ``` + + This will launch the demo application, allowing you to explore various examples, including the one in question. + +For more detailed instructions, refer to the [SciChart.JS.Examples README](https://github.com/ABTSoftware/SciChart.JS.Examples/blob/master/README.md). diff --git a/Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/angular.ts b/Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/angular.ts new file mode 100644 index 000000000..1e5fa5e21 --- /dev/null +++ b/Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/angular.ts @@ -0,0 +1,15 @@ +import { Component } from "@angular/core"; +import { ScichartAngularComponent } from "scichart-angular"; +import { drawExample } from "./drawExample"; + +@Component({ + standalone: true, + selector: "app-root", + imports: [ScichartAngularComponent], + template: ``, +}) +export class AppComponent { + title = "scichart-angular-app"; + + drawExample = drawExample; +} diff --git a/Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/drawExample.ts b/Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/drawExample.ts new file mode 100644 index 000000000..cd5c22779 --- /dev/null +++ b/Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/drawExample.ts @@ -0,0 +1,634 @@ +import { appTheme } from "../../../theme"; +import { + ChartModifierBase2D, + EAxisAlignment, + ECoordinateMode, + EHorizontalAnchorPoint, + EllipsePointMarker, + EVerticalAnchorPoint, + FastLineRenderableSeries, + ModifierMouseArgs, + MouseWheelZoomModifier, + NumericAxis, + NumberRange, + PinchZoomModifier, + SciChartSurface, + TextAnnotation, + TSciChart, + XyDataSeries, + XyScatterRenderableSeries, + ZoomExtentsModifier, +} from "scichart"; + +export const drawExample = async (rootElement: string | HTMLDivElement) => { + const { sciChartSurface, wasmContext } = await SciChartSurface.create(rootElement, { + theme: appTheme.SciChartJsTheme, + }); + + // Configure axes for Smith Chart (-1.15 to 1.15 range to show the full chart with padding) + sciChartSurface.xAxes.add( + new NumericAxis(wasmContext, { + visibleRange: new NumberRange(-1.15, 1.15), + axisAlignment: EAxisAlignment.Bottom, + drawMajorGridLines: false, + drawMinorGridLines: false, + drawMajorBands: false, + drawLabels: false, + drawMajorTickLines: false, + drawMinorTickLines: false, + }) + ); + + sciChartSurface.yAxes.add( + new NumericAxis(wasmContext, { + visibleRange: new NumberRange(-1.15, 1.15), + axisAlignment: EAxisAlignment.Left, + drawMajorGridLines: false, + drawMinorGridLines: false, + drawMajorBands: false, + drawLabels: false, + drawMajorTickLines: false, + drawMinorTickLines: false, + }) + ); + + // Define colors for grid lines + const gridColor = appTheme.ForegroundColor + "30"; // Light gray with transparency + const outerCircleColor = appTheme.ForegroundColor + "80"; // Darker for outer circle + + // Outer circle (unit circle) + sciChartSurface.renderableSeries.add( + new FastLineRenderableSeries(wasmContext, { + dataSeries: createCircle(wasmContext, 0, 0, 1, 360), + stroke: outerCircleColor, + strokeThickness: 2, + }) + ); + + // Constant resistance circles (r values) + const resistanceValues = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 3.0, 4.0, 5.0, 10, 20, 50]; + const majorResistanceValues = [0.2, 0.5, 1.0, 2.0, 5.0, 10, 20, 50]; + + resistanceValues.forEach((r) => { + const center = r / (1 + r); + const radius = 1 / (1 + r); + const isMajor = majorResistanceValues.includes(r); + + sciChartSurface.renderableSeries.add( + new FastLineRenderableSeries(wasmContext, { + dataSeries: createCircle(wasmContext, center, 0, radius, 360), + stroke: gridColor, + strokeThickness: isMajor ? 1 : 0.5, + }) + ); + + // Add resistance labels on the horizontal axis + if (isMajor) { + const labelX = center - radius; + sciChartSurface.annotations.add( + new TextAnnotation({ + text: r >= 1 ? r.toString() : r.toFixed(1), + x1: labelX, + y1: 0, + xCoordShift: 0, + yCoordShift: -15, + fontSize: 10, + textColor: appTheme.ForegroundColor, + opacity: 0.7, + horizontalAnchorPoint: EHorizontalAnchorPoint.Center, + }) + ); + } + }); + + // Constant reactance arcs (x values) + const reactanceValues = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 3.0, 4.0, 5.0, 10, 20, 50]; + const majorReactanceValues = [0.2, 0.5, 1.0, 2.0, 5.0, 10, 20, 50]; + + // Positive reactance (upper half) + reactanceValues.forEach((x) => { + const isMajor = majorReactanceValues.includes(x); + + sciChartSurface.renderableSeries.add( + new FastLineRenderableSeries(wasmContext, { + dataSeries: createReactanceArc(wasmContext, x, true), + stroke: gridColor, + strokeThickness: isMajor ? 1 : 0.5, + }) + ); + + // Add reactance labels on the outer circle where the arc meets it + if (isMajor) { + const xVal2 = x * x; + const labelX = (xVal2 - 1) / (1 + xVal2); + const labelY = 2 * x / (1 + xVal2); + + sciChartSurface.annotations.add( + new TextAnnotation({ + text: `+${x >= 1 ? x.toString() : x.toFixed(1)}`, + x1: labelX, + y1: labelY, + xCoordShift: 15, + yCoordShift: 0, + fontSize: 10, + textColor: appTheme.ForegroundColor, + opacity: 0.7, + horizontalAnchorPoint: EHorizontalAnchorPoint.Left, + verticalAnchorPoint: EVerticalAnchorPoint.Center, + }) + ); + } + }); + + // Negative reactance (lower half) + reactanceValues.forEach((x) => { + const isMajor = majorReactanceValues.includes(x); + + sciChartSurface.renderableSeries.add( + new FastLineRenderableSeries(wasmContext, { + dataSeries: createReactanceArc(wasmContext, x, false), + stroke: gridColor, + strokeThickness: isMajor ? 1 : 0.5, + }) + ); + + // Add reactance labels on the outer circle where the arc meets it + if (isMajor) { + const xVal2 = x * x; + const labelX = (xVal2 - 1) / (1 + xVal2); + const labelY = -2 * x / (1 + xVal2); + + sciChartSurface.annotations.add( + new TextAnnotation({ + text: `-${x >= 1 ? x.toString() : x.toFixed(1)}`, + x1: labelX, + y1: labelY, + xCoordShift: 15, + yCoordShift: 0, + fontSize: 10, + textColor: appTheme.ForegroundColor, + opacity: 0.7, + horizontalAnchorPoint: EHorizontalAnchorPoint.Left, + verticalAnchorPoint: EVerticalAnchorPoint.Center, + }) + ); + } + }); + + // Add horizontal axis line (real axis) + sciChartSurface.renderableSeries.add( + new FastLineRenderableSeries(wasmContext, { + dataSeries: createLine(wasmContext, -1, 0, 1, 0), + stroke: gridColor, + strokeThickness: 1, + }) + ); + + // Add axis label at the left end + sciChartSurface.annotations.add( + new TextAnnotation({ + text: "0.0", + x1: -1, + y1: 0, + xCoordShift: -20, + yCoordShift: 0, + fontSize: 10, + textColor: appTheme.ForegroundColor, + opacity: 0.7, + horizontalAnchorPoint: EHorizontalAnchorPoint.Right, + verticalAnchorPoint: EVerticalAnchorPoint.Center, + }) + ); + + // ═══════════════════════════════════════════════════════ + // INTERACTIVE ELEMENTS - Draggable point with highlights + // ═══════════════════════════════════════════════════════ + + const rHighlightColor = "#FF4444"; // Red for constant resistance + const xHighlightColor = "#4488FF"; // Blue for constant reactance + const gammaColor = "#44CC44"; // Green for |Γ| and point + + // Highlighted constant-R circle (red) + const rCircleDS = new XyDataSeries(wasmContext); + sciChartSurface.renderableSeries.add( + new FastLineRenderableSeries(wasmContext, { + dataSeries: rCircleDS, + stroke: rHighlightColor, + strokeThickness: 2.5, + }) + ); + + // Highlighted constant-X arc (blue) + const xArcDS = new XyDataSeries(wasmContext); + sciChartSurface.renderableSeries.add( + new FastLineRenderableSeries(wasmContext, { + dataSeries: xArcDS, + stroke: xHighlightColor, + strokeThickness: 2.5, + }) + ); + + // |Γ| circle (green, dashed) + const gammaCircleDS = new XyDataSeries(wasmContext); + sciChartSurface.renderableSeries.add( + new FastLineRenderableSeries(wasmContext, { + dataSeries: gammaCircleDS, + stroke: gammaColor, + strokeThickness: 1.5, + strokeDashArray: [5, 5], + }) + ); + + // Draggable point marker (green) + const pointDS = new XyDataSeries(wasmContext); + pointDS.append(0, 0); + sciChartSurface.renderableSeries.add( + new XyScatterRenderableSeries(wasmContext, { + dataSeries: pointDS, + pointMarker: new EllipsePointMarker(wasmContext, { + width: 14, + height: 14, + fill: gammaColor, + stroke: "#FFFFFF", + strokeThickness: 2, + }), + }) + ); + + // ═══════════════════════════════════════════════════════ + // TEXT READOUTS - Γ, |Γ|, Z displayed on chart + // ═══════════════════════════════════════════════════════ + + const gammaTextAnnotation = new TextAnnotation({ + x1: 0.02, + y1: 0.02, + xCoordinateMode: ECoordinateMode.Relative, + yCoordinateMode: ECoordinateMode.Relative, + text: "Γ = 0.000 + j0.000", + fontSize: 14, + fontFamily: "monospace", + textColor: appTheme.ForegroundColor, + horizontalAnchorPoint: EHorizontalAnchorPoint.Left, + verticalAnchorPoint: EVerticalAnchorPoint.Top, + }); + + const gammaMagTextAnnotation = new TextAnnotation({ + x1: 0.02, + y1: 0.06, + xCoordinateMode: ECoordinateMode.Relative, + yCoordinateMode: ECoordinateMode.Relative, + text: "|Γ| = 0.000", + fontSize: 14, + fontFamily: "monospace", + textColor: gammaColor, + horizontalAnchorPoint: EHorizontalAnchorPoint.Left, + verticalAnchorPoint: EVerticalAnchorPoint.Top, + }); + + const impedanceTextAnnotation = new TextAnnotation({ + x1: 0.02, + y1: 0.10, + xCoordinateMode: ECoordinateMode.Relative, + yCoordinateMode: ECoordinateMode.Relative, + text: "Z = 1.000 + j0.000", + fontSize: 14, + fontFamily: "monospace", + textColor: appTheme.ForegroundColor, + horizontalAnchorPoint: EHorizontalAnchorPoint.Left, + verticalAnchorPoint: EVerticalAnchorPoint.Top, + }); + + const resistanceLabel = new TextAnnotation({ + x1: 0.02, + y1: 0.14, + xCoordinateMode: ECoordinateMode.Relative, + yCoordinateMode: ECoordinateMode.Relative, + text: "R = 1.000", + fontSize: 14, + fontFamily: "monospace", + textColor: rHighlightColor, + horizontalAnchorPoint: EHorizontalAnchorPoint.Left, + verticalAnchorPoint: EVerticalAnchorPoint.Top, + }); + + const reactanceLabel = new TextAnnotation({ + x1: 0.02, + y1: 0.18, + xCoordinateMode: ECoordinateMode.Relative, + yCoordinateMode: ECoordinateMode.Relative, + text: "X = 0.000", + fontSize: 14, + fontFamily: "monospace", + textColor: xHighlightColor, + horizontalAnchorPoint: EHorizontalAnchorPoint.Left, + verticalAnchorPoint: EVerticalAnchorPoint.Top, + }); + + sciChartSurface.annotations.add( + gammaTextAnnotation, + gammaMagTextAnnotation, + impedanceTextAnnotation, + resistanceLabel, + reactanceLabel + ); + + // ═══════════════════════════════════════════════════════ + // UPDATE LOGIC - Recalculates all visuals when point moves + // ═══════════════════════════════════════════════════════ + + function updateInteractiveElements(gammaR: number, gammaI: number) { + // Clamp to unit circle + const mag = Math.sqrt(gammaR * gammaR + gammaI * gammaI); + let gr = gammaR; + let gi = gammaI; + if (mag > 1) { + gr = gammaR / mag; + gi = gammaI / mag; + } + + const gammaMag = Math.sqrt(gr * gr + gi * gi); + + // Compute impedance: Z = (1 + Γ) / (1 - Γ) = R + jX + const denom = (1 - gr) * (1 - gr) + gi * gi; + const r = denom > 1e-10 ? (1 - gr * gr - gi * gi) / denom : Infinity; + const x = denom > 1e-10 ? (2 * gi) / denom : Infinity; + + // Update draggable point position + pointDS.clear(); + pointDS.append(gr, gi); + + // Update highlighted R circle + populateRCircle(rCircleDS, r); + + // Update highlighted X arc + populateXArc(xArcDS, x); + + // Update |Γ| circle + populateCircle(gammaCircleDS, 0, 0, gammaMag); + + // Update text readouts + const signG = gi >= 0 ? "+" : "−"; + gammaTextAnnotation.text = `Γ = ${gr.toFixed(3)} ${signG} j${Math.abs(gi).toFixed(3)}`; + gammaMagTextAnnotation.text = `|Γ| = ${gammaMag.toFixed(3)}`; + + if (!isFinite(r) || !isFinite(x)) { + impedanceTextAnnotation.text = "Z = ∞"; + resistanceLabel.text = "R = ∞"; + reactanceLabel.text = "X = ∞"; + } else { + const signZ = x >= 0 ? "+" : "−"; + impedanceTextAnnotation.text = `Z = ${r.toFixed(3)} ${signZ} j${Math.abs(x).toFixed(3)}`; + resistanceLabel.text = `R = ${r.toFixed(3)}`; + reactanceLabel.text = `X = ${x >= 0 ? "" : "−"}${Math.abs(x).toFixed(3)}`; + } + } + + // Populate an XyDataSeries with a constant-R circle + function populateRCircle(ds: XyDataSeries, r: number) { + ds.clear(); + if (!isFinite(r) || r < 0) return; + const cx = r / (1 + r); + const rad = 1 / (1 + r); + const n = 200; + for (let i = 0; i <= n; i++) { + const angle = (i / n) * 2 * Math.PI; + ds.append(cx + rad * Math.cos(angle), rad * Math.sin(angle)); + } + } + + // Populate an XyDataSeries with a constant-X reactance arc + function populateXArc(ds: XyDataSeries, xVal: number) { + ds.clear(); + if (!isFinite(xVal)) return; + + // Near zero: draw horizontal axis + if (Math.abs(xVal) < 0.001) { + ds.append(-1, 0); + ds.append(1, 0); + return; + } + + const absX = Math.abs(xVal); + const isPos = xVal > 0; + const radius = 1 / absX; + const cx = 1; + const cy = isPos ? radius : -radius; + + const xv2 = absX * absX; + const xInt = (xv2 - 1) / (1 + xv2); + const yInt = isPos ? (2 * absX) / (1 + xv2) : (-2 * absX) / (1 + xv2); + + const thetaOther = Math.atan2(yInt - cy, xInt - cx); + const thetaOrigin = isPos ? -Math.PI / 2 : Math.PI / 2; + + let startAngle: number; + let endAngle: number; + + if (isPos) { + startAngle = thetaOther; + endAngle = thetaOrigin; + while (endAngle <= startAngle) endAngle += 2 * Math.PI; + } else { + startAngle = thetaOrigin; + endAngle = thetaOther; + while (endAngle <= startAngle) endAngle += 2 * Math.PI; + } + + const n = 200; + for (let i = 0; i <= n; i++) { + const angle = startAngle + (i / n) * (endAngle - startAngle); + ds.append(cx + radius * Math.cos(angle), cy + radius * Math.sin(angle)); + } + } + + // Populate an XyDataSeries with a circle centered at (cx, cy) with given radius + function populateCircle(ds: XyDataSeries, cx: number, cy: number, radius: number) { + ds.clear(); + if (radius < 0.001) return; + const n = 200; + for (let i = 0; i <= n; i++) { + const angle = (i / n) * 2 * Math.PI; + ds.append(cx + radius * Math.cos(angle), cy + radius * Math.sin(angle)); + } + } + + // Initialize interactive elements at the origin (Γ = 0 → Z = 1) + updateInteractiveElements(0, 0); + + // ═══════════════════════════════════════════════════════ + // CUSTOM CHART MODIFIER - Handles click & drag on chart + // ═══════════════════════════════════════════════════════ + + class SmithChartDragModifier extends ChartModifierBase2D { + readonly type = "SmithChartDragModifier"; + private isDragging = false; + + modifierMouseDown(args: ModifierMouseArgs) { + super.modifierMouseDown(args); + this.isDragging = true; + this.handleDrag(args); + args.handled = true; + } + + modifierMouseMove(args: ModifierMouseArgs) { + super.modifierMouseMove(args); + if (this.isDragging) { + this.handleDrag(args); + args.handled = true; + } + } + + modifierMouseUp(args: ModifierMouseArgs) { + super.modifierMouseUp(args); + this.isDragging = false; + } + + private handleDrag(args: ModifierMouseArgs) { + const { mousePoint } = args; + const xCalc = this.parentSurface.xAxes.get(0).getCurrentCoordinateCalculator(); + const yCalc = this.parentSurface.yAxes.get(0).getCurrentCoordinateCalculator(); + + const dataX = xCalc.getDataValue(mousePoint.x); + const dataY = yCalc.getDataValue(mousePoint.y); + + updateInteractiveElements(dataX, dataY); + } + } + + // Add interaction modifiers + sciChartSurface.chartModifiers.add( + new SmithChartDragModifier(), + new MouseWheelZoomModifier(), + new ZoomExtentsModifier(), + new PinchZoomModifier() + ); + + // Preserve aspect ratio to always show circle (1:1 aspect ratio) + const xAxis = sciChartSurface.xAxes.get(0); + const yAxis = sciChartSurface.yAxes.get(0); + + sciChartSurface.preRender.subscribe(() => { + const result = preserveAspectRatio( + sciChartSurface.viewRect.width, + sciChartSurface.viewRect.height, + xAxis.visibleRange.min, + xAxis.visibleRange.max, + yAxis.visibleRange.min, + yAxis.visibleRange.max + ); + + xAxis.visibleRange = new NumberRange(result.minVisibleX, result.maxVisibleX); + yAxis.visibleRange = new NumberRange(result.minVisibleY, result.maxVisibleY); + }); + + return { sciChartSurface, wasmContext }; +}; + +// ═══════════════════════════════════════════════════════ +// HELPER FUNCTIONS +// ═══════════════════════════════════════════════════════ + +// Preserve aspect ratio (ensures circles remain circular) +function preserveAspectRatio( + width: number, + height: number, + minVisibleX: number, + maxVisibleX: number, + minVisibleY: number, + maxVisibleY: number +) { + const visibleWidth = maxVisibleX - minVisibleX; + const visibleHeight = maxVisibleY - minVisibleY; + const containerAspectRatio = width / height; + const visibleAspectRatio = visibleWidth / visibleHeight; + + let newMinX: number, newMaxX: number, newMinY: number, newMaxY: number; + + if (containerAspectRatio > visibleAspectRatio) { + const newVisibleWidth = visibleHeight * containerAspectRatio; + const widthDiff = newVisibleWidth - visibleWidth; + newMinX = minVisibleX - widthDiff / 2; + newMaxX = maxVisibleX + widthDiff / 2; + newMinY = minVisibleY; + newMaxY = maxVisibleY; + } else { + const newVisibleHeight = visibleWidth / containerAspectRatio; + const heightDiff = newVisibleHeight - visibleHeight; + newMinX = minVisibleX; + newMaxX = maxVisibleX; + newMinY = minVisibleY - heightDiff / 2; + newMaxY = maxVisibleY + heightDiff / 2; + } + + return { minVisibleX: newMinX, maxVisibleX: newMaxX, minVisibleY: newMinY, maxVisibleY: newMaxY }; +} + +// Create a circle as an XyDataSeries +function createCircle(wasmContext: TSciChart, cx: number, cy: number, radius: number, points: number = 360): XyDataSeries { + const dataSeries = new XyDataSeries(wasmContext); + for (let i = 0; i <= points; i++) { + const angle = (i / points) * 2 * Math.PI; + dataSeries.append(cx + radius * Math.cos(angle), cy + radius * Math.sin(angle)); + } + return dataSeries; +} + +// Create a reactance arc as an XyDataSeries. +// For reactance value xVal, the circle has center (1, 1/xVal) and radius 1/xVal (positive) +// or center (1, -1/xVal) and radius 1/xVal (negative). +// All arcs pass through (1, 0) and curve to the other intersection with the unit circle. +function createReactanceArc( + wasmContext: TSciChart, + xVal: number, + isPositive: boolean, + numPoints: number = 200 +): XyDataSeries { + const dataSeries = new XyDataSeries(wasmContext); + const radius = 1 / xVal; + const cx = 1; + const cy = isPositive ? radius : -radius; + + // The reactance circle intersects the unit circle at two points: + // 1) Always at (1, 0) + // 2) At ((xVal²-1)/(1+xVal²), ±2·xVal/(1+xVal²)) + const xVal2 = xVal * xVal; + const xInt = (xVal2 - 1) / (1 + xVal2); + const yInt = isPositive ? (2 * xVal) / (1 + xVal2) : (-2 * xVal) / (1 + xVal2); + + // Angle from circle center to the second intersection point + const thetaOther = Math.atan2(yInt - cy, xInt - cx); + + // Angle from circle center to (1, 0): + // Positive: atan2(0 - 1/xVal, 0) = -π/2 + // Negative: atan2(0 + 1/xVal, 0) = π/2 + const thetaOrigin = isPositive ? -Math.PI / 2 : Math.PI / 2; + + let startAngle: number, endAngle: number; + + if (isPositive) { + // Sweep counterclockwise from thetaOther to thetaOrigin + startAngle = thetaOther; + endAngle = thetaOrigin; + while (endAngle <= startAngle) endAngle += 2 * Math.PI; + } else { + // Sweep counterclockwise from thetaOrigin to thetaOther + startAngle = thetaOrigin; + endAngle = thetaOther; + while (endAngle <= startAngle) endAngle += 2 * Math.PI; + } + + for (let i = 0; i <= numPoints; i++) { + const angle = startAngle + (i / numPoints) * (endAngle - startAngle); + dataSeries.append(cx + radius * Math.cos(angle), cy + radius * Math.sin(angle)); + } + + return dataSeries; +} + +// Create a straight line as an XyDataSeries +function createLine(wasmContext: TSciChart, x1: number, y1: number, x2: number, y2: number): XyDataSeries { + const dataSeries = new XyDataSeries(wasmContext); + dataSeries.append(x1, y1); + dataSeries.append(x2, y2); + return dataSeries; +} diff --git a/Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/exampleInfo.tsx b/Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/exampleInfo.tsx new file mode 100644 index 000000000..0b9b36245 --- /dev/null +++ b/Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/exampleInfo.tsx @@ -0,0 +1,66 @@ +import { createExampleInfo } from "../../../exampleInfoUtils"; +import { IExampleMetadata } from "../../../IExampleMetadata"; + +const metaData: IExampleMetadata = + //// This metadata is computer generated - do not edit! + { + reactComponent: "SmithChart", + id: "chart2D_modifyAxisBehavior_SmithChart", + imagePath: "smith-chart.jpg", + description: + "Demonstrates how to create a **JavaScript Chart with Smith Chart** using SciChart.js, High Performance JavaScript Charts", + tips: [], + frameworks: { + javascript: { + subtitle: + "Demonstrates how to create a **JavaScript Chart with Smith Chart** using SciChart.js, High Performance JavaScript Charts", + title: "JavaScript Chart with Smith Chart", + pageTitle: "JavaScript Chart with Smith Chart", + metaDescription: + "Demonstrates Smith Chart on a JavaScript Chart using SciChart.js. SciChart supports unlimited left, right, top, bottom X, Y axis with configurable layout", + markdownContent: + "## Smith Chart JavaScript Chart\n\n### Overview\nThis example demonstrates how to create a high-performance chart with **Smith Chart** using SciChart.js in a JavaScript framework. The chart positions the x and y axes to cross at the data value (0,0), producing an oscilloscope-like layout. It leverages the advanced customization available in SciChart.js for axis placement, as detailed in the [Central Axis Layout documentation](https://www.scichart.com/documentation/js/v5/2d-charts/axis-api/multi-axis-and-layout/central-axis-layout/).\n\n### Technical Implementation\nThe implementation begins by initializing a `SciChartSurface` with an integrated WebAssembly context, following guidelines from the [Tutorial 01 - Including SciChart.js in an HTML Page using CDN](https://www.scichart.com/documentation/js/v5/get-started/tutorials-cdn/tutorial-01-using-cdn/) documentation. Both the x and y axes are configured as inner axes by enabling the `axis.isInnerAxis` property and aligned to the center by setting the `sciChartSurface.layoutManager` property equal to a `SmithChartLayoutManager` instance with data value based positioning. A dynamically generated butterfly curve is rendered using the `FastLineRenderableSeries` and utilizes a fade animation as provided by the [Animations API](https://www.scichart.com/documentation/js/v5/2d-charts/animations-api/animations-api-overview/) for performance and visual enhancement. Interaction is further enriched with modifiers such as `ZoomPanModifier`, `MouseWheelZoomModifier`, and `ZoomExtentsModifier`, which support smooth zooming and panning.\n\n### Features and Capabilities\nThis example showcases advanced chart capabilities such as real-time data rendering for complex curves and dynamic interactivity. The efficient use of WebAssembly ensures high performance even when visualizing large data sets. Additionally, the chart demonstrates clear separation of concerns by isolating initialization, axis configuration, data rendering, and user interaction, which allows for robust customization based on the application’s needs.\n\n### Integration and Best Practices\nIntegrating SciChart.js into a JavaScript application is streamlined by employing a modular approach where the chart initialization function is directly invoked with a designated root element. Resource cleanup is handled by returning a destructor function that calls the `delete()` method on the `SciChartSurface`, in line with best practices suggested in the [Memory Best Practices](https://www.scichart.com/documentation/js/v5/2d-charts/performance-tips/memory-best-practices/) documentation. Developers looking to extend or optimize their chart applications can also refer to the [Getting Started with SciChart JS](https://www.scichart.com/getting-started/scichart-javascript/) guide for additional insights on integration and performance optimization.", + }, + react: { + subtitle: + "Demonstrates how to create a **React Chart with Smith Chart** using SciChart.js, High Performance JavaScript Charts", + title: "React Chart with Smith Chart", + pageTitle: "React Chart with Smith Chart", + metaDescription: + "Demonstrates Smith Chart on a React Chart using SciChart.js. SciChart supports unlimited left, right, top, bottom X, Y axis with configurable layout", + markdownContent: + "## React Chart with Smith Chart\n\n### Overview\nThis example demonstrates how to create a high performance React chart with Smith Chart using SciChart.js. The chart leverages a custom Smith Chart layout, showcasing how axes can be positioned in the center of the chart by setting the `axis.isInnerAxis` property and using the `SmithChartLayoutManager`.\n\n### Technical Implementation\nThe chart is initialized through the `` component by passing the drawExample function as a prop, a pattern that follows [best practices for React integration](https://www.scichart.com/blog/react-charts-with-scichart-js/). The drawExample function sets up the `SciChartSurface` and configures the Smith Chart by using the `SmithChartLayoutManager` with options that specify data value based positioning. `NumericAxis` instances are added with the `isInnerAxis` flag enabled to ensure that the axes are rendered inside the chart. For more detailed information on central axis customization, please refer to the [Central Axis Layout documentation](https://www.scichart.com/documentation/js/v5/2d-charts/axis-api/multi-axis-and-layout/central-axis-layout/).\n\n### Features and Capabilities\nThe example features an efficient data series generation that calculates a butterfly curve and displays it using `FastLineRenderableSeries` with a fade animation, as described in [The Animations API](https://www.scichart.com/documentation/js/v5/2d-charts/animations-api/animations-api-overview/). Interaction is enhanced by including several chart modifiers such as `ZoomPanModifier`, `MouseWheelZoomModifier`, and `ZoomExtentsModifier`, providing dynamic zoom and pan capabilities which can be explored further in the [ZoomPanModifier documentation](https://www.scichart.com/documentation/js/v5/2d-charts/chart-modifier-api/zooming-and-panning/zoom-pan-modifier/). Additionally, a `TextAnnotation` is added to explain the chart's functionality.\n\n### Integration and Best Practices\nThis React implementation follows a modular approach by using the `` component to encapsulate chart initialization, making it easier to integrate SciChart.js into larger React applications. The example illustrates proper use of inner axis configurations with `NumericAxis`, supporting efficient performance and clear visual presentation. Developers looking to extend the feature set or optimize performance can find further guidance in the [Numeric Axis documentation](https://www.scichart.com/documentation/js/v5/2d-charts/axis-api/axis-types/numeric-axis/) as well as the [React Charts with SciChart.js article](https://www.scichart.com/blog/react-charts-with-scichart-js/).", + }, + angular: { + subtitle: + "Demonstrates how to create a **Angular Chart with Smith Chart** using SciChart.js, High Performance JavaScript Charts", + title: "Angular Chart with Smith Chart", + pageTitle: "Angular Chart with Smith Chart", + metaDescription: + "Demonstrates Smith Chart on a Angular Chart using SciChart.js. SciChart supports unlimited left, right, top, bottom X, Y axis with configurable layout", + markdownContent: + "## Angular Chart with Smith Chart Example\n\n### Overview\nThis Angular example demonstrates how to create a high-performance chart with **Smith Chart** using SciChart.js. The chart positions both axes in the center by using a custom central axis layout and inner axis configuration. It leverages the [scichart-angular](https://www.npmjs.com/package/scichart-angular) package, which simplifies integration with Angular projects using the `ScichartAngularComponent`.\n\n### Technical Implementation\nThe implementation initializes a `SciChartSurface` using the Angular standalone component, which passes the chart setup function through the `[initChart]` property. The chart employs the `SmithChartLayoutManager` with options set to position the axes based on data values, ensuring that the axes cross at (0,0). Both the x and y axes are defined as inner axes by setting the `axis.isInnerAxis` property, as detailed in the [Central Axis Layout documentation](https://www.scichart.com/documentation/js/v5/2d-charts/axis-api/multi-axis-and-layout/central-axis-layout/). A `FastLineRenderableSeries` is used to render a dynamically generated butterfly curve, with a fade animation enhancing performance. Additional interactivity is provided through the `ZoomPanModifier`, `MouseWheelZoomModifier`, and `ZoomExtentsModifier`, which offer smooth zooming and panning as described in the [Tutorial 03 - Adding Zooming, Panning Behavior](https://www.scichart.com/documentation/js/v5/get-started/tutorials-js-npm-webpack/tutorial-03-adding-zooming-panning-behavior/).\n\n### Features and Capabilities\nThis example highlights advanced chart capabilities such as real-time updates, dynamic data rendering, and a unique oscilloscope style layout. The efficient rendering of complex data series, such as the butterfly curve, is optimized by the use of `FastLineRenderableSeries` and `FadeAnimation`, supporting high performance even with large data sets. For insights into performance optimization, developers can refer to the techniques discussed in [Performance Optimisation of JavaScript Applications & Charts](https://www.scichart.com/blog/performance-optimisation-of-javascript-applications-charts/).\n\n### Integration and Best Practices\nFollowing Angular best practices, the example uses a standalone component to encapsulate chart initialization, which promotes modularity and ease of management within larger Angular applications. Developers are encouraged to consult the [Getting Started with SciChart JS](https://www.scichart.com/getting-started/scichart-javascript/) guide for further details on Angular application integration techniques. This example provides a robust reference for implementing complex axis layouts, interactive modifiers, and performance optimizations in Angular environments.", + }, + }, + documentationLinks: [ + { + href: "https://www.scichart.com/documentation/js/v5/2d-charts/axis-api/multi-axis-and-layout/central-axis-layout/", + title: "SciChart.js Central Axis Documentation page", + linkTitle: "Central Axis documentation", + }, + ], + path: "smith-chart", + metaKeywords: "multiple, axis, chart, javascript, webgl, canvas", + onWebsite: true, + filepath: "Charts2D/ModifyAxisBehavior/SmithChart", + thumbnailImage: "smith-chart.jpg", + sandboxConfig: {}, + markdownContent: null, + pageLayout: "default", + extraDependencies: {}, + isNew: false, + }; +//// End of computer generated metadata + +export const SmithChartExampleInfo = createExampleInfo(metaData); +export default SmithChartExampleInfo; diff --git a/Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/index.tsx b/Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/index.tsx new file mode 100644 index 000000000..aa8d648b1 --- /dev/null +++ b/Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/index.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; +import { appTheme } from "../../../theme"; +import { SciChartReact } from "scichart-react"; +import commonClasses from "../../../styles/Examples.module.scss"; +import { drawExample } from "./drawExample"; + +// React component needed as our examples app is react. +// SciChart can be used in Angular, Vue, Blazor and vanilla JS! See our Github repo for more info +export default function ChartComponent() { + return ; +} diff --git a/Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/smith-chart.jpg b/Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/smith-chart.jpg new file mode 100644 index 0000000000000000000000000000000000000000..351db0f9e884fe85c9a5c2722e2d47beb0a3be0b GIT binary patch literal 43248 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 literal 0 HcmV?d00001 diff --git a/Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/vanilla.ts b/Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/vanilla.ts new file mode 100644 index 000000000..4f87ed08b --- /dev/null +++ b/Examples/src/components/Examples/Charts2D/ModifyAxisBehavior/SmithChart/vanilla.ts @@ -0,0 +1,19 @@ +import { drawExample } from "./drawExample"; + +/** + * Creates charts on the provided root elements + * @returns cleanup function + */ +const create = async () => { + const { sciChartSurface } = await drawExample("chart"); + + const destructor = () => { + sciChartSurface.delete(); + }; + + return destructor; +}; + +create(); + +// call the `destructor` returned by the `create` promise to dispose the charts when necessary