@operato/scene-scichart 2.0.0-beta.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +13 -0
  3. package/assets/favicon.ico +0 -0
  4. package/assets/images/spinner.png +0 -0
  5. package/db.sqlite +0 -0
  6. package/dist/charts/sci-candle-stick-chart.d.ts +19 -0
  7. package/dist/charts/sci-candle-stick-chart.js +248 -0
  8. package/dist/charts/sci-candle-stick-chart.js.map +1 -0
  9. package/dist/charts/volume-pallette-provider.d.ts +12 -0
  10. package/dist/charts/volume-pallette-provider.js +21 -0
  11. package/dist/charts/volume-pallette-provider.js.map +1 -0
  12. package/dist/data/binance-rest-client.d.ts +14 -0
  13. package/dist/data/binance-rest-client.js +53 -0
  14. package/dist/data/binance-rest-client.js.map +1 -0
  15. package/dist/editors/index.d.ts +0 -0
  16. package/dist/editors/index.js +2 -0
  17. package/dist/editors/index.js.map +1 -0
  18. package/dist/groups/index.d.ts +0 -0
  19. package/dist/groups/index.js +2 -0
  20. package/dist/groups/index.js.map +1 -0
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.js +3 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/scichart-candle-stick.d.ts +10 -0
  25. package/dist/scichart-candle-stick.js +50 -0
  26. package/dist/scichart-candle-stick.js.map +1 -0
  27. package/dist/scichart-timeseries.d.ts +23 -0
  28. package/dist/scichart-timeseries.js +118 -0
  29. package/dist/scichart-timeseries.js.map +1 -0
  30. package/dist/scichart.d.ts +20 -0
  31. package/dist/scichart.js +70 -0
  32. package/dist/scichart.js.map +1 -0
  33. package/dist/templates/index.d.ts +14 -0
  34. package/dist/templates/index.js +4 -0
  35. package/dist/templates/index.js.map +1 -0
  36. package/dist/templates/scichart-candle-stick.d.ts +14 -0
  37. package/dist/templates/scichart-candle-stick.js +16 -0
  38. package/dist/templates/scichart-candle-stick.js.map +1 -0
  39. package/dist/templates/scichart.d.ts +14 -0
  40. package/dist/templates/scichart.js +16 -0
  41. package/dist/templates/scichart.js.map +1 -0
  42. package/dist/themes/app-theme.d.ts +56 -0
  43. package/dist/themes/app-theme.js +35 -0
  44. package/dist/themes/app-theme.js.map +1 -0
  45. package/icons/no-image.png +0 -0
  46. package/icons/scichart-candle-stick.png +0 -0
  47. package/icons/scichart.png +0 -0
  48. package/logs/.08636eb59927f12972f6774f5947c8507b3564c2-audit.json +25 -0
  49. package/logs/.5e5d741d8b7784a2fbad65eedc0fd46946aaf6f2-audit.json +30 -0
  50. package/logs/application-2024-03-15-04.log +28 -0
  51. package/logs/application-2024-03-17-03.log +12 -0
  52. package/logs/application-2024-03-17-04.log +6 -0
  53. package/logs/connections-2024-03-15-03.log +82 -0
  54. package/logs/connections-2024-03-15-04.log +164 -0
  55. package/logs/connections-2024-03-17-03.log +88 -0
  56. package/logs/connections-2024-03-17-04.log +44 -0
  57. package/package.json +62 -0
  58. package/schema.graphql +3969 -0
  59. package/src/charts/sci-candle-stick-chart.ts +305 -0
  60. package/src/charts/volume-pallette-provider.ts +41 -0
  61. package/src/data/binance-rest-client.ts +74 -0
  62. package/src/editors/index.ts +0 -0
  63. package/src/groups/index.ts +0 -0
  64. package/src/index.ts +2 -0
  65. package/src/scichart-candle-stick.ts +60 -0
  66. package/src/scichart-timeseries.ts +156 -0
  67. package/src/scichart.ts +88 -0
  68. package/src/templates/index.ts +4 -0
  69. package/src/templates/scichart-candle-stick.ts +16 -0
  70. package/src/templates/scichart.ts +16 -0
  71. package/src/themes/app-theme.ts +72 -0
  72. package/things-scene.config.js +9 -0
  73. package/translations/en.json +3 -0
  74. package/translations/ja.json +3 -0
  75. package/translations/ko.json +3 -0
  76. package/translations/ms.json +3 -0
  77. package/translations/zh.json +3 -0
  78. package/tsconfig.json +22 -0
  79. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,305 @@
