inlann commented on issue #13664: URL: https://github.com/apache/echarts/issues/13664#issuecomment-1491214480
@FreidoGit Hi. > I implemented a smith chart using Echarts: The following code is the initial version: ```typescript /** * Smith Chart calculation: * known: * real, imag, PI, W(diameter of max circle, equals to Math.atan(Infinity)), * * unknown: * alfa(the angle alfa between the imag circle and the outermost real circle(max circle)), * beta(the angle alfa between the imag circle and the needed real circle), * rImag(radius of the imag circle), * rReal(radius of the real circle) * x, y, * * steps: * alfa = atan(imag) / atan(Infinity) * PI, * rImag = atan(Infinity) / 2 / atan(alfa / 2), * rReal = (atan(Infinity) - atan(real)) / 2, * beta = 2 * atan(rReal / rImag), * x = atan(Infinity) - rReal - rReal * cos(beta), * y = rReal * sin(beta), */ import * as React from 'react'; import ReactECharts from 'echarts-for-react'; export interface ComplexNumber { real: number, imag: number, } export interface SmithChartProps { r: number, smithChartData: ComplexNumber[], } /** this interface maybe need to add some properties */ export interface SimthChartStates { r: number, smithChartdata: ComplexNumber[], } const tan = Math.tan; const atan = Math.atan; const sin = Math.sin; const cos = Math.cos; const PI = Math.PI; export class SmithChartComponent extends React.Component<SmithChartProps, SimthChartStates>{ private readonly COMPONENT_CLASS: string = 'smith-chart'; constructor(props: SmithChartProps) { super(props); this.state = { r: 0, smithChartdata: [], }; } componentDidMount() { this.setState({ r: this.props.r, smithChartdata: this.props.smithChartData }); } protected drawTheGraphicsWithNumber(aAixsLabels: string[]) { const children = []; const splitNumber = aAixsLabels.length; /** draw */ children.push({ type: 'circle', id: 'bg_circle', silent: false, style: { fill: undefined, stroke: 'gray', }, shape: { r: this.state.r, } }); for (let i = 1; i <= splitNumber; i++) { /** draw the circles */ children.push({ type: 'circle', id: 'real_cir_' + String(i), silent: true, style: { fill: undefined, stroke: 'gray', lineStyle: { type: 'dotted', } }, shape: { cx: (this.state.r / (splitNumber + 1)) * i, r: this.state.r - (this.state.r / (splitNumber + 1) ) * i, clockwise: true, } }); /** draw the top arcs */ children.push({ type: 'arc', id: 'imag_arc_top_' + String(i), silent: true, style: { fill: undefined, stroke: 'gray', }, shape: { cx: this.state.r, cy: - this.state.r * tan(PI * i / (splitNumber + 1) / 2), r: this.state.r * tan(PI * i / (splitNumber + 1) / 2), startAngle: PI / 2, endAngle: (3 / 2 - i / (splitNumber + 1)) * PI, clockwise: true, } }); /** draw the bottom arcs */ children.push({ type: 'arc', id: 'imag_arc_bot_' + String(i), silent: true, style: { fill: undefined, stroke: 'gray', }, shape: { cy: this.state.r * tan(PI * i / (splitNumber + 1) / 2), cx: this.state.r, r: this.state.r * tan(PI * i / (splitNumber + 1) / 2), startAngle: (1 / 2 + i / (splitNumber + 1)) * PI, endAngle: PI * 3 / 2, clockwise: true, } }); /** * draw the top text * FIXME: fix the style of the imag text. */ children.push({ type: 'text', id: 'imag_text_top_' + String(i), silent: true, style: { text: aAixsLabels[splitNumber - i] + 'i', x: this.state.r * cos(PI * i / (splitNumber + 1)), y: -this.state.r * sin(PI * i / (splitNumber + 1)), fill: undefined, stroke: 'gray' } }); /** draw the bottom text */ children.push({ type: 'text', id: 'imag_text_bot_' + String(i), silent: true, style: { text: '-' + aAixsLabels[splitNumber - i] + 'i', x: this.state.r * cos(PI * i / (splitNumber + 1)), y: this.state.r * sin(PI * i / (splitNumber + 1)), fill: undefined, stroke: 'gray' } }); } return children; } protected generateSmithChartGraphic(aAixsLabels: string[]) { const children = this.drawTheGraphicsWithNumber(aAixsLabels); return { type: 'group', id: 'smith_group', /** FIXME: fix the hard code right and top */ right: 87, top: 57, draggable: true, children: children, }; } protected convertPosition(complexNumber: ComplexNumber) { const real = complexNumber.real; const imag = complexNumber.imag; if (imag === 0) { return [atan(real), 0]; } /** * Calculate the angle alfa between the imag circle and the outermost real circle * by calculating the proportion of the imag number on the outermost circle, and * then calculate the radius of the imag circle (to determine the center of the * imaginary circle). */ const alfa = atan(imag) / atan(Infinity) * PI; const rImag = atan(Infinity) / 2 / tan(alfa / 2); /** * By calculating the angle beta between the imaginary circle and the real circle, * we can finally calculate the values of x and y */ const rReal = (atan(Infinity) - atan(real)) / 2; const beta = 2 * atan(rReal / rImag); const x = atan(Infinity) - rReal - rReal * cos(beta); const y = rReal * sin(beta); return [x, y]; } protected calculateComplexNumber(x: number, y: number): [string, string] { if (y === 0) { return [tan(x).toFixed(2), '0']; } /** Reverse the calculation of x and y to restore the complex number */ const beta = 2 * atan(y / (atan(Infinity) - x)); const rReal = y / sin(beta); const real = tan(atan(Infinity) - 2 * rReal).toFixed(2); const rImag = rReal / tan(beta / 2); const alfa = 2 * atan(atan(Infinity) / 2 / rImag); const imag = tan(alfa / PI * atan(Infinity)).toFixed(2); return [real, imag]; } protected generateLabelArray(length: number): string[] { const resultArray: string[] = []; for (let i = 1; i <= length ; i++) { resultArray.push(tan(PI * i / 2 / (length + 1)).toFixed(2)); } return resultArray; } protected generateSimthChartOptions() { /** the `splitNumber` is related to datazoom */ const splitNumber = 7; const aAixsLabels = this.generateLabelArray(splitNumber); const xAxisLabels = [0, ...aAixsLabels]; return { title: { text: 'Smith Chart', left: 'center', }, legend: [{ // eslint-disable-next-line id-blacklist data: ['simth'] }], tooltip: { /** FIXME: fix the type of value */ formatter: (value: { value: number[]; }) => { const complexNumber = this.calculateComplexNumber(value.value[0], value.value[1]); return complexNumber[0] + ', ' + complexNumber[1] + 'i'; } }, grid: { width: 2 * this.state.r, height: 2 * this.state.r }, xAxis: { type: 'value', position: 'bottom', splitNumber: 5, max: atan(Infinity), min: 0, interval: atan(Infinity) / xAxisLabels.length, offset: - this.state.r, axisLabel: { formatter: (_: number, index: number) => xAxisLabels[index] } }, yAxis: { type: 'value', show: true, max: atan(Infinity) / 2, min: - atan(Infinity) / 2, }, graphic: this.generateSmithChartGraphic(aAixsLabels), series: [ { type: 'line', smooth: true, // eslint-disable-next-line id-blacklist data: this.state.smithChartdata.map((complexNumber: ComplexNumber) => this.convertPosition(complexNumber)) } ] }; } public render(): JSX.Element{ return( /** * FIXME: fix the size of chart to be flexible. */ <div className={this.COMPONENT_CLASS} style={{ width: 1100, height: 1100 }}> <ReactECharts option={this.generateSimthChartOptions()} style={{ height: '100%' }} /> </div> ); } } ``` Sorry, I didn't have enough time to support chart features like datazoom so that I didn't use it in our project 😢 We change to use Geo chart and draw some svg lines to implement the smith chart: -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: commits-unsubscr...@echarts.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@echarts.apache.org For additional commands, e-mail: commits-h...@echarts.apache.org