cx 26.7.6 → 26.8.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/charts.js CHANGED
@@ -2999,16 +2999,11 @@ class LineGraph extends Widget {
2999
2999
  stack: undefined,
3000
3000
  stacked: undefined,
3001
3001
  smooth: undefined,
3002
- smoothingRatio: undefined,
3003
3002
  });
3004
3003
  }
3005
3004
  prepareData(context, instance) {
3006
3005
  let { data } = instance;
3007
3006
  if (data.name && !data.colorName) data.colorName = data.name;
3008
- if (data.smooth && data.smoothingRatio != null) {
3009
- if (data.smoothingRatio < 0) data.smoothingRatio = 0;
3010
- if (data.smoothingRatio > 0.4) data.smoothingRatio = 0.4;
3011
- }
3012
3007
  super.prepareData(context, instance);
3013
3008
  }
3014
3009
  explore(context, instance) {
@@ -3119,18 +3114,16 @@ class LineGraph extends Widget {
3119
3114
  ["color-" + data.colorIndex]: data.colorIndex != null,
3120
3115
  };
3121
3116
  let line, area;
3122
- const r = data.smoothingRatio;
3123
3117
  let linePath = "";
3124
3118
  if (data.line) {
3125
3119
  lineSpans.forEach((span) => {
3126
- span.forEach((p, i) => {
3127
- linePath +=
3128
- i == 0
3129
- ? `M ${p.x} ${p.y}`
3130
- : !data.smooth || span.length < 2
3131
- ? `L ${p.x} ${p.y}`
3132
- : this.getCurvedPathSegment(p, span, i - 1, i - 2, i - 1, i + 1, r);
3133
- });
3120
+ if (span.length == 0) return;
3121
+ linePath += `M ${span[0].x} ${span[0].y}`;
3122
+ if (data.smooth && span.length >= 2) linePath += this.getMonotoneSpanPath(span, "y");
3123
+ else
3124
+ span.forEach((p, i) => {
3125
+ if (i > 0) linePath += `L ${p.x} ${p.y}`;
3126
+ });
3134
3127
  });
3135
3128
  line = jsx("path", {
3136
3129
  className: this.CSS.element(this.baseClass, "line", stateMods),
@@ -3141,31 +3134,20 @@ class LineGraph extends Widget {
3141
3134
  if (data.area) {
3142
3135
  let areaPath = "";
3143
3136
  lineSpans.forEach((span) => {
3144
- let closePath = "";
3145
- span.forEach((p, i) => {
3146
- let segment = "";
3147
- if (i == 0) {
3148
- segment = `M ${p.x} ${p.y}`;
3149
- // closing point
3150
- closePath =
3151
- !data.smooth || span.length < 2
3152
- ? `L ${p.x} ${p.y0}`
3153
- : this.getCurvedPathSegment(p, span, i + 1, i + 2, i + 1, i - 1, r, "y0");
3154
- } else {
3155
- if (!data.smooth) {
3156
- segment = `L ${p.x} ${p.y}`;
3157
- closePath = `L ${p.x} ${p.y0}` + closePath;
3158
- } else {
3159
- segment = this.getCurvedPathSegment(p, span, i - 1, i - 2, i - 1, i + 1, r, "y");
3160
- // closing point
3161
- if (i < span.length - 1)
3162
- closePath = this.getCurvedPathSegment(p, span, i + 1, i + 2, i + 1, i - 1, r, "y0") + closePath;
3163
- }
3164
- }
3165
- areaPath += segment;
3166
- });
3167
- areaPath += `L ${span[span.length - 1].x} ${span[span.length - 1].y0}`;
3168
- areaPath += closePath;
3137
+ if (span.length == 0) return;
3138
+ let last = span[span.length - 1];
3139
+ areaPath += `M ${span[0].x} ${span[0].y}`;
3140
+ if (data.smooth && span.length >= 2) {
3141
+ areaPath += this.getMonotoneSpanPath(span, "y");
3142
+ areaPath += `L ${last.x} ${last.y0}`;
3143
+ areaPath += this.getMonotoneSpanPath(span, "y0", true);
3144
+ } else {
3145
+ span.forEach((p, i) => {
3146
+ if (i > 0) areaPath += `L ${p.x} ${p.y}`;
3147
+ });
3148
+ areaPath += `L ${last.x} ${last.y0}`;
3149
+ for (let i = span.length - 2; i >= 0; i--) areaPath += `L ${span[i].x} ${span[i].y0}`;
3150
+ }
3169
3151
  areaPath += "Z";
3170
3152
  });
3171
3153
  area = jsx("path", {
@@ -3183,45 +3165,54 @@ class LineGraph extends Widget {
3183
3165
  key,
3184
3166
  );
3185
3167
  }
3186
- getCurvedPathSegment(p, points, i1, i2, j1, j2, r, yField = "y") {
3187
- const [sx, sy] = this.getControlPoint({
3188
- cp: points[i1],
3189
- pp: points[i2],
3190
- r,
3191
- np: p,
3192
- yField,
3193
- });
3194
- const [ex, ey] = this.getControlPoint({
3195
- cp: p,
3196
- pp: points[j1],
3197
- np: points[j2],
3198
- r,
3199
- reverse: true,
3200
- yField,
3201
- });
3202
- return `C ${sx} ${sy}, ${ex} ${ey}, ${p.x} ${p[yField]}`;
3203
- }
3204
- getControlPoint({ cp, pp, np, r, reverse, yField = "y" }) {
3205
- // When 'current' is the first or last point of the array 'previous' or 'next' don't exist. Replace with 'current'.
3206
- const p = pp || cp;
3207
- const n = np || cp;
3208
- // Properties of the opposed-line
3209
- let { angle, length } = this.getLineInfo(p.x, p[yField], n.x, n[yField]);
3210
- // If it is end-control-point, add PI to the angle to go backward
3211
- angle = angle + (reverse ? Math.PI : 0);
3212
- length = length * r;
3213
- // The control point position is relative to the current point
3214
- const x = cp.x + Math.cos(angle) * length;
3215
- const y = cp[yField] + Math.sin(angle) * length;
3216
- return [x, y];
3217
- }
3218
- getLineInfo(p1x, p1y, p2x, p2y) {
3219
- const lengthX = p2x - p1x;
3220
- const lengthY = p2y - p1y;
3221
- return {
3222
- length: Math.sqrt(Math.pow(lengthX, 2) + Math.pow(lengthY, 2)),
3223
- angle: Math.atan2(lengthY, lengthX),
3168
+ // Fritsch-Carlson monotone cubic interpolation. Tangents are limited so the
3169
+ // curve between two points never leaves their vertical range (no overshoot).
3170
+ getMonotoneTangents(span, yField) {
3171
+ const n = span.length;
3172
+ const m = new Array(n).fill(0);
3173
+ if (n < 2) return m;
3174
+ const secant = (i) => {
3175
+ const h = span[i + 1].x - span[i].x;
3176
+ return h != 0 ? (span[i + 1][yField] - span[i][yField]) / h : 0;
3224
3177
  };
3178
+ if (n == 2) {
3179
+ m[0] = m[1] = secant(0);
3180
+ return m;
3181
+ }
3182
+ for (let i = 1; i < n - 1; i++) {
3183
+ const h0 = span[i].x - span[i - 1].x;
3184
+ const h1 = span[i + 1].x - span[i].x;
3185
+ const s0 = secant(i - 1);
3186
+ const s1 = secant(i);
3187
+ const p = (s0 * h1 + s1 * h0) / (h0 + h1);
3188
+ m[i] = (Math.sign(s0) + Math.sign(s1)) * Math.min(Math.abs(s0), Math.abs(s1), 0.5 * Math.abs(p)) || 0;
3189
+ }
3190
+ const hFirst = span[1].x - span[0].x;
3191
+ m[0] = hFirst != 0 ? (3 * secant(0) - m[1]) / 2 : m[1];
3192
+ const hLast = span[n - 1].x - span[n - 2].x;
3193
+ m[n - 1] = hLast != 0 ? (3 * secant(n - 2) - m[n - 2]) / 2 : m[n - 2];
3194
+ return m;
3195
+ }
3196
+ // Emits cubic bezier segments for the whole span using monotone tangents.
3197
+ // Assumes the path cursor is at the first (or last, when reversed) span point.
3198
+ getMonotoneSpanPath(span, yField, reverse) {
3199
+ const m = this.getMonotoneTangents(span, yField);
3200
+ let path = "";
3201
+ if (!reverse)
3202
+ for (let i = 1; i < span.length; i++) {
3203
+ const p0 = span[i - 1];
3204
+ const p1 = span[i];
3205
+ const dx = (p1.x - p0.x) / 3;
3206
+ path += `C ${p0.x + dx} ${p0[yField] + dx * m[i - 1]}, ${p1.x - dx} ${p1[yField] - dx * m[i]}, ${p1.x} ${p1[yField]}`;
3207
+ }
3208
+ else
3209
+ for (let i = span.length - 1; i > 0; i--) {
3210
+ const p0 = span[i - 1];
3211
+ const p1 = span[i];
3212
+ const dx = (p1.x - p0.x) / 3;
3213
+ path += `C ${p1.x - dx} ${p1[yField] - dx * m[i]}, ${p0.x + dx} ${p0[yField] + dx * m[i - 1]}, ${p0.x} ${p0[yField]}`;
3214
+ }
3215
+ return path;
3225
3216
  }
3226
3217
  }
3227
3218
  LineGraph.prototype.xAxis = "x";
@@ -3240,7 +3231,6 @@ LineGraph.prototype.legendShape = "rect";
3240
3231
  LineGraph.prototype.stack = "stack";
3241
3232
  LineGraph.prototype.hiddenBase = false;
3242
3233
  LineGraph.prototype.smooth = false;
3243
- LineGraph.prototype.smoothingRatio = 0.05;
3244
3234
  LineGraph.prototype.styled = true;
3245
3235
  Widget.alias("line-graph", LineGraph);
3246
3236
 
@@ -3904,92 +3894,6 @@ class Stack {
3904
3894
  }
3905
3895
  }
3906
3896
 
3907
- class NumericAxis extends Axis {
3908
- constructor(config) {
3909
- super(config);
3910
- }
3911
- init() {
3912
- if (this.deadZone) {
3913
- this.lowerDeadZone = this.deadZone;
3914
- this.upperDeadZone = this.deadZone;
3915
- }
3916
- super.init();
3917
- }
3918
- declareData(...args) {
3919
- super.declareData(
3920
- {
3921
- min: undefined,
3922
- max: undefined,
3923
- normalized: undefined,
3924
- inverted: undefined,
3925
- labelDivisor: undefined,
3926
- format: undefined,
3927
- lowerDeadZone: undefined,
3928
- upperDeadZone: undefined,
3929
- },
3930
- ...args,
3931
- );
3932
- }
3933
- initInstance(context, instance) {
3934
- instance.calculator = new NumericScale();
3935
- }
3936
- explore(context, instance) {
3937
- super.explore(context, instance);
3938
- let { min, max, normalized, inverted, lowerDeadZone, upperDeadZone } = instance.data;
3939
- instance.calculator.reset(
3940
- min,
3941
- max,
3942
- this.snapToTicks,
3943
- this.tickDivisions,
3944
- this.minTickDistance,
3945
- this.minTickStep,
3946
- this.minLabelDistance,
3947
- this.minLabelTickSize,
3948
- normalized,
3949
- inverted,
3950
- lowerDeadZone,
3951
- upperDeadZone,
3952
- );
3953
- }
3954
- render(context, instance, key) {
3955
- let { data } = instance;
3956
- if (!data.bounds.valid()) return null;
3957
- let baseFormatter = Format.parse(data.format);
3958
- let formatter = data.labelDivisor != 1 ? (v) => baseFormatter(v / data.labelDivisor) : baseFormatter;
3959
- return jsx(
3960
- "g",
3961
- {
3962
- className: data.classNames,
3963
- style: data.style,
3964
- children: this.renderTicksAndLabels(context, instance, formatter, this.minLabelDistance),
3965
- },
3966
- key,
3967
- );
3968
- }
3969
- static XY() {
3970
- return {
3971
- x: {
3972
- type: NumericAxis,
3973
- },
3974
- y: {
3975
- type: NumericAxis,
3976
- vertical: true,
3977
- },
3978
- };
3979
- }
3980
- }
3981
- NumericAxis.prototype.baseClass = "numericaxis";
3982
- NumericAxis.prototype.tickDivisions = [
3983
- [1, 2, 10, 20, 100],
3984
- [1, 5, 10, 20, 100],
3985
- ];
3986
- NumericAxis.prototype.snapToTicks = 1;
3987
- NumericAxis.prototype.normalized = false;
3988
- NumericAxis.prototype.format = "n";
3989
- NumericAxis.prototype.labelDivisor = 1;
3990
- NumericAxis.prototype.minLabelTickSize = 0;
3991
- NumericAxis.prototype.minTickStep = 0;
3992
- Axis.alias("numeric", NumericAxis);
3993
3897
  class NumericScale {
3994
3898
  min;
3995
3899
  max;
@@ -4059,7 +3963,7 @@ class NumericScale {
4059
3963
  }
4060
3964
  trackValue(v, offset = 0, constrain = false) {
4061
3965
  let value = (v - this.origin) / this.scale.factor - offset + this.scale.min - this.scale.minPadding;
4062
- if (constrain) value = this.constrainValue(v);
3966
+ if (constrain) value = this.constrainValue(value);
4063
3967
  return value;
4064
3968
  }
4065
3969
  hash() {
@@ -4237,6 +4141,93 @@ class NumericScale {
4237
4141
  }
4238
4142
  }
4239
4143
 
4144
+ class NumericAxis extends Axis {
4145
+ constructor(config) {
4146
+ super(config);
4147
+ }
4148
+ init() {
4149
+ if (this.deadZone) {
4150
+ this.lowerDeadZone = this.deadZone;
4151
+ this.upperDeadZone = this.deadZone;
4152
+ }
4153
+ super.init();
4154
+ }
4155
+ declareData(...args) {
4156
+ super.declareData(
4157
+ {
4158
+ min: undefined,
4159
+ max: undefined,
4160
+ normalized: undefined,
4161
+ inverted: undefined,
4162
+ labelDivisor: undefined,
4163
+ format: undefined,
4164
+ lowerDeadZone: undefined,
4165
+ upperDeadZone: undefined,
4166
+ },
4167
+ ...args,
4168
+ );
4169
+ }
4170
+ initInstance(context, instance) {
4171
+ instance.calculator = new NumericScale();
4172
+ }
4173
+ explore(context, instance) {
4174
+ super.explore(context, instance);
4175
+ let { min, max, normalized, inverted, lowerDeadZone, upperDeadZone } = instance.data;
4176
+ instance.calculator.reset(
4177
+ min,
4178
+ max,
4179
+ this.snapToTicks,
4180
+ this.tickDivisions,
4181
+ this.minTickDistance,
4182
+ this.minTickStep,
4183
+ this.minLabelDistance,
4184
+ this.minLabelTickSize,
4185
+ normalized,
4186
+ inverted,
4187
+ lowerDeadZone,
4188
+ upperDeadZone,
4189
+ );
4190
+ }
4191
+ render(context, instance, key) {
4192
+ let { data } = instance;
4193
+ if (!data.bounds.valid()) return null;
4194
+ let baseFormatter = Format.parse(data.format);
4195
+ let formatter = data.labelDivisor != 1 ? (v) => baseFormatter(v / data.labelDivisor) : baseFormatter;
4196
+ return jsx(
4197
+ "g",
4198
+ {
4199
+ className: data.classNames,
4200
+ style: data.style,
4201
+ children: this.renderTicksAndLabels(context, instance, formatter, this.minLabelDistance),
4202
+ },
4203
+ key,
4204
+ );
4205
+ }
4206
+ static XY() {
4207
+ return {
4208
+ x: {
4209
+ type: NumericAxis,
4210
+ },
4211
+ y: {
4212
+ type: NumericAxis,
4213
+ vertical: true,
4214
+ },
4215
+ };
4216
+ }
4217
+ }
4218
+ NumericAxis.prototype.baseClass = "numericaxis";
4219
+ NumericAxis.prototype.tickDivisions = [
4220
+ [1, 2, 10, 20, 100],
4221
+ [1, 5, 10, 20, 100],
4222
+ ];
4223
+ NumericAxis.prototype.snapToTicks = 1;
4224
+ NumericAxis.prototype.normalized = false;
4225
+ NumericAxis.prototype.format = "n";
4226
+ NumericAxis.prototype.labelDivisor = 1;
4227
+ NumericAxis.prototype.minLabelTickSize = 0;
4228
+ NumericAxis.prototype.minTickStep = 0;
4229
+ Axis.alias("numeric", NumericAxis);
4230
+
4240
4231
  class CategoryAxis extends Axis {
4241
4232
  constructor(config) {
4242
4233
  super(config);