1
+ import { LitElement, PropertyValues, PropertyValueMap, css, html } from 'lit'
2
+ import { customElement, property, query, state } from 'lit/decorators.js'
3
+
4
+ import {
5
+ SciChartSurface,
6
+ NumberRange,
7
+ NumericAxis,
8
+ OhlcDataSeries,
9
+ FastCandlestickRenderableSeries,
10
+ ZoomPanModifier,
11
+ ZoomExtentsModifier,
12
+ MouseWheelZoomModifier,
13
+ ENumericFormat,
14
+ DateTimeNumericAxis,
15
+ EAutoRange,
16
+ FastLineRenderableSeries,
17
+ XyMovingAverageFilter,
18
+ SciChartOverview,
19
+ CursorModifier,
20
+ CursorTooltipSvgAnnotation,
21
+ SeriesInfo,
22
+ EDataSeriesType,
23
+ ESeriesType,
24
+ IRenderableSeries,
25
+ FastMountainRenderableSeries,
26
+ GradientParams,
27
+ Point,
28
+ OhlcSeriesInfo,
29
+ FastColumnRenderableSeries,
30
+ XyDataSeries,
31
+ FastOhlcRenderableSeries
32
+ } from 'scichart'
33
+
34
+ import { appTheme } from '../themes/app-theme'
35
+ import { VolumePaletteProvider } from './volume-pallette-provider'
36
+ import { simpleBinanceRestClient } from '../data/binance-rest-client'
37
+
38
+ const Y_AXIS_VOLUME_ID = 'Y_AXIS_VOLUME_ID'
39
+
40
+ SciChartSurface.configure({
41
+ dataUrl: `/node_modules/scichart/_wasm/scichart2d.data`,
42
+ wasmUrl: `/node_modules/scichart/_wasm/scichart2d.wasm`
43
+ })
44
+
45
+ @customElement('sci-candle-stock-chart')
46
+ export class SciCandleStockChart extends LitElement {
47
+ static styles = [
48
+ css`
49
+ :host {
50
+ display: flex;
51
+ flex-direction: column;
52
+
53
+ width: 100%;
54
+ }
55
+
56
+ #chart {
57
+ flex: 8;
58
+ }
59
+
60
+ #overview {
61
+ flex: 2;
62
+ }
63
+ `
64
+ ]
65
+
66
+ render() {
67
+ return html`
68
+ <div id="chart"></div>
69
+ <div id="overview"></div>
70
+ `
71
+ }
72
+
73
+ @property({ type: String, attribute: 'series-type' }) seriesType: 'candle-stick' | 'ohlc' = 'candle-stick'
74
+
75
+ @state() candlestickChartSeries!: FastCandlestickRenderableSeries
76
+ @state() ohlcChartSeries!: FastOhlcRenderableSeries
77
+ @state() sciChartSurface?: SciChartSurface
78
+ @state() sciChartOverview?: SciChartOverview
79
+
80
+ @query('#chart') chart!: HTMLDivElement
81
+ @query('#overview') overview!: HTMLDivElement
82
+
83
+ disconnectedCallback(): void {
84
+ if (this.sciChartSurface) {
85
+ this.sciChartSurface.delete()
86
+ this.sciChartOverview!.delete()
87
+ this.sciChartSurface = undefined
88
+ this.sciChartOverview = undefined
89
+ return
90
+ }
91
+ }
92
+
93
+ protected async firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): Promise<void> {
94
+ const { sciChartSurface, overview, candlestickSeries, ohlcSeries } = await this.build()
95
+
96
+ this.candlestickChartSeries = candlestickSeries
97
+ this.ohlcChartSeries = ohlcSeries
98
+ this.sciChartSurface = sciChartSurface
99
+ this.sciChartOverview = overview
100
+
101
+ this.candlestickChartSeries.isVisible = this.seriesType == 'candle-stick'
102
+ this.ohlcChartSeries.isVisible = this.seriesType == 'ohlc'
103
+ }
104
+
105
+ updated(changes: PropertyValues<this>) {
106
+ if (changes.has('seriesType') && this.candlestickChartSeries) {
107
+ this.candlestickChartSeries.isVisible = this.seriesType == 'candle-stick'
108
+ this.ohlcChartSeries.isVisible = this.seriesType == 'ohlc'
109
+ }
110
+ }
111
+
112
+ // Override the Renderableseries to display on the scichart overview
113
+ private getOverviewSeries(defaultSeries: IRenderableSeries) {
114
+ if (defaultSeries.type === ESeriesType.CandlestickSeries) {
115
+ // Swap the default candlestick series on the overview chart for a mountain series. Same data
116
+ return new FastMountainRenderableSeries(defaultSeries.parentSurface.webAssemblyContext2D, {
117
+ dataSeries: defaultSeries.dataSeries,
118
+ fillLinearGradient: new GradientParams(new Point(0, 0), new Point(0, 1), [
119
+ { color: appTheme.VividSkyBlue + '77', offset: 0 },
120
+ { color: 'Transparent', offset: 1 }
121
+ ]),
122
+ stroke: appTheme.VividSkyBlue
123
+ })
124
+ }
125
+ }
126
+
127
+ // Override the standard tooltip displayed by CursorModifier
128
+ private getTooltipLegendTemplate(seriesInfos: SeriesInfo[], svgAnnotation: CursorTooltipSvgAnnotation) {
129
+ let outputSvgString = ''
130
+
131
+ // Foreach series there will be a seriesInfo supplied by SciChart. This contains info about the series under the house
132
+ seriesInfos.forEach((seriesInfo, index) => {
133
+ const y = 20 + index * 20
134
+ const textColor = seriesInfo.stroke
135
+ let legendText = seriesInfo.formattedYValue
136
+ if (seriesInfo.dataSeriesType === EDataSeriesType.Ohlc) {
137
+ const o = seriesInfo as OhlcSeriesInfo
138
+ legendText = `Open=${o.formattedOpenValue} High=${o.formattedHighValue} Low=${o.formattedLowValue} Close=${o.formattedCloseValue}`
139
+ }
140
+ outputSvgString += `<text x="8" y="${y}" font-size="13" font-family="Verdana" fill="${textColor}">
141
+ ${seriesInfo.seriesName}: ${legendText}
142
+ </text>`
143
+ })
144
+
145
+ return `<svg width="100%" height="100%">
146
+ ${outputSvgString}
147
+ </svg>`
148
+ }
149
+
150
+ private async build() {
151
+ // Create a SciChartSurface
152
+ const { sciChartSurface, wasmContext } = await SciChartSurface.create(this.chart, {
153
+ theme: appTheme.SciChartJsTheme
154
+ })
155
+
156
+ // Add an XAxis of type DateTimeAxis
157
+ // Note for crypto data this is fine, but for stocks/forex you will need to use CategoryAxis which collapses gaps at weekends
158
+ // In future we have a hybrid IndexDateAxis which 'magically' solves problems of different # of points in stock market datasetd with gaps
159
+ const xAxis = new DateTimeNumericAxis(wasmContext, {
160
+ // autoRange.never as we're setting visibleRange explicitly below. If you dont do this, leave this flag default
161
+ autoRange: EAutoRange.Never
162
+ })
163
+ sciChartSurface.xAxes.add(xAxis)
164
+
165
+ // Create a NumericAxis on the YAxis with 2 Decimal Places
166
+ sciChartSurface.yAxes.add(
167
+ new NumericAxis(wasmContext, {
168
+ growBy: new NumberRange(0.1, 0.1),
169
+ labelFormat: ENumericFormat.Decimal,
170
+ labelPrecision: 2,
171
+ labelPrefix: '$',
172
+ autoRange: EAutoRange.Always
173
+ })
174
+ )
175
+
176
+ // Create a secondary YAxis to host volume data on its own scale
177
+ sciChartSurface.yAxes.add(
178
+ new NumericAxis(wasmContext, {
179
+ id: Y_AXIS_VOLUME_ID,
180
+ growBy: new NumberRange(0, 4),
181
+ isVisible: false,
182
+ autoRange: EAutoRange.Always
183
+ })
184
+ )
185
+
186
+ // Fetch data from now to 300 1hr candles ago
187
+ const endDate = new Date(Date.now())
188
+ const startDate = new Date()
189
+ startDate.setHours(endDate.getHours() - 300)
190
+ const priceBars = await simpleBinanceRestClient.getCandles('BTCUSDT', '1h', startDate, endDate)
191
+
192
+ // Maps PriceBar { date, open, high, low, close, volume } to structure-of-arrays expected by scichart
193
+ const xValues: number[] = []
194
+ const openValues: number[] = []
195
+ const highValues: number[] = []
196
+ const lowValues: number[] = []
197
+ const closeValues: number[] = []
198
+ const volumeValues: number[] = []
199
+ priceBars.forEach((priceBar: any) => {
200
+ xValues.push(priceBar.date)
201
+ openValues.push(priceBar.open)
202
+ highValues.push(priceBar.high)
203
+ lowValues.push(priceBar.low)
204
+ closeValues.push(priceBar.close)
205
+ volumeValues.push(priceBar.volume)
206
+ })
207
+
208
+ // Zoom to the latest 100 candles
209
+ const startViewportRange = new Date()
210
+ startViewportRange.setHours(startDate.getHours() - 100)
211
+ xAxis.visibleRange = new NumberRange(startViewportRange.getTime() / 1000, endDate.getTime() / 1000)
212
+
213
+ // Create and add the Candlestick series
214
+ // The Candlestick Series requires a special dataseries type called OhlcDataSeries with o,h,l,c and date values
215
+ const candleDataSeries = new OhlcDataSeries(wasmContext, {
216
+ xValues,
217
+ openValues,
218
+ highValues,
219
+ lowValues,
220
+ closeValues,
221
+ dataSeriesName: 'BTC/USDT'
222
+ })
223
+ const candlestickSeries = new FastCandlestickRenderableSeries(wasmContext, {
224
+ dataSeries: candleDataSeries,
225
+ stroke: appTheme.ForegroundColor, // used by cursorModifier below
226
+ strokeThickness: 1,
227
+ brushUp: appTheme.VividGreen + '77',
228
+ brushDown: appTheme.MutedRed + '77',
229
+ strokeUp: appTheme.VividGreen,
230
+ strokeDown: appTheme.MutedRed
231
+ })
232
+ sciChartSurface.renderableSeries.add(candlestickSeries)
233
+
234
+ // Add an Ohlcseries. this will be invisible to begin with
235
+ const ohlcSeries = new FastOhlcRenderableSeries(wasmContext, {
236
+ dataSeries: candleDataSeries,
237
+ stroke: appTheme.ForegroundColor, // used by cursorModifier below
238
+ strokeThickness: 1,
239
+ dataPointWidth: 0.9,
240
+ strokeUp: appTheme.VividGreen,
241
+ strokeDown: appTheme.MutedRed,
242
+ isVisible: false
243
+ })
244
+ sciChartSurface.renderableSeries.add(ohlcSeries)
245
+
246
+ // Add some moving averages using SciChart's filters/transforms API
247
+ // when candleDataSeries updates, XyMovingAverageFilter automatically recomputes
248
+ sciChartSurface.renderableSeries.add(
249
+ new FastLineRenderableSeries(wasmContext, {
250
+ dataSeries: new XyMovingAverageFilter(candleDataSeries, {
251
+ dataSeriesName: 'Moving Average (20)',
252
+ length: 20
253
+ }),
254
+ stroke: appTheme.VividSkyBlue
255
+ })
256
+ )
257
+
258
+ sciChartSurface.renderableSeries.add(
259
+ new FastLineRenderableSeries(wasmContext, {
260
+ dataSeries: new XyMovingAverageFilter(candleDataSeries, {
261
+ dataSeriesName: 'Moving Average (50)',
262
+ length: 50
263
+ }),
264
+ stroke: appTheme.VividPink
265
+ })
266
+ )
267
+
268
+ // Add volume data onto the chart
269
+ sciChartSurface.renderableSeries.add(
270
+ new FastColumnRenderableSeries(wasmContext, {
271
+ dataSeries: new XyDataSeries(wasmContext, { xValues, yValues: volumeValues, dataSeriesName: 'Volume' }),
272
+ strokeThickness: 0,
273
+ // This is how we get volume to scale - on a hidden YAxis
274
+ yAxisId: Y_AXIS_VOLUME_ID,
275
+ // This is how we colour volume bars red or green
276
+ paletteProvider: new VolumePaletteProvider(
277
+ candleDataSeries,
278
+ appTheme.VividGreen + '77',
279
+ appTheme.MutedRed + '77'
280
+ )
281
+ })
282
+ )
283
+
284
+ // Optional: Add some interactivity modifiers
285
+ sciChartSurface.chartModifiers.add(
286
+ new ZoomExtentsModifier(),
287
+ new ZoomPanModifier(),
288
+ new MouseWheelZoomModifier(),
289
+ new CursorModifier({
290
+ crosshairStroke: appTheme.VividOrange,
291
+ axisLabelFill: appTheme.VividOrange,
292
+ tooltipLegendTemplate: this.getTooltipLegendTemplate
293
+ })
294
+ )
295
+
296
+ // Add Overview chart. This will automatically bind to the parent surface
297
+ // displaying its series. Zooming the chart will zoom the overview and vice versa
298
+ const overview = await SciChartOverview.create(sciChartSurface, this.overview, {
299
+ theme: appTheme.SciChartJsTheme,
300
+ transformRenderableSeries: this.getOverviewSeries as any
301
+ })
302
+
303
+ return { sciChartSurface, overview, candlestickSeries, ohlcSeries }
304
+ }
305
+ }
@@ -0,0 +1,41 @@
1
+ import {
2
+ OhlcDataSeries,
3
+ IRenderableSeries,
4
+ EFillPaletteMode,
5
+ IFillPaletteProvider,
6
+ IPointMetadata,
7
+ parseColorToUIntArgb
8
+ } from 'scichart'
9
+
10
+ export class VolumePaletteProvider implements IFillPaletteProvider {
11
+ fillPaletteMode: EFillPaletteMode = EFillPaletteMode.SOLID
12
+ private ohlcDataSeries: OhlcDataSeries
13
+ private upColorArgb: number
14
+ private downColorArgb: number
15
+
16
+ constructor(masterData: OhlcDataSeries, upColor: string, downColor: string) {
17
+ this.upColorArgb = parseColorToUIntArgb(upColor)
18
+ this.downColorArgb = parseColorToUIntArgb(downColor)
19
+ this.ohlcDataSeries = masterData
20
+ }
21
+ onAttached(parentSeries: IRenderableSeries): void {}
22
+ onDetached(): void {}
23
+
24
+ // Return up or down color for the volume bars depending on Ohlc data
25
+ overrideFillArgb(xValue: number, yValue: number, index: number, opacity?: number, metadata?: IPointMetadata): number {
26
+ const isUpCandle =
27
+ this.ohlcDataSeries.getNativeOpenValues().get(index) >= this.ohlcDataSeries.getNativeCloseValues().get(index)
28
+ return isUpCandle ? this.upColorArgb : this.downColorArgb
29
+ }
30
+
31
+ // Override stroke as well, even though strokethickness is 0, because stroke is used if column thickness goes to 0.
32
+ overrideStrokeArgb(
33
+ xValue: number,
34
+ yValue: number,
35
+ index: number,
36
+ opacity?: number,
37
+ metadata?: IPointMetadata
38
+ ): number {
39
+ return this.overrideFillArgb(xValue, yValue, index, opacity, metadata)
40
+ }
41
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Defines a price bar with Open, High, Low, Close and Date encoded as number
3
+ */
4
+ export type TPriceBar = {
5
+ date: number
6
+ open: number
7
+ high: number
8
+ low: number
9
+ close: number
10
+ volume: number
11
+ }
12
+
13
+ /**
14
+ * Parses JSON candles into TPriceBar array
15
+ * @param candles
16
+ */
17
+ const parseCandles = (candles: any[]): TPriceBar[] => {
18
+ const priceBars: TPriceBar[] = []
19
+
20
+ candles.forEach((candle: any) => {
21
+ const [timestamp, open, high, low, close, volume] = candle
22
+ const openValue = parseFloat(open)
23
+ const highValue = parseFloat(high)
24
+ const lowValue = parseFloat(low)
25
+ const closeValue = parseFloat(close)
26
+ const volumeValue = parseFloat(volume)
27
+
28
+ priceBars.push({
29
+ date: timestamp / 1000,
30
+ open: openValue,
31
+ high: highValue,
32
+ low: lowValue,
33
+ close: closeValue,
34
+ volume: volumeValue
35
+ })
36
+ })
37
+
38
+ return priceBars
39
+ }
40
+
41
+ /**
42
+ * Fetches candles from Binance Rest API
43
+ */
44
+ const getCandles = async (
45
+ symbol: string,
46
+ interval: string,
47
+ startTime?: Date,
48
+ endTime?: Date,
49
+ limit: number = 500
50
+ ): Promise<TPriceBar[]> => {
51
+ let url = `https://api.binance.us/api/v3/klines?symbol=${symbol}&interval=${interval}`
52
+ if (startTime) {
53
+ url += `&startTime=${startTime.getTime()}`
54
+ }
55
+ if (endTime) {
56
+ url += `&endTime=${endTime.getTime()}`
57
+ }
58
+ if (limit) {
59
+ url += `&limit=${limit}`
60
+ }
61
+ try {
62
+ console.log(`SimpleBinanceClient: Fetching ${symbol} ${interval} from ${startTime} to ${endTime}`)
63
+ const response = await fetch(url)
64
+ const data = await response.json()
65
+ return parseCandles(data)
66
+ } catch (err) {
67
+ console.error(err)
68
+ return []
69
+ }
70
+ }
71
+
72
+ export const simpleBinanceRestClient = {
73
+ getCandles
74
+ }
File without changes
File without changes
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { default as Scichart } from './scichart'
2
+ export { default as ScichartCandleStick } from './scichart-candle-stick'
@@ -0,0 +1,60 @@
1
+ /*
2
+ * Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+
5
+ const NATURE = {
6
+ mutable: false,
7
+ resizable: true,
8
+ rotatable: true,
9
+ properties: [
10
+ {
11
+ type: 'select',
12
+ label: 'series',
13
+ name: 'series',
14
+ property: {
15
+ options: ['', 'candle-stick', 'ohlc']
16
+ }
17
+ }
18
+ ]
19
+ }
20
+
21
+ import './charts/sci-candle-stick-chart'
22
+ import { Component, HTMLOverlayContainer, Properties, ComponentNature, error } from '@hatiolab/things-scene'
23
+
24
+ import { SciCandleStockChart } from './charts/sci-candle-stick-chart'
25
+
26
+ export default class ScichartCandleStick extends HTMLOverlayContainer {
27
+ static get nature(): ComponentNature {
28
+ return NATURE
29
+ }
30
+
31
+ dispose() {
32
+ super.dispose()
33
+ }
34
+
35
+ /*
36
+ * 컴포넌트의 생성 또는 속성 변화 시에 호출되며,
37
+ * 그에 따른 html element의 반영이 필요한 부분을 구현한다.
38
+ *
39
+ * ThingsComponent state => HTML element properties
40
+ */
41
+ setElementProperties(chart: SciCandleStockChart) {
42
+ const { series = 'candle-stick' } = this.state
43
+
44
+ chart.seriesType = series
45
+ }
46
+
47
+ /*
48
+ * 컴포넌트가 ready 상태가 되거나, 컴포넌트의 속성이 변화될 시 setElementProperties 뒤에 호출된다.
49
+ * 변화에 따른 기본적인 html 속성이 super.reposition()에서 진행되고, 그 밖의 작업이 필요할 때, 오버라이드 한다.
50
+ */
51
+ reposition() {
52
+ super.reposition()
53
+ }
54
+
55
+ get tagName() {
56
+ return 'sci-candle-stock-chart'
57
+ }
58
+ }
59
+
60
+ Component.register('scichart-candle-stick', ScichartCandleStick)
@@ -0,0 +1,156 @@
1
+ /*
2
+ * Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+
5
+ const NATURE = {
6
+ mutable: false,
7
+ resizable: true,
8
+ rotatable: true,
9
+ properties: [
10
+ {
11
+ type: 'number',
12
+ label: 'sample-count',
13
+ name: 'sampleCount'
14
+ },
15
+ {
16
+ type: 'number',
17
+ label: 'max-value',
18
+ name: 'maxValue'
19
+ },
20
+ {
21
+ type: 'string',
22
+ label: 'attr-x',
23
+ name: 'attrX'
24
+ },
25
+ {
26
+ type: 'string',
27
+ label: 'attr-y',
28
+ name: 'attrY'
29
+ }
30
+ ]
31
+ }
32
+
33
+ import { Component, HTMLOverlayContainer, Properties, ComponentNature, error } from '@hatiolab/things-scene'
34
+
35
+ import {
36
+ MouseWheelZoomModifier,
37
+ FastLineRenderableSeries,
38
+ XyDataSeries,
39
+ ZoomExtentsModifier,
40
+ ZoomPanModifier,
41
+ NumberRange,
42
+ EAxisAlignment,
43
+ EAutoRange
44
+ } from 'scichart'
45
+
46
+ import { SciChartSurface } from 'scichart/Charting/Visuals/SciChartSurface'
47
+ import { NumericAxis } from 'scichart/Charting/Visuals/Axis/NumericAxis'
48
+
49
+ SciChartSurface.configure({
50
+ dataUrl: `/node_modules/scichart/_wasm/scichart2d.data`,
51
+ wasmUrl: `/node_modules/scichart/_wasm/scichart2d.wasm`
52
+ })
53
+
54
+ export default class ScichartTimeSeries extends HTMLOverlayContainer {
55
+ static get nature() {
56
+ return NATURE
57
+ }
58
+
59
+ _anchor?: HTMLDivElement
60
+
61
+ async oncreate_element(div: HTMLDivElement) {
62
+ const { sciChartSurface, wasmContext } = await SciChartSurface.create(div)
63
+ var { sampleCount, maxValue } = this.state
64
+
65
+ // SciChartSurface.setRuntimeLicenseKey('YOUR_RUNTIME_KEY')
66
+
67
+ const xAxis = new NumericAxis(wasmContext, {
68
+ axisTitle: 'X Axis',
69
+ autoRange: EAutoRange.Never,
70
+ axisAlignment: EAxisAlignment.Bottom,
71
+ visibleRange: new NumberRange(0, sampleCount)
72
+ })
73
+ const yAxis = new NumericAxis(wasmContext, {
74
+ axisTitle: 'Y Axis',
75
+ autoRange: EAutoRange.Never,
76
+ visibleRange: new NumberRange(0, maxValue)
77
+ })
78
+
79
+ sciChartSurface.xAxes.add(xAxis)
80
+ sciChartSurface.yAxes.add(yAxis)
81
+
82
+ const dataSeries = new XyDataSeries(wasmContext, {
83
+ xValues: this.dataSet.map(d => d.xValue),
84
+ yValues: this.dataSet.map(d => d.yValue)
85
+ })
86
+
87
+ const lineSeries = new FastLineRenderableSeries(wasmContext, {
88
+ dataSeries: dataSeries,
89
+ xAxisId: sciChartSurface.xAxes.get(0).id,
90
+ yAxisId: sciChartSurface.yAxes.get(0).id,
91
+ stroke: '#FF6600',
92
+ strokeThickness: 2
93
+ })
94
+
95
+ sciChartSurface.renderableSeries.add(lineSeries)
96
+
97
+ // ZoomPanModifier, MouseWheelZoomModifier, ZoomExtentsModifier 추가
98
+ // ZoomPanModifier: Chart를 드래그하여 이동
99
+ // MouseWheelZoomModifier: 마우스 휠로 Chart Zoom
100
+ // ZoomExtentsModifier: Chart의 전체 범위로 Zoom
101
+ sciChartSurface.chartModifiers.add(
102
+ new ZoomPanModifier({ enableZoom: true }),
103
+ new MouseWheelZoomModifier(),
104
+ new ZoomExtentsModifier()
105
+ )
106
+ }
107
+
108
+ dispose() {
109
+ super.dispose()
110
+
111
+ delete this._anchor
112
+ }
113
+
114
+ /*
115
+ * 컴포넌트의 생성 또는 속성 변화 시에 호출되며,
116
+ * 그에 따른 html element의 반영이 필요한 부분을 구현한다.
117
+ *
118
+ * ThingsComponent state => HTML element properties
119
+ */
120
+ setElementProperties(div: HTMLDivElement) {}
121
+
122
+ /*
123
+ * 컴포넌트가 ready 상태가 되거나, 컴포넌트의 속성이 변화될 시 setElementProperties 뒤에 호출된다.
124
+ * 변화에 따른 기본적인 html 속성이 super.reposition()에서 진행되고, 그 밖의 작업이 필요할 때, 오버라이드 한다.
125
+ */
126
+ reposition() {
127
+ super.reposition()
128
+ }
129
+
130
+ get dataSet(): { xValue: number; yValue: number }[] {
131
+ var { attrX, attrY, data } = this.state
132
+
133
+ if (!(data instanceof Array)) {
134
+ return []
135
+ }
136
+
137
+ return data
138
+ .map(item => {
139
+ if (!item || typeof item !== 'object') {
140
+ return
141
+ }
142
+
143
+ return {
144
+ xValue: item[attrX],
145
+ yValue: item[attrY]
146
+ }
147
+ })
148
+ .filter(Boolean) as { xValue: number; yValue: number }[]
149
+ }
150
+
151
+ get tagName() {
152
+ return 'div'
153
+ }
154
+ }
155
+
156
+ Component.register('scichart-timeseries', ScichartTimeSeries)