@tranding-custom-chart/core 0.0.0-stage → 2.0.0
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.
- package/README.md +37 -2
- package/dist/animation/animator.d.ts +28 -0
- package/dist/animation/animator.d.ts.map +1 -0
- package/dist/animation/animator.js +72 -0
- package/dist/animation/easing.d.ts +22 -0
- package/dist/animation/easing.d.ts.map +1 -0
- package/dist/animation/easing.js +69 -0
- package/dist/data/downsample.d.ts +12 -0
- package/dist/data/downsample.d.ts.map +1 -0
- package/dist/data/downsample.js +60 -0
- package/dist/data/validation.d.ts +24 -0
- package/dist/data/validation.d.ts.map +1 -0
- package/dist/data/validation.js +93 -0
- package/dist/drawing/drawing-geometry.d.ts +44 -0
- package/dist/drawing/drawing-geometry.d.ts.map +1 -0
- package/dist/drawing/drawing-geometry.js +297 -0
- package/dist/drawing/drawing-history.d.ts +33 -0
- package/dist/drawing/drawing-history.d.ts.map +1 -0
- package/dist/drawing/drawing-history.js +61 -0
- package/dist/drawing/drawing-storage.d.ts +27 -0
- package/dist/drawing/drawing-storage.d.ts.map +1 -0
- package/dist/drawing/drawing-storage.js +106 -0
- package/dist/drawing/index.d.ts +8 -0
- package/dist/drawing/index.d.ts.map +1 -0
- package/dist/drawing/index.js +7 -0
- package/dist/events/event-emitter.d.ts +44 -0
- package/dist/events/event-emitter.d.ts.map +1 -0
- package/dist/events/event-emitter.js +172 -0
- package/dist/events/event-types.d.ts +97 -0
- package/dist/events/event-types.d.ts.map +1 -0
- package/dist/events/event-types.js +5 -0
- package/dist/events/selection-resolver.d.ts +33 -0
- package/dist/events/selection-resolver.d.ts.map +1 -0
- package/dist/events/selection-resolver.js +155 -0
- package/dist/formatting/formatters.d.ts +32 -0
- package/dist/formatting/formatters.d.ts.map +1 -0
- package/dist/formatting/formatters.js +88 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/indexing/bisect.d.ts +25 -0
- package/dist/indexing/bisect.d.ts.map +1 -0
- package/dist/indexing/bisect.js +70 -0
- package/dist/math/box-plot.d.ts +7 -0
- package/dist/math/box-plot.d.ts.map +1 -0
- package/dist/math/box-plot.js +40 -0
- package/dist/math/spline.d.ts +19 -0
- package/dist/math/spline.d.ts.map +1 -0
- package/dist/math/spline.js +32 -0
- package/dist/options.d.ts +169 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +353 -0
- package/dist/plugins/plugin-manager.d.ts +27 -0
- package/dist/plugins/plugin-manager.d.ts.map +1 -0
- package/dist/plugins/plugin-manager.js +149 -0
- package/dist/plugins/plugin-types.d.ts +43 -0
- package/dist/plugins/plugin-types.d.ts.map +1 -0
- package/dist/plugins/plugin-types.js +6 -0
- package/dist/scales/continuous-time-scale.d.ts +20 -0
- package/dist/scales/continuous-time-scale.d.ts.map +1 -0
- package/dist/scales/continuous-time-scale.js +97 -0
- package/dist/scales/index-time-scale.d.ts +28 -0
- package/dist/scales/index-time-scale.d.ts.map +1 -0
- package/dist/scales/index-time-scale.js +88 -0
- package/dist/scales/linear-scale.d.ts +22 -0
- package/dist/scales/linear-scale.d.ts.map +1 -0
- package/dist/scales/linear-scale.js +71 -0
- package/dist/scales/log-scale.d.ts +23 -0
- package/dist/scales/log-scale.d.ts.map +1 -0
- package/dist/scales/log-scale.js +96 -0
- package/dist/scales/ticks.d.ts +13 -0
- package/dist/scales/ticks.d.ts.map +1 -0
- package/dist/scales/ticks.js +66 -0
- package/dist/types.d.ts +331 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +46 -0
- package/dist/version.d.ts +6 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +5 -0
- package/package.json +30 -4
package/README.md
CHANGED
|
@@ -1,3 +1,38 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @tranding-custom-chart/core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Pure TypeScript domain primitives, scales, math, data structures, and formatting for the `@tranding-custom-chart` suite.
|
|
4
|
+
|
|
5
|
+
[](https://semver.org)
|
|
6
|
+
[](#)
|
|
7
|
+
[](#)
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
- **Dynamic Options System**: `ChartOptions`, `DEFAULT_CHART_OPTIONS`, `mergeChartOptions`, `validateChartOptions`, and `resolveTheme`.
|
|
11
|
+
- **Scales**: `LinearScale`, `LogScale` (safe log-space projection with decade ticks), `IndexTimeScale` (discrete session-aware time scale), and `ContinuousTimeScale`.
|
|
12
|
+
- **Domain Invariants**: Strict financial candle validation ($Low \le \min(O,C) \le \max(O,C) \le High$).
|
|
13
|
+
- **Data Downsampling**: Largest-Triangle-Three-Buckets (LTTB) algorithm.
|
|
14
|
+
- **Fast Search**: $O(\log N)$ bisection culling for visible coordinate ranges.
|
|
15
|
+
- **Catmull-Rom Splines**: $C^1$ continuous cubic Bezier spline math.
|
|
16
|
+
- **Types**: 13 Chart types, Drawing tools, Indicators, Scales, Themes, and Color tokens.
|
|
17
|
+
- **Runtime Version Constant**: `CHART_VERSION = '2.0.0'`.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
```bash
|
|
21
|
+
npm install @tranding-custom-chart/core
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
```ts
|
|
26
|
+
import { createLinearScale, createIndexTimeScale, CHART_VERSION } from '@tranding-custom-chart/core';
|
|
27
|
+
|
|
28
|
+
console.log(`Using chart-core v${CHART_VERSION}`);
|
|
29
|
+
|
|
30
|
+
const xScale = createIndexTimeScale(200, [0, 800]);
|
|
31
|
+
const yScale = createLinearScale([100, 200], [400, 0]);
|
|
32
|
+
|
|
33
|
+
console.log('X pixel for bar #10:', xScale.scale(10));
|
|
34
|
+
console.log('Y pixel for price 150:', yScale.scale(150));
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## License
|
|
38
|
+
UNLICENSED (Internal / In-House Use)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-core/src/animation/animator.ts
|
|
3
|
+
* Interpolation utilities and animation loop controller for progressive chart transitions.
|
|
4
|
+
*/
|
|
5
|
+
import { type EasingFunction, type EasingName } from './easing.js';
|
|
6
|
+
export declare function interpolateNumber(from: number, to: number, progress: number): number;
|
|
7
|
+
export declare function interpolateNumberArray(from: readonly number[], to: readonly number[], progress: number): number[];
|
|
8
|
+
export declare const interpolateArrays: typeof interpolateNumberArray;
|
|
9
|
+
export interface AnimationOptions {
|
|
10
|
+
/** Duration in milliseconds (default: 400ms) */
|
|
11
|
+
readonly duration?: number | undefined;
|
|
12
|
+
/** Easing function or name (default: 'easeOutQuart') */
|
|
13
|
+
readonly easing?: EasingName | EasingFunction | undefined;
|
|
14
|
+
/** Frame update callback called with progress value [0, 1] */
|
|
15
|
+
readonly onUpdate: (progress: number) => void;
|
|
16
|
+
/** Callback fired when animation finishes successfully */
|
|
17
|
+
readonly onComplete?: (() => void) | undefined;
|
|
18
|
+
}
|
|
19
|
+
export declare class Animator {
|
|
20
|
+
private readonly options;
|
|
21
|
+
private rafId;
|
|
22
|
+
private startTime;
|
|
23
|
+
private isCancelled;
|
|
24
|
+
constructor(options: AnimationOptions);
|
|
25
|
+
start(): void;
|
|
26
|
+
cancel(): void;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=animator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animator.d.ts","sourceRoot":"","sources":["../../src/animation/animator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAiB,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAElF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEpF;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,EAAE,SAAS,MAAM,EAAE,EACrB,QAAQ,EAAE,MAAM,GACf,MAAM,EAAE,CAWV;AAED,eAAO,MAAM,iBAAiB,+BAAyB,CAAC;AAExD,MAAM,WAAW,gBAAgB;IAC/B,gDAAgD;IAChD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,wDAAwD;IACxD,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,cAAc,GAAG,SAAS,CAAC;IAC1D,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,0DAA0D;IAC1D,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;CAChD;AAED,qBAAa,QAAQ;IAKP,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,WAAW,CAAS;gBAEC,OAAO,EAAE,gBAAgB;IAE/C,KAAK,IAAI,IAAI;IAyCb,MAAM,IAAI,IAAI;CAOtB"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-core/src/animation/animator.ts
|
|
3
|
+
* Interpolation utilities and animation loop controller for progressive chart transitions.
|
|
4
|
+
*/
|
|
5
|
+
import { resolveEasing } from './easing.js';
|
|
6
|
+
export function interpolateNumber(from, to, progress) {
|
|
7
|
+
return from + (to - from) * progress;
|
|
8
|
+
}
|
|
9
|
+
export function interpolateNumberArray(from, to, progress) {
|
|
10
|
+
const maxLength = Math.max(from.length, to.length);
|
|
11
|
+
const result = new Array(maxLength);
|
|
12
|
+
for (let i = 0; i < maxLength; i++) {
|
|
13
|
+
const f = i < from.length ? (from[i] ?? 0) : 0;
|
|
14
|
+
const t = i < to.length ? (to[i] ?? 0) : 0;
|
|
15
|
+
result[i] = interpolateNumber(f, t, progress);
|
|
16
|
+
}
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
export const interpolateArrays = interpolateNumberArray;
|
|
20
|
+
export class Animator {
|
|
21
|
+
options;
|
|
22
|
+
rafId = null;
|
|
23
|
+
startTime = null;
|
|
24
|
+
isCancelled = false;
|
|
25
|
+
constructor(options) {
|
|
26
|
+
this.options = options;
|
|
27
|
+
}
|
|
28
|
+
start() {
|
|
29
|
+
this.cancel();
|
|
30
|
+
this.isCancelled = false;
|
|
31
|
+
this.startTime = null;
|
|
32
|
+
const duration = Math.max(16, this.options.duration ?? 400);
|
|
33
|
+
const easingFn = resolveEasing(this.options.easing);
|
|
34
|
+
const step = (timestamp) => {
|
|
35
|
+
if (this.isCancelled)
|
|
36
|
+
return;
|
|
37
|
+
if (this.startTime === null) {
|
|
38
|
+
this.startTime = timestamp;
|
|
39
|
+
}
|
|
40
|
+
const elapsed = timestamp - this.startTime;
|
|
41
|
+
const rawProgress = Math.min(1, elapsed / duration);
|
|
42
|
+
const easedProgress = easingFn(rawProgress);
|
|
43
|
+
this.options.onUpdate(easedProgress);
|
|
44
|
+
if (rawProgress < 1) {
|
|
45
|
+
if (typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function') {
|
|
46
|
+
this.rafId = window.requestAnimationFrame(step);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// Node / headless fallback
|
|
50
|
+
setTimeout(() => step(timestamp + 16), 16);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
this.rafId = null;
|
|
55
|
+
this.options.onComplete?.();
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
if (typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function') {
|
|
59
|
+
this.rafId = window.requestAnimationFrame(step);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
setTimeout(() => step(16), 16);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
cancel() {
|
|
66
|
+
this.isCancelled = true;
|
|
67
|
+
if (this.rafId !== null && typeof window !== 'undefined' && typeof window.cancelAnimationFrame === 'function') {
|
|
68
|
+
window.cancelAnimationFrame(this.rafId);
|
|
69
|
+
this.rafId = null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-core/src/animation/easing.ts
|
|
3
|
+
* Comprehensive mathematical easing functions and animation curves.
|
|
4
|
+
*/
|
|
5
|
+
export type EasingName = 'linear' | 'easeInQuad' | 'easeOutQuad' | 'easeInOutQuad' | 'easeInCubic' | 'easeOutCubic' | 'easeInOutCubic' | 'easeInQuart' | 'easeOutQuart' | 'easeInOutQuart' | 'easeInBounce' | 'easeOutBounce' | 'easeInOutBounce';
|
|
6
|
+
export type EasingFunction = (t: number) => number;
|
|
7
|
+
export declare const EASING_FUNCTIONS: Record<EasingName, EasingFunction>;
|
|
8
|
+
export declare function resolveEasing(easing?: EasingName | EasingFunction): EasingFunction;
|
|
9
|
+
export declare const linear: EasingFunction;
|
|
10
|
+
export declare const easeInQuad: EasingFunction;
|
|
11
|
+
export declare const easeOutQuad: EasingFunction;
|
|
12
|
+
export declare const easeInOutQuad: EasingFunction;
|
|
13
|
+
export declare const easeInCubic: EasingFunction;
|
|
14
|
+
export declare const easeOutCubic: EasingFunction;
|
|
15
|
+
export declare const easeInOutCubic: EasingFunction;
|
|
16
|
+
export declare const easeInQuart: EasingFunction;
|
|
17
|
+
export declare const easeOutQuart: EasingFunction;
|
|
18
|
+
export declare const easeInOutQuart: EasingFunction;
|
|
19
|
+
export declare const easeInBounce: EasingFunction;
|
|
20
|
+
export declare const easeOutBounce: EasingFunction;
|
|
21
|
+
export declare const easeInOutBounce: EasingFunction;
|
|
22
|
+
//# sourceMappingURL=easing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"easing.d.ts","sourceRoot":"","sources":["../../src/animation/easing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,UAAU,GAClB,QAAQ,GACR,YAAY,GACZ,aAAa,GACb,eAAe,GACf,aAAa,GACb,cAAc,GACd,gBAAgB,GAChB,aAAa,GACb,cAAc,GACd,gBAAgB,GAChB,cAAc,GACd,eAAe,GACf,iBAAiB,CAAC;AAEtB,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;AAEnD,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,UAAU,EAAE,cAAc,CAiD/D,CAAC;AAEF,wBAAgB,aAAa,CAAC,MAAM,GAAE,UAAU,GAAG,cAA+B,GAAG,cAAc,CAKlG;AAED,eAAO,MAAM,MAAM,EAAE,cAAwC,CAAC;AAC9D,eAAO,MAAM,UAAU,EAAE,cAA4C,CAAC;AACtE,eAAO,MAAM,WAAW,EAAE,cAA6C,CAAC;AACxE,eAAO,MAAM,aAAa,EAAE,cAA+C,CAAC;AAC5E,eAAO,MAAM,WAAW,EAAE,cAA6C,CAAC;AACxE,eAAO,MAAM,YAAY,EAAE,cAA8C,CAAC;AAC1E,eAAO,MAAM,cAAc,EAAE,cAAgD,CAAC;AAC9E,eAAO,MAAM,WAAW,EAAE,cAA6C,CAAC;AACxE,eAAO,MAAM,YAAY,EAAE,cAA8C,CAAC;AAC1E,eAAO,MAAM,cAAc,EAAE,cAAgD,CAAC;AAC9E,eAAO,MAAM,YAAY,EAAE,cAA8C,CAAC;AAC1E,eAAO,MAAM,aAAa,EAAE,cAA+C,CAAC;AAC5E,eAAO,MAAM,eAAe,EAAE,cAAiD,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-core/src/animation/easing.ts
|
|
3
|
+
* Comprehensive mathematical easing functions and animation curves.
|
|
4
|
+
*/
|
|
5
|
+
export const EASING_FUNCTIONS = {
|
|
6
|
+
linear: (t) => t,
|
|
7
|
+
easeInQuad: (t) => t * t,
|
|
8
|
+
easeOutQuad: (t) => t * (2 - t),
|
|
9
|
+
easeInOutQuad: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
|
|
10
|
+
easeInCubic: (t) => t * t * t,
|
|
11
|
+
easeOutCubic: (t) => {
|
|
12
|
+
const t1 = t - 1;
|
|
13
|
+
return t1 * t1 * t1 + 1;
|
|
14
|
+
},
|
|
15
|
+
easeInOutCubic: (t) => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1,
|
|
16
|
+
easeInQuart: (t) => t * t * t * t,
|
|
17
|
+
easeOutQuart: (t) => {
|
|
18
|
+
const t1 = t - 1;
|
|
19
|
+
return 1 - t1 * t1 * t1 * t1;
|
|
20
|
+
},
|
|
21
|
+
easeInOutQuart: (t) => {
|
|
22
|
+
if (t < 0.5)
|
|
23
|
+
return 8 * t * t * t * t;
|
|
24
|
+
const t1 = t - 1;
|
|
25
|
+
return 1 - 8 * t1 * t1 * t1 * t1;
|
|
26
|
+
},
|
|
27
|
+
easeOutBounce: (t) => {
|
|
28
|
+
const n1 = 7.5625;
|
|
29
|
+
const d1 = 2.75;
|
|
30
|
+
if (t < 1 / d1) {
|
|
31
|
+
return n1 * t * t;
|
|
32
|
+
}
|
|
33
|
+
else if (t < 2 / d1) {
|
|
34
|
+
const t1 = t - 1.5 / d1;
|
|
35
|
+
return n1 * t1 * t1 + 0.75;
|
|
36
|
+
}
|
|
37
|
+
else if (t < 2.5 / d1) {
|
|
38
|
+
const t1 = t - 2.25 / d1;
|
|
39
|
+
return n1 * t1 * t1 + 0.9375;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
const t1 = t - 2.625 / d1;
|
|
43
|
+
return n1 * t1 * t1 + 0.984375;
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
easeInBounce: (t) => 1 - EASING_FUNCTIONS.easeOutBounce(1 - t),
|
|
47
|
+
easeInOutBounce: (t) => t < 0.5
|
|
48
|
+
? (1 - EASING_FUNCTIONS.easeOutBounce(1 - 2 * t)) / 2
|
|
49
|
+
: (1 + EASING_FUNCTIONS.easeOutBounce(2 * t - 1)) / 2,
|
|
50
|
+
};
|
|
51
|
+
export function resolveEasing(easing = 'easeOutQuart') {
|
|
52
|
+
if (typeof easing === 'function') {
|
|
53
|
+
return easing;
|
|
54
|
+
}
|
|
55
|
+
return EASING_FUNCTIONS[easing] ?? EASING_FUNCTIONS.easeOutQuart;
|
|
56
|
+
}
|
|
57
|
+
export const linear = EASING_FUNCTIONS.linear;
|
|
58
|
+
export const easeInQuad = EASING_FUNCTIONS.easeInQuad;
|
|
59
|
+
export const easeOutQuad = EASING_FUNCTIONS.easeOutQuad;
|
|
60
|
+
export const easeInOutQuad = EASING_FUNCTIONS.easeInOutQuad;
|
|
61
|
+
export const easeInCubic = EASING_FUNCTIONS.easeInCubic;
|
|
62
|
+
export const easeOutCubic = EASING_FUNCTIONS.easeOutCubic;
|
|
63
|
+
export const easeInOutCubic = EASING_FUNCTIONS.easeInOutCubic;
|
|
64
|
+
export const easeInQuart = EASING_FUNCTIONS.easeInQuart;
|
|
65
|
+
export const easeOutQuart = EASING_FUNCTIONS.easeOutQuart;
|
|
66
|
+
export const easeInOutQuart = EASING_FUNCTIONS.easeInOutQuart;
|
|
67
|
+
export const easeInBounce = EASING_FUNCTIONS.easeInBounce;
|
|
68
|
+
export const easeOutBounce = EASING_FUNCTIONS.easeOutBounce;
|
|
69
|
+
export const easeInOutBounce = EASING_FUNCTIONS.easeInOutBounce;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-core/src/data/downsample.ts
|
|
3
|
+
* Downsampling algorithms for high-density 2D visual series.
|
|
4
|
+
*/
|
|
5
|
+
import type { Point } from '../types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Downsamples a collection of 2D points using the Largest-Triangle-Three-Buckets (LTTB) algorithm.
|
|
8
|
+
* Preserves visual peaks, troughs, and overall trend characteristics while strictly capping
|
|
9
|
+
* point count to threshold.
|
|
10
|
+
*/
|
|
11
|
+
export declare function downsampleLTTB(data: readonly Point[], threshold: number): readonly Point[];
|
|
12
|
+
//# sourceMappingURL=downsample.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"downsample.d.ts","sourceRoot":"","sources":["../../src/data/downsample.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,SAAS,KAAK,EAAE,EACtB,SAAS,EAAE,MAAM,GAChB,SAAS,KAAK,EAAE,CAoElB"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-core/src/data/downsample.ts
|
|
3
|
+
* Downsampling algorithms for high-density 2D visual series.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Downsamples a collection of 2D points using the Largest-Triangle-Three-Buckets (LTTB) algorithm.
|
|
7
|
+
* Preserves visual peaks, troughs, and overall trend characteristics while strictly capping
|
|
8
|
+
* point count to threshold.
|
|
9
|
+
*/
|
|
10
|
+
export function downsampleLTTB(data, threshold) {
|
|
11
|
+
const dataLen = data.length;
|
|
12
|
+
if (threshold >= dataLen || threshold <= 2) {
|
|
13
|
+
return data;
|
|
14
|
+
}
|
|
15
|
+
const sampled = [];
|
|
16
|
+
let sampledIndex = 0;
|
|
17
|
+
// Bucket size. Leave room for start and end data points
|
|
18
|
+
const every = (dataLen - 2) / (threshold - 2);
|
|
19
|
+
let a = 0; // Initially point a is the first point in the first bucket
|
|
20
|
+
let maxAreaPoint = data[a];
|
|
21
|
+
let nextA = 0;
|
|
22
|
+
sampled[sampledIndex++] = data[a]; // Always add the first point
|
|
23
|
+
for (let i = 0; i < threshold - 2; i++) {
|
|
24
|
+
// Calculate point average for next bucket (containing c)
|
|
25
|
+
let avgX = 0;
|
|
26
|
+
let avgY = 0;
|
|
27
|
+
let avgRangeStart = Math.floor((i + 1) * every) + 1;
|
|
28
|
+
let avgRangeEnd = Math.floor((i + 2) * every) + 1;
|
|
29
|
+
avgRangeEnd = avgRangeEnd < dataLen ? avgRangeEnd : dataLen;
|
|
30
|
+
const avgRangeLength = avgRangeEnd - avgRangeStart;
|
|
31
|
+
for (; avgRangeStart < avgRangeEnd; avgRangeStart++) {
|
|
32
|
+
avgX += data[avgRangeStart].x;
|
|
33
|
+
avgY += data[avgRangeStart].y;
|
|
34
|
+
}
|
|
35
|
+
avgX /= avgRangeLength || 1;
|
|
36
|
+
avgY /= avgRangeLength || 1;
|
|
37
|
+
// Get the range for this bucket
|
|
38
|
+
let rangeOffs = Math.floor(i * every) + 1;
|
|
39
|
+
const rangeTo = Math.floor((i + 1) * every) + 1;
|
|
40
|
+
// Point a
|
|
41
|
+
const pointAX = data[a].x;
|
|
42
|
+
const pointAY = data[a].y;
|
|
43
|
+
let maxArea = -1;
|
|
44
|
+
for (; rangeOffs < rangeTo; rangeOffs++) {
|
|
45
|
+
const current = data[rangeOffs];
|
|
46
|
+
// Calculate triangle area over three points: a, current, and avg of next bucket
|
|
47
|
+
const area = Math.abs((pointAX - avgX) * (current.y - pointAY) -
|
|
48
|
+
(pointAX - current.x) * (avgY - pointAY)) * 0.5;
|
|
49
|
+
if (area > maxArea) {
|
|
50
|
+
maxArea = area;
|
|
51
|
+
maxAreaPoint = current;
|
|
52
|
+
nextA = rangeOffs; // Next a is this point
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
sampled[sampledIndex++] = maxAreaPoint;
|
|
56
|
+
a = nextA;
|
|
57
|
+
}
|
|
58
|
+
sampled[sampledIndex++] = data[dataLen - 1]; // Always add the last point
|
|
59
|
+
return sampled;
|
|
60
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-core/src/data/validation.ts
|
|
3
|
+
* Validation and normalization utilities for financial time series data.
|
|
4
|
+
*/
|
|
5
|
+
import type { OHLCVBar } from '../types.js';
|
|
6
|
+
export interface BarValidationResult {
|
|
7
|
+
readonly valid: boolean;
|
|
8
|
+
readonly errors?: readonly string[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Validates a single OHLCV bar candidate.
|
|
12
|
+
*/
|
|
13
|
+
export declare function validateOHLCVBar(bar: unknown): BarValidationResult;
|
|
14
|
+
/**
|
|
15
|
+
* Normalizes, deduplicates, and sorts a time series collection ascending by time.
|
|
16
|
+
* If duplicate timestamps occur, later items in the array take precedence.
|
|
17
|
+
*/
|
|
18
|
+
export declare function normalizeTimeSeries<T>(items: readonly T[], getTime: (item: T) => number): readonly T[];
|
|
19
|
+
/**
|
|
20
|
+
* Calculates the [minLow, maxHigh] domain across a collection of OHLCV bars.
|
|
21
|
+
* Gracefully handles empty data and flat markets.
|
|
22
|
+
*/
|
|
23
|
+
export declare function calculateOHLCDomain(bars: readonly OHLCVBar[]): [number, number];
|
|
24
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/data/validation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,mBAAmB,CAmDlE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EACnC,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAC3B,SAAS,CAAC,EAAE,CAcd;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAwB/E"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-core/src/data/validation.ts
|
|
3
|
+
* Validation and normalization utilities for financial time series data.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Validates a single OHLCV bar candidate.
|
|
7
|
+
*/
|
|
8
|
+
export function validateOHLCVBar(bar) {
|
|
9
|
+
if (!bar || typeof bar !== 'object') {
|
|
10
|
+
return { valid: false, errors: ['Bar must be a non-null object'] };
|
|
11
|
+
}
|
|
12
|
+
const b = bar;
|
|
13
|
+
const errors = [];
|
|
14
|
+
if (typeof b.time !== 'number' || !Number.isFinite(b.time) || b.time <= 0) {
|
|
15
|
+
errors.push('Bar time must be a positive finite number');
|
|
16
|
+
}
|
|
17
|
+
if (typeof b.open !== 'number' || !Number.isFinite(b.open)) {
|
|
18
|
+
errors.push('Bar open must be a finite number');
|
|
19
|
+
}
|
|
20
|
+
if (typeof b.high !== 'number' || !Number.isFinite(b.high)) {
|
|
21
|
+
errors.push('Bar high must be a finite number');
|
|
22
|
+
}
|
|
23
|
+
if (typeof b.low !== 'number' || !Number.isFinite(b.low)) {
|
|
24
|
+
errors.push('Bar low must be a finite number');
|
|
25
|
+
}
|
|
26
|
+
if (typeof b.close !== 'number' || !Number.isFinite(b.close)) {
|
|
27
|
+
errors.push('Bar close must be a finite number');
|
|
28
|
+
}
|
|
29
|
+
if (errors.length === 0) {
|
|
30
|
+
const { open, high, low, close } = b;
|
|
31
|
+
if (high < low) {
|
|
32
|
+
errors.push(`High (${high}) cannot be less than low (${low})`);
|
|
33
|
+
}
|
|
34
|
+
if (open > high || open < low) {
|
|
35
|
+
errors.push(`Open (${open}) must be between low (${low}) and high (${high})`);
|
|
36
|
+
}
|
|
37
|
+
if (close > high || close < low) {
|
|
38
|
+
errors.push(`Close (${close}) must be between low (${low}) and high (${high})`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (b.volume !== null && b.volume !== undefined) {
|
|
42
|
+
if (typeof b.volume !== 'number' || !Number.isFinite(b.volume) || b.volume < 0) {
|
|
43
|
+
errors.push('Bar volume must be null, undefined, or a non-negative finite number');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return errors.length === 0
|
|
47
|
+
? { valid: true }
|
|
48
|
+
: { valid: false, errors };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Normalizes, deduplicates, and sorts a time series collection ascending by time.
|
|
52
|
+
* If duplicate timestamps occur, later items in the array take precedence.
|
|
53
|
+
*/
|
|
54
|
+
export function normalizeTimeSeries(items, getTime) {
|
|
55
|
+
if (!items || items.length === 0)
|
|
56
|
+
return [];
|
|
57
|
+
const map = new Map();
|
|
58
|
+
for (const item of items) {
|
|
59
|
+
const t = getTime(item);
|
|
60
|
+
if (Number.isFinite(t)) {
|
|
61
|
+
map.set(t, item);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return Array.from(map.entries())
|
|
65
|
+
.sort(([t1], [t2]) => t1 - t2)
|
|
66
|
+
.map(([, item]) => item);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Calculates the [minLow, maxHigh] domain across a collection of OHLCV bars.
|
|
70
|
+
* Gracefully handles empty data and flat markets.
|
|
71
|
+
*/
|
|
72
|
+
export function calculateOHLCDomain(bars) {
|
|
73
|
+
if (!bars || bars.length === 0) {
|
|
74
|
+
return [0, 100];
|
|
75
|
+
}
|
|
76
|
+
let min = Number.POSITIVE_INFINITY;
|
|
77
|
+
let max = Number.NEGATIVE_INFINITY;
|
|
78
|
+
for (let i = 0; i < bars.length; i++) {
|
|
79
|
+
const b = bars[i];
|
|
80
|
+
if (b.low < min)
|
|
81
|
+
min = b.low;
|
|
82
|
+
if (b.high > max)
|
|
83
|
+
max = b.high;
|
|
84
|
+
}
|
|
85
|
+
if (!Number.isFinite(min) || !Number.isFinite(max)) {
|
|
86
|
+
return [0, 100];
|
|
87
|
+
}
|
|
88
|
+
if (min === max) {
|
|
89
|
+
const pad = min === 0 ? 1 : Math.abs(min) * 0.05;
|
|
90
|
+
return [min - pad, max + pad];
|
|
91
|
+
}
|
|
92
|
+
return [min, max];
|
|
93
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-core/src/drawing/drawing-geometry.ts
|
|
3
|
+
* Pure data-domain coordinate calculations, spatial hit-testing, handle resolution,
|
|
4
|
+
* translation, and geometric mutations for interactive chart drawings.
|
|
5
|
+
*/
|
|
6
|
+
import type { DrawingHandleType, DrawingItem, Scale } from '../types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Calculates Euclidean distance between point (px, py) and segment (x1, y1)-(x2, y2).
|
|
9
|
+
*/
|
|
10
|
+
export declare function distanceToSegment(px: number, py: number, x1: number, y1: number, x2: number, y2: number): number;
|
|
11
|
+
/**
|
|
12
|
+
* Checks if point (px, py) is within radius of target (tx, ty).
|
|
13
|
+
*/
|
|
14
|
+
export declare function isNearPoint(px: number, py: number, tx: number, ty: number, radius?: number): boolean;
|
|
15
|
+
export interface DrawingHitResult {
|
|
16
|
+
readonly drawing: DrawingItem;
|
|
17
|
+
readonly handle: DrawingHandleType;
|
|
18
|
+
readonly distance: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Hit-tests an individual drawing against screen coordinates (x, y) given scales.
|
|
22
|
+
* Returns handle type ('p1' | 'p2' | 'body' | 'channelOffset' | 'vertex') or null.
|
|
23
|
+
*/
|
|
24
|
+
export declare function hitTestDrawing(drawing: DrawingItem, x: number, y: number, xScale: Scale<number>, yScale: Scale<number>, tolerance?: number): {
|
|
25
|
+
handle: DrawingHandleType;
|
|
26
|
+
distance: number;
|
|
27
|
+
} | null;
|
|
28
|
+
/**
|
|
29
|
+
* Hit-tests against an entire collection of drawings in reverse z-order (topmost first).
|
|
30
|
+
*/
|
|
31
|
+
export declare function hitTestAllDrawings(drawings: readonly DrawingItem[], x: number, y: number, xScale: Scale<number>, yScale: Scale<number>, tolerance?: number): DrawingHitResult | null;
|
|
32
|
+
/**
|
|
33
|
+
* Translates an entire drawing by deltaIndex and deltaPrice in data-domain coordinates.
|
|
34
|
+
*/
|
|
35
|
+
export declare function translateDrawing(drawing: DrawingItem, deltaIndex: number, deltaPrice: number): DrawingItem;
|
|
36
|
+
/**
|
|
37
|
+
* Moves a specific control handle of a drawing to a new data-domain position.
|
|
38
|
+
*/
|
|
39
|
+
export declare function moveDrawingHandle(drawing: DrawingItem, handle: DrawingHandleType, newIndex: number, newPrice: number): DrawingItem;
|
|
40
|
+
/**
|
|
41
|
+
* Duplicates a drawing with a slight offset in data space.
|
|
42
|
+
*/
|
|
43
|
+
export declare function duplicateDrawing(drawing: DrawingItem, offsetIndex?: number, offsetPrice?: number): DrawingItem;
|
|
44
|
+
//# sourceMappingURL=drawing-geometry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drawing-geometry.d.ts","sourceRoot":"","sources":["../../src/drawing/drawing-geometry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAgB,KAAK,EAAE,MAAM,aAAa,CAAC;AAEvF;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,GACT,MAAM,CAWR;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,SAAI,GAAG,OAAO,CAE/F;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,WAAW,EACpB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,EACrB,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,EACrB,SAAS,SAAI,GACZ;IAAE,MAAM,EAAE,iBAAiB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAyJxD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,SAAS,WAAW,EAAE,EAChC,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,EACrB,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,EACrB,SAAS,SAAI,GACZ,gBAAgB,GAAG,IAAI,CASzB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,WAAW,EACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,WAAW,CAiDb;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,iBAAiB,EACzB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,WAAW,CAsCb;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,WAAW,EACpB,WAAW,SAAI,EACf,WAAW,SAAI,GACd,WAAW,CASb"}
|