@orbcharts/core 3.0.0-beta.3 → 3.0.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,662 @@
1
+ import {
2
+ combineLatest,
3
+ distinctUntilChanged,
4
+ iif,
5
+ filter,
6
+ map,
7
+ merge,
8
+ takeUntil,
9
+ shareReplay,
10
+ switchMap,
11
+ Subject,
12
+ Observable } from 'rxjs'
13
+ import type {
14
+ AxisPosition,
15
+ ChartType,
16
+ ChartParams,
17
+ ComputedDataTypeMap,
18
+ ComputedDatumTypeMap,
19
+ ComputedDataMultiValue,
20
+ ComputedDatumMultiValue,
21
+ DataFormatterTypeMap,
22
+ DataFormatterMultiValue,
23
+ DataFormatterAxis,
24
+ ComputedLayoutDatumMultiValue,
25
+ ComputedLayoutDataMultiValue,
26
+ ContainerPositionScaled,
27
+ HighlightTarget,
28
+ Layout,
29
+ TransformData } from '../../lib/core-types'
30
+ import { getMinAndMax, getMinAndMaxMultiValue } from './orbchartsUtils'
31
+ import { createValueToAxisScale, createLabelToAxisScale, createAxisToLabelIndexScale } from './d3Scale'
32
+ import { calcGridContainerLayout } from './orbchartsUtils'
33
+
34
+ export const minMaxXYObservable = ({ computedData$ }: { computedData$: Observable<ComputedDataTypeMap<'multiValue'>> }) => {
35
+ return computedData$.pipe(
36
+ map(data => {
37
+ const flatData = data.flat()
38
+ const [minX, maxX] = getMinAndMax(flatData.map(d => d.value[0]))
39
+ const [minY, maxY] = getMinAndMax(flatData.map(d => d.value[1]))
40
+ return { minX, maxX, minY, maxY }
41
+ })
42
+ )
43
+ }
44
+
45
+ export const multiValueComputedLayoutDataObservable = ({ computedData$, minMaxXY$, fullDataFormatter$, layout$ }: {
46
+ computedData$: Observable<ComputedDataTypeMap<'multiValue'>>
47
+ minMaxXY$: Observable<{ minX: number, maxX: number, minY: number, maxY: number }>
48
+ fullDataFormatter$: Observable<DataFormatterTypeMap<'multiValue'>>
49
+ layout$: Observable<Layout>
50
+ }): Observable<ComputedLayoutDataMultiValue> => {
51
+
52
+ // 未篩選範圍前的 scale
53
+ function createOriginXScale (minMaxXY: { minX: number, maxX: number, minY: number, maxY: number }, layout: Layout) {
54
+ let maxValue = minMaxXY.maxX
55
+ let minValue = minMaxXY.minX
56
+ if (minValue === maxValue && maxValue === 0) {
57
+ // 避免最大及最小值相同造成無法計算scale
58
+ maxValue = 1
59
+ }
60
+ const valueScale: d3.ScaleLinear<number, number> = createValueToAxisScale({
61
+ maxValue,
62
+ minValue,
63
+ axisWidth: layout.width,
64
+ scaleDomain: ['auto', 'auto'], // 不使用dataFormatter設定 --> 以0為基準到最大或最小值為範圍( * 如果是使用[minValue, maxValue]的話,在兩者很接近的情況下有可能造成scale倍率過高而svg變型時失真的情況)
65
+ scaleRange: [0, 1] // 不使用dataFormatter設定
66
+ })
67
+
68
+ return valueScale
69
+ }
70
+
71
+ // 未篩選範圍及visible前的 scale
72
+ function createOriginYScale (minMaxXY: { minX: number, maxX: number, minY: number, maxY: number }, layout: Layout) {
73
+ let maxValue = minMaxXY.maxY
74
+ let minValue = minMaxXY.minY
75
+ if (minValue === maxValue && maxValue === 0) {
76
+ // 避免最大及最小值相同造成無法計算scale
77
+ maxValue = 1
78
+ }
79
+ const valueScale: d3.ScaleLinear<number, number> = createValueToAxisScale({
80
+ maxValue,
81
+ minValue,
82
+ axisWidth: layout.height,
83
+ scaleDomain: ['auto', 'auto'], // 不使用dataFormatter設定 --> 以0為基準到最大或最小值為範圍( * 如果是使用[minValue, maxValue]的話,在兩者很接近的情況下有可能造成scale倍率過高而svg變型時失真的情況)
84
+ scaleRange: [0, 1], // 不使用dataFormatter設定
85
+ reverse: true
86
+ })
87
+
88
+ return valueScale
89
+ }
90
+
91
+ return combineLatest({
92
+ computedData: computedData$,
93
+ minMaxXY: minMaxXY$,
94
+ fullDataFormatter: fullDataFormatter$,
95
+ layout: layout$
96
+ }).pipe(
97
+ switchMap(async d => d),
98
+ map(data => {
99
+
100
+ const xScale = createOriginXScale(data.minMaxXY, data.layout)
101
+ const yScale = createOriginYScale(data.minMaxXY, data.layout)
102
+
103
+ return data.computedData
104
+ .map((categoryData, categoryIndex) => {
105
+ return categoryData.map((datum, datumIndex) => {
106
+ return {
107
+ ...datum,
108
+ axisX: xScale(datum.value[0] ?? 0),
109
+ // axisY: data.layout.height - yScale(datum.value[1] ?? 0), // y軸的繪圖座標是從上到下,所以反轉
110
+ axisY: yScale(datum.value[1] ?? 0), // y軸的繪圖座標是從上到下,所以反轉
111
+ }
112
+ })
113
+ })
114
+ })
115
+ )
116
+ }
117
+
118
+ // export const multiValueAxesTransformObservable = ({ fullDataFormatter$, layout$ }: {
119
+ // fullDataFormatter$: Observable<DataFormatterTypeMap<'multiValue'>>
120
+ // layout$: Observable<Layout>
121
+ // }): Observable<TransformData> => {
122
+ // const destroy$ = new Subject()
123
+
124
+ // function calcAxesTransform ({ xAxis, yAxis, width, height }: {
125
+ // xAxis: DataFormatterAxis,
126
+ // yAxis: DataFormatterAxis,
127
+ // width: number,
128
+ // height: number
129
+ // }): TransformData {
130
+ // if (!xAxis || !yAxis) {
131
+ // return {
132
+ // translate: [0, 0],
133
+ // scale: [1, 1],
134
+ // rotate: 0,
135
+ // rotateX: 0,
136
+ // rotateY: 0,
137
+ // value: ''
138
+ // }
139
+ // }
140
+ // // const width = size.width - fullChartParams.layout.left - fullChartParams.layout.right
141
+ // // const height = size.height - fullChartParams.layout.top - fullChartParams.layout.bottom
142
+ // let translateX = 0
143
+ // let translateY = height
144
+ // let rotate = 0
145
+ // let rotateX = 180
146
+ // let rotateY = 0
147
+
148
+ // return {
149
+ // translate: [translateX, translateY],
150
+ // scale: [1, 1],
151
+ // rotate,
152
+ // rotateX,
153
+ // rotateY,
154
+ // value: `translate(${translateX}px, ${translateY}px) rotate(${rotate}deg) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`
155
+ // }
156
+ // }
157
+
158
+ // return new Observable(subscriber => {
159
+ // combineLatest({
160
+ // fullDataFormatter: fullDataFormatter$,
161
+ // layout: layout$
162
+ // }).pipe(
163
+ // takeUntil(destroy$),
164
+ // switchMap(async (d) => d),
165
+ // ).subscribe(data => {
166
+ // const axesTransformData = calcAxesTransform({
167
+ // xAxis: data.fullDataFormatter.xAxis,
168
+ // yAxis: data.fullDataFormatter.yAxis,
169
+ // width: data.layout.width,
170
+ // height: data.layout.height
171
+ // })
172
+
173
+ // subscriber.next(axesTransformData)
174
+ // })
175
+
176
+ // return function unscbscribe () {
177
+ // destroy$.next(undefined)
178
+ // }
179
+ // })
180
+ // }
181
+
182
+
183
+ // export const multiValueAxesReverseTransformObservable = ({ multiValueAxesTransform$ }: {
184
+ // multiValueAxesTransform$: Observable<TransformData>
185
+ // }): Observable<TransformData> => {
186
+ // return multiValueAxesTransform$.pipe(
187
+ // map(d => {
188
+ // // const translate: [number, number] = [d.translate[0] * -1, d.translate[1] * -1]
189
+ // const translate: [number, number] = [0, 0] // 無需逆轉
190
+ // const scale: [number, number] = [1 / d.scale[0], 1 / d.scale[1]]
191
+ // const rotate = d.rotate * -1
192
+ // const rotateX = d.rotateX * -1
193
+ // const rotateY = d.rotateY * -1
194
+ // return {
195
+ // translate,
196
+ // scale,
197
+ // rotate,
198
+ // rotateX,
199
+ // rotateY,
200
+ // value: `translate(${translate[0]}px, ${translate[1]}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) rotate(${rotate}deg)`
201
+ // }
202
+ // }),
203
+ // )
204
+ // }
205
+
206
+
207
+
208
+ // export const multiValueAxesSizeObservable = ({ fullDataFormatter$, layout$ }: {
209
+ // fullDataFormatter$: Observable<DataFormatterMultiValue>
210
+ // layout$: Observable<Layout>
211
+ // }): Observable<{
212
+ // width: number;
213
+ // height: number;
214
+ // }> => {
215
+ // const destroy$ = new Subject()
216
+
217
+ // function calcAxesSize ({ xAxisPosition, yAxisPosition, width, height }: {
218
+ // xAxisPosition: AxisPosition
219
+ // yAxisPosition: AxisPosition
220
+ // width: number
221
+ // height: number
222
+ // }) {
223
+ // if ((xAxisPosition === 'bottom' || xAxisPosition === 'top') && (yAxisPosition === 'left' || yAxisPosition === 'right')) {
224
+ // return { width, height }
225
+ // } else if ((xAxisPosition === 'left' || xAxisPosition === 'right') && (yAxisPosition === 'bottom' || yAxisPosition === 'top')) {
226
+ // return {
227
+ // width: height,
228
+ // height: width
229
+ // }
230
+ // } else {
231
+ // // default
232
+ // return { width, height }
233
+ // }
234
+ // }
235
+
236
+ // return new Observable(subscriber => {
237
+ // combineLatest({
238
+ // fullDataFormatter: fullDataFormatter$,
239
+ // layout: layout$
240
+ // }).pipe(
241
+ // takeUntil(destroy$),
242
+ // switchMap(async (d) => d),
243
+ // ).subscribe(data => {
244
+
245
+ // const axisSize = calcAxesSize({
246
+ // xAxisPosition: 'bottom',
247
+ // yAxisPosition: 'left',
248
+ // width: data.layout.width,
249
+ // height: data.layout.height,
250
+ // })
251
+
252
+ // subscriber.next(axisSize)
253
+
254
+ // return function unsubscribe () {
255
+ // destroy$.next(undefined)
256
+ // }
257
+ // })
258
+ // })
259
+ // }
260
+
261
+ // export const multiValueHighlightObservable = ({ computedData$, fullChartParams$, event$ }: {
262
+ // computedData$: Observable<ComputedDataTypeMap<'multiValue'>>
263
+ // fullChartParams$: Observable<ChartParams>
264
+ // event$: Subject<any>
265
+ // }): Observable<string[]> => {
266
+ // const datumList$ = computedData$.pipe(
267
+ // map(d => d.flat())
268
+ // )
269
+ // return highlightObservable ({ datumList$, fullChartParams$, event$ })
270
+ // }
271
+
272
+ export const multiValueCategoryLabelsObservable = ({ computedData$, fullDataFormatter$ }: {
273
+ computedData$: Observable<ComputedDataTypeMap<'multiValue'>>
274
+ fullDataFormatter$: Observable<DataFormatterTypeMap<'multiValue'>>
275
+ }) => {
276
+ return computedData$.pipe(
277
+ map(data => {
278
+ return data
279
+ .map(d => d[0] ? d[0].categoryLabel : '')
280
+ // .filter(d => d != null && d != '')
281
+ }),
282
+ distinctUntilChanged((a, b) => {
283
+ return JSON.stringify(a).length === JSON.stringify(b).length
284
+ }),
285
+ )
286
+ }
287
+
288
+ export const multiValueVisibleComputedDataObservable = ({ computedData$ }: { computedData$: Observable<ComputedDataTypeMap<'multiValue'>> }) => {
289
+ return computedData$.pipe(
290
+ map(data => {
291
+ return data
292
+ .map(categoryData => {
293
+ return categoryData.filter(d => d.visible == true)
294
+ })
295
+ .filter(categoryData => {
296
+ return categoryData.length > 0
297
+ })
298
+ })
299
+ )
300
+ }
301
+
302
+ export const multiValueVisibleComputedLayoutDataObservable = ({ computedLayoutData$ }: { computedLayoutData$: Observable<ComputedLayoutDataMultiValue> }) => {
303
+ return computedLayoutData$.pipe(
304
+ map(data => {
305
+ return data
306
+ .map(categoryData => {
307
+ return categoryData.filter(d => d.visible == true)
308
+ })
309
+ .filter(categoryData => {
310
+ return categoryData.length > 0
311
+ })
312
+ })
313
+ )
314
+ }
315
+
316
+ // 所有container位置(對應series)
317
+ export const multiValueContainerPositionObservable = ({ computedData$, fullDataFormatter$, layout$ }: {
318
+ computedData$: Observable<ComputedDataTypeMap<'multiValue'>>
319
+ fullDataFormatter$: Observable<DataFormatterTypeMap<'multiValue'>>
320
+ layout$: Observable<Layout>
321
+ }): Observable<ContainerPositionScaled[]> => {
322
+
323
+ const multiValueContainerPosition$ = combineLatest({
324
+ computedData: computedData$,
325
+ fullDataFormatter: fullDataFormatter$,
326
+ layout: layout$,
327
+ }).pipe(
328
+ switchMap(async (d) => d),
329
+ map(data => {
330
+
331
+ if (data.fullDataFormatter.separateCategory) {
332
+ // -- 依slotIndexes計算 --
333
+ return calcGridContainerLayout(data.layout, data.fullDataFormatter.container, data.computedData.length)
334
+ // return data.computedData.map((seriesData, seriesIndex) => {
335
+ // const columnIndex = seriesIndex % data.fullDataFormatter.container.columnAmount
336
+ // const rowIndex = Math.floor(seriesIndex / data.fullDataFormatter.container.columnAmount)
337
+ // const { translate, scale } = calcMultiValueContainerPosition(data.layout, data.fullDataFormatter.container, rowIndex, columnIndex)
338
+ // return {
339
+ // slotIndex: seriesIndex,
340
+ // rowIndex,
341
+ // columnIndex,
342
+ // translate,
343
+ // scale,
344
+ // }
345
+ // })
346
+ } else {
347
+ // -- 無拆分 --
348
+ const multiValueContainerPositionArr = calcGridContainerLayout(data.layout, data.fullDataFormatter.container, 1)
349
+ return data.computedData.map((d, i) => multiValueContainerPositionArr[0]) // 每個series相同位置
350
+ // const columnIndex = 0
351
+ // const rowIndex = 0
352
+ // return data.computedData.map((seriesData, seriesIndex) => {
353
+ // const { translate, scale } = calcMultiValueContainerPosition(data.layout, data.fullDataFormatter.container, rowIndex, columnIndex)
354
+ // return {
355
+ // slotIndex: 0,
356
+ // rowIndex,
357
+ // columnIndex,
358
+ // translate,
359
+ // scale,
360
+ // }
361
+ // })
362
+ }
363
+ })
364
+ )
365
+
366
+ return multiValueContainerPosition$
367
+ }
368
+
369
+
370
+ export const filteredMinMaxXYDataObservable = ({ visibleComputedLayoutData$, minMaxXY$, fullDataFormatter$ }: {
371
+ visibleComputedLayoutData$: Observable<ComputedLayoutDataMultiValue>
372
+ minMaxXY$: Observable<{ minX: number, maxX: number, minY: number, maxY: number }>
373
+ fullDataFormatter$: Observable<DataFormatterTypeMap<'multiValue'>>
374
+ }) => {
375
+ return combineLatest({
376
+ visibleComputedLayoutData: visibleComputedLayoutData$,
377
+ minMaxXY: minMaxXY$,
378
+ fullDataFormatter: fullDataFormatter$,
379
+ }).pipe(
380
+ map(data => {
381
+ // 所有可見資料依 dataFormatter 的 scale 設定篩選出最大小值
382
+ const { minX, maxX, minY, maxY } = (() => {
383
+
384
+ let { minX, maxX, minY, maxY } = data.minMaxXY
385
+
386
+ if (data.fullDataFormatter.xAxis.scaleDomain[0] === 'auto' && minX > 0) {
387
+ minX = 0
388
+ } else if (typeof data.fullDataFormatter.xAxis.scaleDomain[0] === 'number') {
389
+ minX = data.fullDataFormatter.xAxis.scaleDomain[0] as number
390
+ }
391
+ if (data.fullDataFormatter.xAxis.scaleDomain[1] === 'auto' && maxX < 0) {
392
+ maxX = 0
393
+ } else if (typeof data.fullDataFormatter.xAxis.scaleDomain[1] === 'number') {
394
+ maxX = data.fullDataFormatter.xAxis.scaleDomain[1] as number
395
+ }
396
+ if (data.fullDataFormatter.yAxis.scaleDomain[0] === 'auto' && minY > 0) {
397
+ minY = 0
398
+ } else if (typeof data.fullDataFormatter.yAxis.scaleDomain[0] === 'number') {
399
+ minY = data.fullDataFormatter.yAxis.scaleDomain[0] as number
400
+ }
401
+ if (data.fullDataFormatter.yAxis.scaleDomain[1] === 'auto' && maxY < 0) {
402
+ maxY = 0
403
+ } else if (typeof data.fullDataFormatter.yAxis.scaleDomain[1] === 'number') {
404
+ maxY = data.fullDataFormatter.yAxis.scaleDomain[1] as number
405
+ }
406
+
407
+ return { minX, maxX, minY, maxY }
408
+ })()
409
+ // console.log({ minX, maxX, minY, maxY })
410
+ let datumList: ComputedLayoutDatumMultiValue[] = []
411
+ let minXDatum: ComputedLayoutDatumMultiValue | null = null
412
+ let maxXDatum: ComputedLayoutDatumMultiValue | null = null
413
+ let minYDatum: ComputedLayoutDatumMultiValue | null = null
414
+ let maxYDatum: ComputedLayoutDatumMultiValue | null = null
415
+ // console.log('data.visibleComputedLayoutData', data.visibleComputedLayoutData)
416
+ // minX, maxX, minY, maxY 範圍內的最大最小值資料
417
+ // console.log({ minX, maxX, minY, maxY })
418
+ for (let categoryData of data.visibleComputedLayoutData) {
419
+ for (let datum of categoryData) {
420
+ // 比較矩形範圍(所以 minX, maxX, minY, maxY 要同時比較)
421
+ if (datum.value[0] >= minX && datum.value[0] <= maxX && datum.value[1] >= minY && datum.value[1] <= maxY) {
422
+ datumList.push(datum)
423
+ if (minXDatum == null || datum.value[0] < minXDatum.value[0]) {
424
+ minXDatum = datum
425
+ }
426
+ if (maxXDatum == null || datum.value[0] > maxXDatum.value[0]) {
427
+ maxXDatum = datum
428
+ }
429
+ if (minYDatum == null || datum.value[1] < minYDatum.value[1]) {
430
+ minYDatum = datum
431
+ }
432
+ if (maxYDatum == null || datum.value[1] > maxYDatum.value[1]) {
433
+ maxYDatum = datum
434
+ }
435
+ }
436
+ }
437
+ }
438
+
439
+ return {
440
+ datumList,
441
+ minXDatum,
442
+ maxXDatum,
443
+ minYDatum,
444
+ maxYDatum
445
+ }
446
+ })
447
+ )
448
+ }
449
+
450
+ export const multiValueGraphicTransformObservable = ({ minMaxXY$, filteredMinMaxXYData$, fullDataFormatter$, layout$ }: {
451
+ minMaxXY$: Observable<{ minX: number, maxX: number, minY: number, maxY: number }>
452
+ filteredMinMaxXYData$: Observable<{
453
+ minXDatum: ComputedLayoutDatumMultiValue
454
+ maxXDatum: ComputedLayoutDatumMultiValue
455
+ minYDatum: ComputedLayoutDatumMultiValue
456
+ maxYDatum: ComputedLayoutDatumMultiValue
457
+ }>
458
+ fullDataFormatter$: Observable<DataFormatterTypeMap<'multiValue'>>
459
+ layout$: Observable<Layout>
460
+ }): Observable<TransformData> => {
461
+ const destroy$ = new Subject()
462
+
463
+ function calcDataAreaTransform ({ minMaxXY, filteredMinMaxXYData, xAxis, yAxis, width, height }: {
464
+ minMaxXY: { minX: number, maxX: number, minY: number, maxY: number }
465
+ filteredMinMaxXYData: {
466
+ minXDatum: ComputedLayoutDatumMultiValue
467
+ maxXDatum: ComputedLayoutDatumMultiValue
468
+ minYDatum: ComputedLayoutDatumMultiValue
469
+ maxYDatum: ComputedLayoutDatumMultiValue
470
+ }
471
+ xAxis: DataFormatterAxis
472
+ yAxis: DataFormatterAxis
473
+ width: number
474
+ height: number
475
+ }): TransformData {
476
+ // const flatData = data.flat()
477
+
478
+ let translateX = 0
479
+ let translateY = 0
480
+ let scaleX = 0
481
+ let scaleY = 0
482
+
483
+ // // minX, maxX, filteredMinX, filteredMaxX
484
+ // let filteredMinX = 0
485
+ // let filteredMaxX = 0
486
+ // let [minX, maxX] = getMinAndMax(flatData.map(d => d.value[0]))
487
+ // if (minX === maxX) {
488
+ // minX = maxX - 1 // 避免最大及最小值相同造成無法計算scale
489
+ // }
490
+ // if (xAxis.scaleDomain[0] === 'auto' && filteredMinX > 0) {
491
+ // filteredMinX = 0
492
+ // } else if (typeof xAxis.scaleDomain[0] === 'number') {
493
+ // filteredMinX = xAxis.scaleDomain[0] as number
494
+ // } else {
495
+ // filteredMinX = minX
496
+ // }
497
+ // if (xAxis.scaleDomain[1] === 'auto' && filteredMaxX < 0) {
498
+ // filteredMaxX = 0
499
+ // } else if (typeof xAxis.scaleDomain[1] === 'number') {
500
+ // filteredMaxX = xAxis.scaleDomain[1] as number
501
+ // } else {
502
+ // filteredMaxX = maxX
503
+ // }
504
+ // if (filteredMinX === filteredMaxX) {
505
+ // filteredMinX = filteredMaxX - 1 // 避免最大及最小值相同造成無法計算scale
506
+ // }
507
+
508
+ // // minY, maxY, filteredMinY, filteredMaxY
509
+ // let filteredMinY = 0
510
+ // let filteredMaxY = 0
511
+ // let [minY, maxY] = getMinAndMax(flatData.map(d => d.value[1]))
512
+ // console.log('filteredMinMaxXYData', filteredMinMaxXYData)
513
+ let { minX, maxX, minY, maxY } = minMaxXY
514
+ // console.log({ minX, maxX, minY, maxY })
515
+ let filteredMinX = filteredMinMaxXYData.minXDatum.value[0] ?? 0
516
+ let filteredMaxX = filteredMinMaxXYData.maxXDatum.value[0] ?? 0
517
+ let filteredMinY = filteredMinMaxXYData.minYDatum.value[1] ?? 0
518
+ let filteredMaxY = filteredMinMaxXYData.maxYDatum.value[1] ?? 0
519
+
520
+ // if (yAxis.scaleDomain[0] === 'auto' && filteredMinY > 0) {
521
+ // filteredMinY = 0
522
+ // } else if (typeof yAxis.scaleDomain[0] === 'number') {
523
+ // filteredMinY = yAxis.scaleDomain[0] as number
524
+ // } else {
525
+ // filteredMinY = minY
526
+ // }
527
+ // if (yAxis.scaleDomain[1] === 'auto' && filteredMaxY < 0) {
528
+ // filteredMaxY = 0
529
+ // } else if (typeof yAxis.scaleDomain[1] === 'number') {
530
+ // filteredMaxY = yAxis.scaleDomain[1] as number
531
+ // } else {
532
+ // filteredMaxY = maxY
533
+ // }
534
+
535
+ // console.log({ minX, maxX, minY, maxY, filteredMinX, filteredMaxX, filteredMinY, filteredMaxY })
536
+ if (filteredMinX === filteredMaxX && filteredMaxX === 0) {
537
+ // 避免最大及最小值相同造成無法計算scale
538
+ filteredMaxX = 1
539
+ }
540
+ if (filteredMinY === filteredMaxY && filteredMaxY === 0) {
541
+ // 避免最大及最小值相同造成無法計算scale
542
+ filteredMaxY = 1
543
+ }
544
+ if (minX === maxX && maxX === 0) {
545
+ // 避免最大及最小值相同造成無法計算scale
546
+ maxX = 1
547
+ }
548
+ if (minY === maxY && maxY === 0) {
549
+ // 避免最大及最小值相同造成無法計算scale
550
+ maxY = 1
551
+ }
552
+ // -- xScale --
553
+ const xScale: d3.ScaleLinear<number, number> = createValueToAxisScale({
554
+ maxValue: filteredMaxX,
555
+ minValue: filteredMinX,
556
+ axisWidth: width,
557
+ scaleDomain: xAxis.scaleDomain,
558
+ scaleRange: xAxis.scaleRange
559
+ })
560
+
561
+ // -- translateX, scaleX --
562
+ const rangeMinX = xScale(minX > 0 ? 0 : minX) // * 因為原本的座標就是以 0 到最大值或最小值範範圍計算的,所以這邊也是用同樣的方式計算
563
+ const rangeMaxX = xScale(maxX < 0 ? 0 : maxX) // * 因為原本的座標就是以 0 到最大值或最小值範範圍計算的,所以這邊也是用同樣的方式計算
564
+ translateX = rangeMinX
565
+ const gWidth = rangeMaxX - rangeMinX
566
+ scaleX = gWidth / width
567
+ // console.log({ gWidth, width, rangeMaxX, rangeMinX, scaleX, translateX })
568
+ // -- yScale --
569
+ const yScale: d3.ScaleLinear<number, number> = createValueToAxisScale({
570
+ maxValue: filteredMaxY,
571
+ minValue: filteredMinY,
572
+ axisWidth: height,
573
+ scaleDomain: yAxis.scaleDomain,
574
+ scaleRange: yAxis.scaleRange,
575
+ reverse: true
576
+ })
577
+
578
+ // -- translateY, scaleY --
579
+ const rangeMinY = yScale(minY > 0 ? 0 : minY) // * 因為原本的座標就是以 0 到最大值或最小值範範圍計算的,所以這邊也是用同樣的方式計算
580
+ const rangeMaxY = yScale(maxY < 0 ? 0 : maxY) // * 因為原本的座標就是以 0 到最大值或最小值範範圍計算的,所以這邊也是用同樣的方式計算
581
+ translateY = rangeMaxY // 最大值的 y 最小(最上方)
582
+ const gHeight = rangeMinY - rangeMaxY // 最大的 y 減最小的 y
583
+ scaleY = gHeight / height
584
+
585
+ return {
586
+ translate: [translateX, translateY],
587
+ scale: [scaleX, scaleY],
588
+ rotate: 0,
589
+ rotateX: 0,
590
+ rotateY: 0,
591
+ value: `translate(${translateX}px, ${translateY}px) scale(${scaleX}, ${scaleY})`
592
+ }
593
+ }
594
+
595
+ return new Observable(subscriber => {
596
+ combineLatest({
597
+ minMaxXY: minMaxXY$,
598
+ filteredMinMaxXYData: filteredMinMaxXYData$,
599
+ fullDataFormatter: fullDataFormatter$,
600
+ layout: layout$
601
+ }).pipe(
602
+ takeUntil(destroy$),
603
+ switchMap(async (d) => d),
604
+ ).subscribe(data => {
605
+ if (!data.filteredMinMaxXYData.minXDatum || !data.filteredMinMaxXYData.maxXDatum
606
+ || data.filteredMinMaxXYData.minXDatum.value[0] == null || data.filteredMinMaxXYData.maxXDatum.value[0] == null
607
+ || !data.filteredMinMaxXYData.minYDatum || !data.filteredMinMaxXYData.maxYDatum
608
+ || data.filteredMinMaxXYData.minYDatum.value[1] == null || data.filteredMinMaxXYData.maxYDatum.value[1] == null
609
+ ) {
610
+ return
611
+ }
612
+ const dataAreaTransformData = calcDataAreaTransform({
613
+ minMaxXY: data.minMaxXY,
614
+ filteredMinMaxXYData: data.filteredMinMaxXYData,
615
+ xAxis: data.fullDataFormatter.xAxis,
616
+ yAxis: data.fullDataFormatter.yAxis,
617
+ width: data.layout.width,
618
+ height: data.layout.height
619
+ })
620
+
621
+ // console.log('dataAreaTransformData', dataAreaTransformData)
622
+
623
+ subscriber.next(dataAreaTransformData)
624
+ })
625
+
626
+ return function unscbscribe () {
627
+ destroy$.next(undefined)
628
+ }
629
+ })
630
+ }
631
+
632
+ export const multiValueGraphicReverseScaleObservable = ({ multiValueContainerPosition$, multiValueGraphicTransform$ }: {
633
+ multiValueContainerPosition$: Observable<ContainerPositionScaled[]>
634
+ // multiValueAxesTransform$: Observable<TransformData>
635
+ multiValueGraphicTransform$: Observable<TransformData>
636
+ }): Observable<[number, number][]> => {
637
+ return combineLatest({
638
+ multiValueContainerPosition: multiValueContainerPosition$,
639
+ // multiValueAxesTransform: multiValueAxesTransform$,
640
+ multiValueGraphicTransform: multiValueGraphicTransform$,
641
+ }).pipe(
642
+ switchMap(async (d) => d),
643
+ map(data => {
644
+ // if (data.multiValueAxesTransform.rotate == 0 || data.multiValueAxesTransform.rotate == 180) {
645
+ return data.multiValueContainerPosition.map((series, seriesIndex) => {
646
+ return [
647
+ 1 / data.multiValueGraphicTransform.scale[0] / data.multiValueContainerPosition[seriesIndex].scale[0],
648
+ 1 / data.multiValueGraphicTransform.scale[1] / data.multiValueContainerPosition[seriesIndex].scale[1],
649
+ ]
650
+ })
651
+ // } else {
652
+ // return data.multiValueContainerPosition.map((series, seriesIndex) => {
653
+ // // 由於有垂直的旋轉,所以外層 (container) x和y的scale要互換
654
+ // return [
655
+ // 1 / data.multiValueGraphicTransform.scale[0] / data.multiValueContainerPosition[seriesIndex].scale[1],
656
+ // 1 / data.multiValueGraphicTransform.scale[1] / data.multiValueContainerPosition[seriesIndex].scale[0],
657
+ // ]
658
+ // })
659
+ // }
660
+ }),
661
+ )
662
+ }