@funnest/charts-viz 0.1.1 → 0.2.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/dist/index.d.ts CHANGED
@@ -4,8 +4,8 @@
4
4
  * Pure TypeScript with no DOM access, so the same scales, ticks and SVG path strings
5
5
  * drive the web components (@funnest/charts-react) and React Native (react-native-svg).
6
6
  */
7
- export { linearScale, bandScale, clamp, type LinearScale, type BandScale } from './scale';
8
- export { niceStep, niceDomain, ticks, sampleIndices } from './ticks';
7
+ export { linearScale, logScale, bandScale, clamp, type LinearScale, type LogScale, type ValueScale, type BandScale } from './scale';
8
+ export { niceStep, niceDomain, ticks, logTicks, sampleIndices } from './ticks';
9
9
  export { linePath, areaPath, barPath, segments, type XY, type Curve } from './path';
10
10
  export { extent, maxLength, stack, stackedExtent, percentages, nearestIndex, seriesChange, type SeriesData, type StackedSeries, type PercentageSegment, } from './data';
11
11
  export { DEFAULT_PALETTE, seriesColor } from './palette';
package/dist/index.js CHANGED
@@ -8,6 +8,15 @@ function linearScale(domain, range) {
8
8
  Object.assign(scale, { kind: "linear", domain: [d0, d1], range: [r0, r1] });
9
9
  return scale;
10
10
  }
11
+ function logScale(domain, range) {
12
+ const d0 = Math.max(Number.MIN_VALUE, domain[0]);
13
+ const d1 = Math.max(d0, domain[1]);
14
+ const inner = linearScale([Math.log10(d0), Math.log10(d1)], range);
15
+ const scale = ((v) => inner(Math.log10(Math.max(v, d0))));
16
+ scale.invert = (p) => 10 ** inner.invert(p);
17
+ Object.assign(scale, { kind: "log", domain: [d0, d1], range: [range[0], range[1]] });
18
+ return scale;
19
+ }
11
20
  function bandScale(count, range, padding = 0.2) {
12
21
  const [r0, r1] = range;
13
22
  const n = Math.max(1, count);
@@ -61,6 +70,22 @@ function sampleIndices(count, max) {
61
70
  for (let i = 0; i < count; i += every) out.push(i);
62
71
  return out;
63
72
  }
73
+ function logTicks(min, max, count = 5) {
74
+ if (!(min > 0) || !(max > min)) return ticks(min, max, count);
75
+ const lo = Math.floor(Math.log10(min));
76
+ const hi = Math.ceil(Math.log10(max));
77
+ if (hi - lo <= 1) return ticks(min, max, count);
78
+ const pick = (mults) => {
79
+ const out = [];
80
+ for (let e = lo; e <= hi; e++) for (const m of mults) {
81
+ const v = round(m * 10 ** e);
82
+ if (v >= min * (1 - 1e-9) && v <= max * (1 + 1e-9)) out.push(v);
83
+ }
84
+ return out;
85
+ };
86
+ const dense = pick([1, 2, 5]);
87
+ return dense.length <= count * 2 ? dense : pick([1]);
88
+ }
64
89
 
65
90
  // src/path.ts
66
91
  var f = (n) => (Math.round(n * 100) / 100).toString();
@@ -118,17 +143,17 @@ function segments(values) {
118
143
  });
119
144
  return out;
120
145
  }
121
- function barPath(x, width, baseY, valueY, radius = 0) {
146
+ function barPath(x, width, baseY, valueY, radius = 0, roundBase = false) {
122
147
  const top = Math.min(baseY, valueY);
123
- const h = Math.abs(valueY - baseY);
124
- const r = Math.max(0, Math.min(radius, width / 2, h));
148
+ const bottom = Math.max(baseY, valueY);
149
+ const h = bottom - top;
125
150
  const x1 = x + width;
126
- if (r === 0) return `M${f(x)},${f(top)}H${f(x1)}V${f(top + h)}H${f(x)}Z`;
127
- if (valueY <= baseY) {
128
- return `M${f(x)},${f(baseY)}V${f(top + r)}Q${f(x)},${f(top)} ${f(x + r)},${f(top)}H${f(x1 - r)}Q${f(x1)},${f(top)} ${f(x1)},${f(top + r)}V${f(baseY)}Z`;
129
- }
130
- const bottom = top + h;
131
- return `M${f(x)},${f(baseY)}V${f(bottom - r)}Q${f(x)},${f(bottom)} ${f(x + r)},${f(bottom)}H${f(x1 - r)}Q${f(x1)},${f(bottom)} ${f(x1)},${f(bottom - r)}V${f(baseY)}Z`;
151
+ const r = Math.max(0, Math.min(radius, width / 2, roundBase ? h / 2 : h));
152
+ if (r === 0) return `M${f(x)},${f(top)}H${f(x1)}V${f(bottom)}H${f(x)}Z`;
153
+ const up = valueY <= baseY;
154
+ const rTop = up || roundBase ? r : 0;
155
+ const rBottom = !up || roundBase ? r : 0;
156
+ return `M${f(x)},${f(top + rTop)}` + (rTop ? `Q${f(x)},${f(top)} ${f(x + rTop)},${f(top)}` : "") + `H${f(x1 - rTop)}` + (rTop ? `Q${f(x1)},${f(top)} ${f(x1)},${f(top + rTop)}` : "") + `V${f(bottom - rBottom)}` + (rBottom ? `Q${f(x1)},${f(bottom)} ${f(x1 - rBottom)},${f(bottom)}` : "") + `H${f(x + rBottom)}` + (rBottom ? `Q${f(x)},${f(bottom)} ${f(x)},${f(bottom - rBottom)}` : "") + "Z";
132
157
  }
133
158
  function monotoneSegments(p) {
134
159
  const n = p.length;
@@ -260,6 +285,8 @@ export {
260
285
  extent,
261
286
  linePath,
262
287
  linearScale,
288
+ logScale,
289
+ logTicks,
263
290
  maxLength,
264
291
  nearestIndex,
265
292
  niceDomain,
package/dist/path.d.ts CHANGED
@@ -20,7 +20,7 @@ export declare function segments<T>(values: readonly (T | null | undefined)[]):
20
20
  values: T[];
21
21
  }[];
22
22
  /**
23
- * Bar from `baseY` to `valueY` (pixels), `radius` rounding only the end away from the
24
- * baseline — so negative bars round at the bottom.
23
+ * Bar from `baseY` to `valueY` (pixels). `radius` rounds the end away from the baseline
24
+ * (negative bars round at the bottom); `roundBase` rounds the baseline end too.
25
25
  */
26
- export declare function barPath(x: number, width: number, baseY: number, valueY: number, radius?: number): string;
26
+ export declare function barPath(x: number, width: number, baseY: number, valueY: number, radius?: number, roundBase?: boolean): string;
package/dist/scale.d.ts CHANGED
@@ -7,6 +7,20 @@ export interface LinearScale {
7
7
  invert(pixel: number): number;
8
8
  }
9
9
  export declare function linearScale(domain: readonly [number, number], range: readonly [number, number]): LinearScale;
10
+ /**
11
+ * A log10 scale for values spanning orders of magnitude (long-term price charts).
12
+ * Domain values must be positive; non-positive inputs clamp to the domain minimum.
13
+ */
14
+ export interface LogScale {
15
+ readonly kind: 'log';
16
+ readonly domain: readonly [number, number];
17
+ readonly range: readonly [number, number];
18
+ (value: number): number;
19
+ invert(pixel: number): number;
20
+ }
21
+ export declare function logScale(domain: readonly [number, number], range: readonly [number, number]): LogScale;
22
+ /** Either value scale; both map a value to a pixel and back. */
23
+ export type ValueScale = LinearScale | LogScale;
10
24
  /** A categorical scale: `count` equal bands across the range, each with inner padding. */
11
25
  export interface BandScale {
12
26
  readonly kind: 'band';
package/dist/ticks.d.ts CHANGED
@@ -9,3 +9,8 @@ export declare function ticks(min: number, max: number, count?: number): number[
9
9
  * always keeping the first one.
10
10
  */
11
11
  export declare function sampleIndices(count: number, max: number): number[];
12
+ /**
13
+ * Ticks for a log scale: 1, 2, 5 × 10ⁿ inside [min, max] (min > 0). Falls back to powers
14
+ * of ten alone when that would be too many, and to linear ticks for a narrow range.
15
+ */
16
+ export declare function logTicks(min: number, max: number, count?: number): number[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnest/charts-viz",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Platform-neutral chart math (scales, ticks, paths, scrubbing) shared by Funnest Charts web and React Native components",
5
5
  "keywords": [
6
6
  "chart",