pptx-glimpse 0.4.1 → 0.5.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.cjs +92 -10
- package/dist/index.js +92 -10
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2491,10 +2491,19 @@ function extractCategories(serList) {
|
|
|
2491
2491
|
if (!cat) continue;
|
|
2492
2492
|
const strRef = cat.strRef;
|
|
2493
2493
|
const numRef = cat.numRef;
|
|
2494
|
+
const multiLvlStrRef = cat.multiLvlStrRef;
|
|
2494
2495
|
const strCache = strRef?.strCache ?? numRef?.numCache;
|
|
2495
2496
|
if (strCache?.pt) {
|
|
2496
2497
|
return strCache.pt.slice().sort((a, b) => Number(a["@_idx"]) - Number(b["@_idx"])).map((pt) => String(pt.v ?? ""));
|
|
2497
2498
|
}
|
|
2499
|
+
const multiCache = multiLvlStrRef?.multiLvlStrCache;
|
|
2500
|
+
if (multiCache?.lvl) {
|
|
2501
|
+
const lvls = Array.isArray(multiCache.lvl) ? multiCache.lvl : [multiCache.lvl];
|
|
2502
|
+
const firstLvl = lvls[0];
|
|
2503
|
+
if (firstLvl?.pt) {
|
|
2504
|
+
return firstLvl.pt.slice().sort((a, b) => Number(a["@_idx"]) - Number(b["@_idx"])).map((pt) => String(pt.v ?? ""));
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2498
2507
|
}
|
|
2499
2508
|
return [];
|
|
2500
2509
|
}
|
|
@@ -4983,12 +4992,26 @@ function renderBarChart(chart, x, y, w, h) {
|
|
|
4983
4992
|
return "";
|
|
4984
4993
|
}
|
|
4985
4994
|
const isHorizontal = chart.barDirection === "bar";
|
|
4995
|
+
const ticks = computeNiceTicks(0, maxVal);
|
|
4996
|
+
const scaleMax = ticks[ticks.length - 1];
|
|
4986
4997
|
parts.push(
|
|
4987
4998
|
`<line x1="${round(x)}" y1="${round(y + h)}" x2="${round(x + w)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
4988
4999
|
);
|
|
4989
5000
|
parts.push(
|
|
4990
5001
|
`<line x1="${round(x)}" y1="${round(y)}" x2="${round(x)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
4991
5002
|
);
|
|
5003
|
+
if (isHorizontal) {
|
|
5004
|
+
for (const tick of ticks) {
|
|
5005
|
+
const ratio = tick / scaleMax;
|
|
5006
|
+
if (ratio < -1e-3 || ratio > 1.001) continue;
|
|
5007
|
+
const tickX = x + ratio * w;
|
|
5008
|
+
parts.push(
|
|
5009
|
+
`<text x="${round(tickX)}" y="${round(y + h + 15)}" text-anchor="middle" font-size="10" fill="#595959">${escapeXml(formatTickValue(tick))}</text>`
|
|
5010
|
+
);
|
|
5011
|
+
}
|
|
5012
|
+
} else {
|
|
5013
|
+
parts.push(renderValueAxisLabels(ticks, 0, scaleMax, x, y, h));
|
|
5014
|
+
}
|
|
4992
5015
|
if (isHorizontal) {
|
|
4993
5016
|
const groupHeight = h / catCount;
|
|
4994
5017
|
const barHeight = groupHeight * 0.7 / series.length;
|
|
@@ -5004,7 +5027,7 @@ function renderBarChart(chart, x, y, w, h) {
|
|
|
5004
5027
|
const color = series[s].color;
|
|
5005
5028
|
for (let c = 0; c < series[s].values.length; c++) {
|
|
5006
5029
|
const val = series[s].values[c];
|
|
5007
|
-
const barW = val /
|
|
5030
|
+
const barW = val / scaleMax * w;
|
|
5008
5031
|
const barX = x;
|
|
5009
5032
|
const barY = y + c * groupHeight + groupPadding + s * barHeight;
|
|
5010
5033
|
parts.push(
|
|
@@ -5027,7 +5050,7 @@ function renderBarChart(chart, x, y, w, h) {
|
|
|
5027
5050
|
const color = series[s].color;
|
|
5028
5051
|
for (let c = 0; c < series[s].values.length; c++) {
|
|
5029
5052
|
const val = series[s].values[c];
|
|
5030
|
-
const barH = val /
|
|
5053
|
+
const barH = val / scaleMax * h;
|
|
5031
5054
|
const barX = x + c * groupWidth + groupPadding + s * barWidth;
|
|
5032
5055
|
const barY = y + h - barH;
|
|
5033
5056
|
parts.push(
|
|
@@ -5055,12 +5078,15 @@ function renderLineChart(chart, x, y, w, h) {
|
|
|
5055
5078
|
debug("chart.line", "category count is 0");
|
|
5056
5079
|
return "";
|
|
5057
5080
|
}
|
|
5081
|
+
const ticks = computeNiceTicks(0, maxVal);
|
|
5082
|
+
const scaleMax = ticks[ticks.length - 1];
|
|
5058
5083
|
parts.push(
|
|
5059
5084
|
`<line x1="${round(x)}" y1="${round(y + h)}" x2="${round(x + w)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5060
5085
|
);
|
|
5061
5086
|
parts.push(
|
|
5062
5087
|
`<line x1="${round(x)}" y1="${round(y)}" x2="${round(x)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5063
5088
|
);
|
|
5089
|
+
parts.push(renderValueAxisLabels(ticks, 0, scaleMax, x, y, h));
|
|
5064
5090
|
for (let c = 0; c < catCount; c++) {
|
|
5065
5091
|
const label = categories[c] ?? "";
|
|
5066
5092
|
const divisor = catCount > 1 ? catCount - 1 : 1;
|
|
@@ -5074,7 +5100,7 @@ function renderLineChart(chart, x, y, w, h) {
|
|
|
5074
5100
|
const divisor = catCount > 1 ? catCount - 1 : 1;
|
|
5075
5101
|
const points = series[s].values.map((val, i) => {
|
|
5076
5102
|
const px = round(x + i / divisor * w);
|
|
5077
|
-
const py = round(y + h - val /
|
|
5103
|
+
const py = round(y + h - val / scaleMax * h);
|
|
5078
5104
|
return `${px},${py}`;
|
|
5079
5105
|
});
|
|
5080
5106
|
parts.push(
|
|
@@ -5104,12 +5130,15 @@ function renderAreaChart(chart, x, y, w, h) {
|
|
|
5104
5130
|
debug("chart.area", "category count is 0");
|
|
5105
5131
|
return "";
|
|
5106
5132
|
}
|
|
5133
|
+
const ticks = computeNiceTicks(0, maxVal);
|
|
5134
|
+
const scaleMax = ticks[ticks.length - 1];
|
|
5107
5135
|
parts.push(
|
|
5108
5136
|
`<line x1="${round(x)}" y1="${round(y + h)}" x2="${round(x + w)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5109
5137
|
);
|
|
5110
5138
|
parts.push(
|
|
5111
5139
|
`<line x1="${round(x)}" y1="${round(y)}" x2="${round(x)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5112
5140
|
);
|
|
5141
|
+
parts.push(renderValueAxisLabels(ticks, 0, scaleMax, x, y, h));
|
|
5113
5142
|
for (let c = 0; c < catCount; c++) {
|
|
5114
5143
|
const label = categories[c] ?? "";
|
|
5115
5144
|
const divisor = catCount > 1 ? catCount - 1 : 1;
|
|
@@ -5124,7 +5153,7 @@ function renderAreaChart(chart, x, y, w, h) {
|
|
|
5124
5153
|
const divisor = catCount > 1 ? catCount - 1 : 1;
|
|
5125
5154
|
const dataPoints = series[s].values.map((val, i) => {
|
|
5126
5155
|
const px = round(x + i / divisor * w);
|
|
5127
|
-
const py = round(y + h - val /
|
|
5156
|
+
const py = round(y + h - val / scaleMax * h);
|
|
5128
5157
|
return { px, py };
|
|
5129
5158
|
});
|
|
5130
5159
|
const topPoints = dataPoints.map((p) => `${p.px},${p.py}`).join(" ");
|
|
@@ -5238,12 +5267,15 @@ function renderScatterChart(chart, x, y, w, h) {
|
|
|
5238
5267
|
}
|
|
5239
5268
|
if (maxX === 0) maxX = 1;
|
|
5240
5269
|
if (maxY === 0) maxY = 1;
|
|
5270
|
+
const yTicks = computeNiceTicks(0, maxY);
|
|
5271
|
+
const scaleMaxY = yTicks[yTicks.length - 1];
|
|
5241
5272
|
parts.push(
|
|
5242
5273
|
`<line x1="${round(x)}" y1="${round(y + h)}" x2="${round(x + w)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5243
5274
|
);
|
|
5244
5275
|
parts.push(
|
|
5245
5276
|
`<line x1="${round(x)}" y1="${round(y)}" x2="${round(x)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5246
5277
|
);
|
|
5278
|
+
parts.push(renderValueAxisLabels(yTicks, 0, scaleMaxY, x, y, h));
|
|
5247
5279
|
for (let s = 0; s < series.length; s++) {
|
|
5248
5280
|
const color = series[s].color;
|
|
5249
5281
|
const xVals = series[s].xValues ?? [];
|
|
@@ -5251,7 +5283,7 @@ function renderScatterChart(chart, x, y, w, h) {
|
|
|
5251
5283
|
const xVal = xVals[i] ?? i;
|
|
5252
5284
|
const yVal = series[s].values[i];
|
|
5253
5285
|
const px = x + xVal / maxX * w;
|
|
5254
|
-
const py = y + h - yVal /
|
|
5286
|
+
const py = y + h - yVal / scaleMaxY * h;
|
|
5255
5287
|
parts.push(`<circle cx="${round(px)}" cy="${round(py)}" r="4" ${fillAttr(color)}/>`);
|
|
5256
5288
|
}
|
|
5257
5289
|
}
|
|
@@ -5278,12 +5310,15 @@ function renderBubbleChart(chart, x, y, w, h) {
|
|
|
5278
5310
|
if (maxY === 0) maxY = 1;
|
|
5279
5311
|
if (maxBubble === 0) maxBubble = 1;
|
|
5280
5312
|
const maxRadius = Math.min(w, h) * 0.08;
|
|
5313
|
+
const yTicks = computeNiceTicks(0, maxY);
|
|
5314
|
+
const scaleMaxY = yTicks[yTicks.length - 1];
|
|
5281
5315
|
parts.push(
|
|
5282
5316
|
`<line x1="${round(x)}" y1="${round(y + h)}" x2="${round(x + w)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5283
5317
|
);
|
|
5284
5318
|
parts.push(
|
|
5285
5319
|
`<line x1="${round(x)}" y1="${round(y)}" x2="${round(x)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5286
5320
|
);
|
|
5321
|
+
parts.push(renderValueAxisLabels(yTicks, 0, scaleMaxY, x, y, h));
|
|
5287
5322
|
for (let s = 0; s < series.length; s++) {
|
|
5288
5323
|
const color = series[s].color;
|
|
5289
5324
|
const xVals = series[s].xValues ?? [];
|
|
@@ -5293,7 +5328,7 @@ function renderBubbleChart(chart, x, y, w, h) {
|
|
|
5293
5328
|
const yVal = series[s].values[i];
|
|
5294
5329
|
const size = sizes[i] ?? 1;
|
|
5295
5330
|
const px = x + xVal / maxX * w;
|
|
5296
|
-
const py = y + h - yVal /
|
|
5331
|
+
const py = y + h - yVal / scaleMaxY * h;
|
|
5297
5332
|
const r = Math.max(2, Math.sqrt(size / maxBubble) * maxRadius);
|
|
5298
5333
|
parts.push(
|
|
5299
5334
|
`<circle cx="${round(px)}" cy="${round(py)}" r="${round(r)}" ${fillAttr(color)} fill-opacity="0.6"/>`
|
|
@@ -5406,12 +5441,16 @@ function renderStockChart(chart, x, y, w, h) {
|
|
|
5406
5441
|
debug("chart.stock", "max equals min value");
|
|
5407
5442
|
return "";
|
|
5408
5443
|
}
|
|
5444
|
+
const ticks = computeNiceTicks(minVal, maxVal);
|
|
5445
|
+
const scaleMin = ticks[0];
|
|
5446
|
+
const scaleMax = ticks[ticks.length - 1];
|
|
5409
5447
|
parts.push(
|
|
5410
5448
|
`<line x1="${round(x)}" y1="${round(y + h)}" x2="${round(x + w)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5411
5449
|
);
|
|
5412
5450
|
parts.push(
|
|
5413
5451
|
`<line x1="${round(x)}" y1="${round(y)}" x2="${round(x)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5414
5452
|
);
|
|
5453
|
+
parts.push(renderValueAxisLabels(ticks, scaleMin, scaleMax, x, y, h));
|
|
5415
5454
|
for (let c = 0; c < catCount; c++) {
|
|
5416
5455
|
const label = categories[c] ?? "";
|
|
5417
5456
|
const labelX = x + (c + 0.5) * (w / catCount);
|
|
@@ -5419,15 +5458,15 @@ function renderStockChart(chart, x, y, w, h) {
|
|
|
5419
5458
|
`<text x="${round(labelX)}" y="${round(y + h + 15)}" text-anchor="middle" font-size="10" fill="#595959">${escapeXml(label)}</text>`
|
|
5420
5459
|
);
|
|
5421
5460
|
}
|
|
5422
|
-
const range =
|
|
5461
|
+
const range = scaleMax - scaleMin;
|
|
5423
5462
|
for (let c = 0; c < catCount; c++) {
|
|
5424
5463
|
const cx = x + (c + 0.5) * (w / catCount);
|
|
5425
5464
|
const highVal = highSeries.values[c] ?? 0;
|
|
5426
5465
|
const lowVal = lowSeries.values[c] ?? 0;
|
|
5427
5466
|
const closeVal = closeSeries.values[c] ?? 0;
|
|
5428
|
-
const highY = y + h - (highVal -
|
|
5429
|
-
const lowY = y + h - (lowVal -
|
|
5430
|
-
const closeY = y + h - (closeVal -
|
|
5467
|
+
const highY = y + h - (highVal - scaleMin) / range * h;
|
|
5468
|
+
const lowY = y + h - (lowVal - scaleMin) / range * h;
|
|
5469
|
+
const closeY = y + h - (closeVal - scaleMin) / range * h;
|
|
5431
5470
|
parts.push(
|
|
5432
5471
|
`<line x1="${round(cx)}" y1="${round(highY)}" x2="${round(cx)}" y2="${round(lowY)}" stroke="#404040" stroke-width="2"/>`
|
|
5433
5472
|
);
|
|
@@ -5660,6 +5699,49 @@ function getPieSliceColor(index, chart) {
|
|
|
5660
5699
|
}
|
|
5661
5700
|
return DEFAULT_SERIES_COLORS[index % DEFAULT_SERIES_COLORS.length];
|
|
5662
5701
|
}
|
|
5702
|
+
function computeNiceTicks(minVal, maxVal, targetCount = 5) {
|
|
5703
|
+
const range = maxVal - minVal;
|
|
5704
|
+
if (range === 0) return [minVal];
|
|
5705
|
+
const roughStep = range / targetCount;
|
|
5706
|
+
const magnitude = Math.pow(10, Math.floor(Math.log10(roughStep)));
|
|
5707
|
+
const residual = roughStep / magnitude;
|
|
5708
|
+
let niceStep;
|
|
5709
|
+
if (residual <= 1.5) niceStep = magnitude;
|
|
5710
|
+
else if (residual <= 3) niceStep = 2 * magnitude;
|
|
5711
|
+
else if (residual <= 7) niceStep = 5 * magnitude;
|
|
5712
|
+
else niceStep = 10 * magnitude;
|
|
5713
|
+
const niceMin = Math.floor(minVal / niceStep) * niceStep;
|
|
5714
|
+
const niceMax = Math.ceil(maxVal / niceStep) * niceStep;
|
|
5715
|
+
const ticks = [];
|
|
5716
|
+
for (let v = niceMin; v <= niceMax + niceStep * 0.5; v += niceStep) {
|
|
5717
|
+
ticks.push(Math.round(v * 1e10) / 1e10);
|
|
5718
|
+
}
|
|
5719
|
+
return ticks;
|
|
5720
|
+
}
|
|
5721
|
+
function renderValueAxisLabels(ticks, scaleMin, scaleMax, x, y, h) {
|
|
5722
|
+
const parts = [];
|
|
5723
|
+
const range = scaleMax - scaleMin;
|
|
5724
|
+
if (range === 0) return "";
|
|
5725
|
+
for (const tick of ticks) {
|
|
5726
|
+
const ratio = (tick - scaleMin) / range;
|
|
5727
|
+
if (ratio < -1e-3 || ratio > 1.001) continue;
|
|
5728
|
+
const tickY = y + h - ratio * h;
|
|
5729
|
+
parts.push(
|
|
5730
|
+
`<text x="${round(x - 5)}" y="${round(tickY + 4)}" text-anchor="end" font-size="10" fill="#595959">${escapeXml(formatTickValue(tick))}</text>`
|
|
5731
|
+
);
|
|
5732
|
+
parts.push(
|
|
5733
|
+
`<line x1="${round(x - 3)}" y1="${round(tickY)}" x2="${round(x)}" y2="${round(tickY)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5734
|
+
);
|
|
5735
|
+
}
|
|
5736
|
+
return parts.join("");
|
|
5737
|
+
}
|
|
5738
|
+
function formatTickValue(value) {
|
|
5739
|
+
if (Math.abs(value) >= 1e9) return `${round(value / 1e9)}B`;
|
|
5740
|
+
if (Math.abs(value) >= 1e6) return `${round(value / 1e6)}M`;
|
|
5741
|
+
if (Math.abs(value) >= 1e4) return `${round(value / 1e3)}K`;
|
|
5742
|
+
if (Number.isInteger(value)) return String(value);
|
|
5743
|
+
return String(round(value));
|
|
5744
|
+
}
|
|
5663
5745
|
function getMaxValue(series) {
|
|
5664
5746
|
let max = 0;
|
|
5665
5747
|
for (const s of series) {
|
package/dist/index.js
CHANGED
|
@@ -2446,10 +2446,19 @@ function extractCategories(serList) {
|
|
|
2446
2446
|
if (!cat) continue;
|
|
2447
2447
|
const strRef = cat.strRef;
|
|
2448
2448
|
const numRef = cat.numRef;
|
|
2449
|
+
const multiLvlStrRef = cat.multiLvlStrRef;
|
|
2449
2450
|
const strCache = strRef?.strCache ?? numRef?.numCache;
|
|
2450
2451
|
if (strCache?.pt) {
|
|
2451
2452
|
return strCache.pt.slice().sort((a, b) => Number(a["@_idx"]) - Number(b["@_idx"])).map((pt) => String(pt.v ?? ""));
|
|
2452
2453
|
}
|
|
2454
|
+
const multiCache = multiLvlStrRef?.multiLvlStrCache;
|
|
2455
|
+
if (multiCache?.lvl) {
|
|
2456
|
+
const lvls = Array.isArray(multiCache.lvl) ? multiCache.lvl : [multiCache.lvl];
|
|
2457
|
+
const firstLvl = lvls[0];
|
|
2458
|
+
if (firstLvl?.pt) {
|
|
2459
|
+
return firstLvl.pt.slice().sort((a, b) => Number(a["@_idx"]) - Number(b["@_idx"])).map((pt) => String(pt.v ?? ""));
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2453
2462
|
}
|
|
2454
2463
|
return [];
|
|
2455
2464
|
}
|
|
@@ -4938,12 +4947,26 @@ function renderBarChart(chart, x, y, w, h) {
|
|
|
4938
4947
|
return "";
|
|
4939
4948
|
}
|
|
4940
4949
|
const isHorizontal = chart.barDirection === "bar";
|
|
4950
|
+
const ticks = computeNiceTicks(0, maxVal);
|
|
4951
|
+
const scaleMax = ticks[ticks.length - 1];
|
|
4941
4952
|
parts.push(
|
|
4942
4953
|
`<line x1="${round(x)}" y1="${round(y + h)}" x2="${round(x + w)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
4943
4954
|
);
|
|
4944
4955
|
parts.push(
|
|
4945
4956
|
`<line x1="${round(x)}" y1="${round(y)}" x2="${round(x)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
4946
4957
|
);
|
|
4958
|
+
if (isHorizontal) {
|
|
4959
|
+
for (const tick of ticks) {
|
|
4960
|
+
const ratio = tick / scaleMax;
|
|
4961
|
+
if (ratio < -1e-3 || ratio > 1.001) continue;
|
|
4962
|
+
const tickX = x + ratio * w;
|
|
4963
|
+
parts.push(
|
|
4964
|
+
`<text x="${round(tickX)}" y="${round(y + h + 15)}" text-anchor="middle" font-size="10" fill="#595959">${escapeXml(formatTickValue(tick))}</text>`
|
|
4965
|
+
);
|
|
4966
|
+
}
|
|
4967
|
+
} else {
|
|
4968
|
+
parts.push(renderValueAxisLabels(ticks, 0, scaleMax, x, y, h));
|
|
4969
|
+
}
|
|
4947
4970
|
if (isHorizontal) {
|
|
4948
4971
|
const groupHeight = h / catCount;
|
|
4949
4972
|
const barHeight = groupHeight * 0.7 / series.length;
|
|
@@ -4959,7 +4982,7 @@ function renderBarChart(chart, x, y, w, h) {
|
|
|
4959
4982
|
const color = series[s].color;
|
|
4960
4983
|
for (let c = 0; c < series[s].values.length; c++) {
|
|
4961
4984
|
const val = series[s].values[c];
|
|
4962
|
-
const barW = val /
|
|
4985
|
+
const barW = val / scaleMax * w;
|
|
4963
4986
|
const barX = x;
|
|
4964
4987
|
const barY = y + c * groupHeight + groupPadding + s * barHeight;
|
|
4965
4988
|
parts.push(
|
|
@@ -4982,7 +5005,7 @@ function renderBarChart(chart, x, y, w, h) {
|
|
|
4982
5005
|
const color = series[s].color;
|
|
4983
5006
|
for (let c = 0; c < series[s].values.length; c++) {
|
|
4984
5007
|
const val = series[s].values[c];
|
|
4985
|
-
const barH = val /
|
|
5008
|
+
const barH = val / scaleMax * h;
|
|
4986
5009
|
const barX = x + c * groupWidth + groupPadding + s * barWidth;
|
|
4987
5010
|
const barY = y + h - barH;
|
|
4988
5011
|
parts.push(
|
|
@@ -5010,12 +5033,15 @@ function renderLineChart(chart, x, y, w, h) {
|
|
|
5010
5033
|
debug("chart.line", "category count is 0");
|
|
5011
5034
|
return "";
|
|
5012
5035
|
}
|
|
5036
|
+
const ticks = computeNiceTicks(0, maxVal);
|
|
5037
|
+
const scaleMax = ticks[ticks.length - 1];
|
|
5013
5038
|
parts.push(
|
|
5014
5039
|
`<line x1="${round(x)}" y1="${round(y + h)}" x2="${round(x + w)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5015
5040
|
);
|
|
5016
5041
|
parts.push(
|
|
5017
5042
|
`<line x1="${round(x)}" y1="${round(y)}" x2="${round(x)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5018
5043
|
);
|
|
5044
|
+
parts.push(renderValueAxisLabels(ticks, 0, scaleMax, x, y, h));
|
|
5019
5045
|
for (let c = 0; c < catCount; c++) {
|
|
5020
5046
|
const label = categories[c] ?? "";
|
|
5021
5047
|
const divisor = catCount > 1 ? catCount - 1 : 1;
|
|
@@ -5029,7 +5055,7 @@ function renderLineChart(chart, x, y, w, h) {
|
|
|
5029
5055
|
const divisor = catCount > 1 ? catCount - 1 : 1;
|
|
5030
5056
|
const points = series[s].values.map((val, i) => {
|
|
5031
5057
|
const px = round(x + i / divisor * w);
|
|
5032
|
-
const py = round(y + h - val /
|
|
5058
|
+
const py = round(y + h - val / scaleMax * h);
|
|
5033
5059
|
return `${px},${py}`;
|
|
5034
5060
|
});
|
|
5035
5061
|
parts.push(
|
|
@@ -5059,12 +5085,15 @@ function renderAreaChart(chart, x, y, w, h) {
|
|
|
5059
5085
|
debug("chart.area", "category count is 0");
|
|
5060
5086
|
return "";
|
|
5061
5087
|
}
|
|
5088
|
+
const ticks = computeNiceTicks(0, maxVal);
|
|
5089
|
+
const scaleMax = ticks[ticks.length - 1];
|
|
5062
5090
|
parts.push(
|
|
5063
5091
|
`<line x1="${round(x)}" y1="${round(y + h)}" x2="${round(x + w)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5064
5092
|
);
|
|
5065
5093
|
parts.push(
|
|
5066
5094
|
`<line x1="${round(x)}" y1="${round(y)}" x2="${round(x)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5067
5095
|
);
|
|
5096
|
+
parts.push(renderValueAxisLabels(ticks, 0, scaleMax, x, y, h));
|
|
5068
5097
|
for (let c = 0; c < catCount; c++) {
|
|
5069
5098
|
const label = categories[c] ?? "";
|
|
5070
5099
|
const divisor = catCount > 1 ? catCount - 1 : 1;
|
|
@@ -5079,7 +5108,7 @@ function renderAreaChart(chart, x, y, w, h) {
|
|
|
5079
5108
|
const divisor = catCount > 1 ? catCount - 1 : 1;
|
|
5080
5109
|
const dataPoints = series[s].values.map((val, i) => {
|
|
5081
5110
|
const px = round(x + i / divisor * w);
|
|
5082
|
-
const py = round(y + h - val /
|
|
5111
|
+
const py = round(y + h - val / scaleMax * h);
|
|
5083
5112
|
return { px, py };
|
|
5084
5113
|
});
|
|
5085
5114
|
const topPoints = dataPoints.map((p) => `${p.px},${p.py}`).join(" ");
|
|
@@ -5193,12 +5222,15 @@ function renderScatterChart(chart, x, y, w, h) {
|
|
|
5193
5222
|
}
|
|
5194
5223
|
if (maxX === 0) maxX = 1;
|
|
5195
5224
|
if (maxY === 0) maxY = 1;
|
|
5225
|
+
const yTicks = computeNiceTicks(0, maxY);
|
|
5226
|
+
const scaleMaxY = yTicks[yTicks.length - 1];
|
|
5196
5227
|
parts.push(
|
|
5197
5228
|
`<line x1="${round(x)}" y1="${round(y + h)}" x2="${round(x + w)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5198
5229
|
);
|
|
5199
5230
|
parts.push(
|
|
5200
5231
|
`<line x1="${round(x)}" y1="${round(y)}" x2="${round(x)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5201
5232
|
);
|
|
5233
|
+
parts.push(renderValueAxisLabels(yTicks, 0, scaleMaxY, x, y, h));
|
|
5202
5234
|
for (let s = 0; s < series.length; s++) {
|
|
5203
5235
|
const color = series[s].color;
|
|
5204
5236
|
const xVals = series[s].xValues ?? [];
|
|
@@ -5206,7 +5238,7 @@ function renderScatterChart(chart, x, y, w, h) {
|
|
|
5206
5238
|
const xVal = xVals[i] ?? i;
|
|
5207
5239
|
const yVal = series[s].values[i];
|
|
5208
5240
|
const px = x + xVal / maxX * w;
|
|
5209
|
-
const py = y + h - yVal /
|
|
5241
|
+
const py = y + h - yVal / scaleMaxY * h;
|
|
5210
5242
|
parts.push(`<circle cx="${round(px)}" cy="${round(py)}" r="4" ${fillAttr(color)}/>`);
|
|
5211
5243
|
}
|
|
5212
5244
|
}
|
|
@@ -5233,12 +5265,15 @@ function renderBubbleChart(chart, x, y, w, h) {
|
|
|
5233
5265
|
if (maxY === 0) maxY = 1;
|
|
5234
5266
|
if (maxBubble === 0) maxBubble = 1;
|
|
5235
5267
|
const maxRadius = Math.min(w, h) * 0.08;
|
|
5268
|
+
const yTicks = computeNiceTicks(0, maxY);
|
|
5269
|
+
const scaleMaxY = yTicks[yTicks.length - 1];
|
|
5236
5270
|
parts.push(
|
|
5237
5271
|
`<line x1="${round(x)}" y1="${round(y + h)}" x2="${round(x + w)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5238
5272
|
);
|
|
5239
5273
|
parts.push(
|
|
5240
5274
|
`<line x1="${round(x)}" y1="${round(y)}" x2="${round(x)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5241
5275
|
);
|
|
5276
|
+
parts.push(renderValueAxisLabels(yTicks, 0, scaleMaxY, x, y, h));
|
|
5242
5277
|
for (let s = 0; s < series.length; s++) {
|
|
5243
5278
|
const color = series[s].color;
|
|
5244
5279
|
const xVals = series[s].xValues ?? [];
|
|
@@ -5248,7 +5283,7 @@ function renderBubbleChart(chart, x, y, w, h) {
|
|
|
5248
5283
|
const yVal = series[s].values[i];
|
|
5249
5284
|
const size = sizes[i] ?? 1;
|
|
5250
5285
|
const px = x + xVal / maxX * w;
|
|
5251
|
-
const py = y + h - yVal /
|
|
5286
|
+
const py = y + h - yVal / scaleMaxY * h;
|
|
5252
5287
|
const r = Math.max(2, Math.sqrt(size / maxBubble) * maxRadius);
|
|
5253
5288
|
parts.push(
|
|
5254
5289
|
`<circle cx="${round(px)}" cy="${round(py)}" r="${round(r)}" ${fillAttr(color)} fill-opacity="0.6"/>`
|
|
@@ -5361,12 +5396,16 @@ function renderStockChart(chart, x, y, w, h) {
|
|
|
5361
5396
|
debug("chart.stock", "max equals min value");
|
|
5362
5397
|
return "";
|
|
5363
5398
|
}
|
|
5399
|
+
const ticks = computeNiceTicks(minVal, maxVal);
|
|
5400
|
+
const scaleMin = ticks[0];
|
|
5401
|
+
const scaleMax = ticks[ticks.length - 1];
|
|
5364
5402
|
parts.push(
|
|
5365
5403
|
`<line x1="${round(x)}" y1="${round(y + h)}" x2="${round(x + w)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5366
5404
|
);
|
|
5367
5405
|
parts.push(
|
|
5368
5406
|
`<line x1="${round(x)}" y1="${round(y)}" x2="${round(x)}" y2="${round(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5369
5407
|
);
|
|
5408
|
+
parts.push(renderValueAxisLabels(ticks, scaleMin, scaleMax, x, y, h));
|
|
5370
5409
|
for (let c = 0; c < catCount; c++) {
|
|
5371
5410
|
const label = categories[c] ?? "";
|
|
5372
5411
|
const labelX = x + (c + 0.5) * (w / catCount);
|
|
@@ -5374,15 +5413,15 @@ function renderStockChart(chart, x, y, w, h) {
|
|
|
5374
5413
|
`<text x="${round(labelX)}" y="${round(y + h + 15)}" text-anchor="middle" font-size="10" fill="#595959">${escapeXml(label)}</text>`
|
|
5375
5414
|
);
|
|
5376
5415
|
}
|
|
5377
|
-
const range =
|
|
5416
|
+
const range = scaleMax - scaleMin;
|
|
5378
5417
|
for (let c = 0; c < catCount; c++) {
|
|
5379
5418
|
const cx = x + (c + 0.5) * (w / catCount);
|
|
5380
5419
|
const highVal = highSeries.values[c] ?? 0;
|
|
5381
5420
|
const lowVal = lowSeries.values[c] ?? 0;
|
|
5382
5421
|
const closeVal = closeSeries.values[c] ?? 0;
|
|
5383
|
-
const highY = y + h - (highVal -
|
|
5384
|
-
const lowY = y + h - (lowVal -
|
|
5385
|
-
const closeY = y + h - (closeVal -
|
|
5422
|
+
const highY = y + h - (highVal - scaleMin) / range * h;
|
|
5423
|
+
const lowY = y + h - (lowVal - scaleMin) / range * h;
|
|
5424
|
+
const closeY = y + h - (closeVal - scaleMin) / range * h;
|
|
5386
5425
|
parts.push(
|
|
5387
5426
|
`<line x1="${round(cx)}" y1="${round(highY)}" x2="${round(cx)}" y2="${round(lowY)}" stroke="#404040" stroke-width="2"/>`
|
|
5388
5427
|
);
|
|
@@ -5615,6 +5654,49 @@ function getPieSliceColor(index, chart) {
|
|
|
5615
5654
|
}
|
|
5616
5655
|
return DEFAULT_SERIES_COLORS[index % DEFAULT_SERIES_COLORS.length];
|
|
5617
5656
|
}
|
|
5657
|
+
function computeNiceTicks(minVal, maxVal, targetCount = 5) {
|
|
5658
|
+
const range = maxVal - minVal;
|
|
5659
|
+
if (range === 0) return [minVal];
|
|
5660
|
+
const roughStep = range / targetCount;
|
|
5661
|
+
const magnitude = Math.pow(10, Math.floor(Math.log10(roughStep)));
|
|
5662
|
+
const residual = roughStep / magnitude;
|
|
5663
|
+
let niceStep;
|
|
5664
|
+
if (residual <= 1.5) niceStep = magnitude;
|
|
5665
|
+
else if (residual <= 3) niceStep = 2 * magnitude;
|
|
5666
|
+
else if (residual <= 7) niceStep = 5 * magnitude;
|
|
5667
|
+
else niceStep = 10 * magnitude;
|
|
5668
|
+
const niceMin = Math.floor(minVal / niceStep) * niceStep;
|
|
5669
|
+
const niceMax = Math.ceil(maxVal / niceStep) * niceStep;
|
|
5670
|
+
const ticks = [];
|
|
5671
|
+
for (let v = niceMin; v <= niceMax + niceStep * 0.5; v += niceStep) {
|
|
5672
|
+
ticks.push(Math.round(v * 1e10) / 1e10);
|
|
5673
|
+
}
|
|
5674
|
+
return ticks;
|
|
5675
|
+
}
|
|
5676
|
+
function renderValueAxisLabels(ticks, scaleMin, scaleMax, x, y, h) {
|
|
5677
|
+
const parts = [];
|
|
5678
|
+
const range = scaleMax - scaleMin;
|
|
5679
|
+
if (range === 0) return "";
|
|
5680
|
+
for (const tick of ticks) {
|
|
5681
|
+
const ratio = (tick - scaleMin) / range;
|
|
5682
|
+
if (ratio < -1e-3 || ratio > 1.001) continue;
|
|
5683
|
+
const tickY = y + h - ratio * h;
|
|
5684
|
+
parts.push(
|
|
5685
|
+
`<text x="${round(x - 5)}" y="${round(tickY + 4)}" text-anchor="end" font-size="10" fill="#595959">${escapeXml(formatTickValue(tick))}</text>`
|
|
5686
|
+
);
|
|
5687
|
+
parts.push(
|
|
5688
|
+
`<line x1="${round(x - 3)}" y1="${round(tickY)}" x2="${round(x)}" y2="${round(tickY)}" stroke="#D9D9D9" stroke-width="1"/>`
|
|
5689
|
+
);
|
|
5690
|
+
}
|
|
5691
|
+
return parts.join("");
|
|
5692
|
+
}
|
|
5693
|
+
function formatTickValue(value) {
|
|
5694
|
+
if (Math.abs(value) >= 1e9) return `${round(value / 1e9)}B`;
|
|
5695
|
+
if (Math.abs(value) >= 1e6) return `${round(value / 1e6)}M`;
|
|
5696
|
+
if (Math.abs(value) >= 1e4) return `${round(value / 1e3)}K`;
|
|
5697
|
+
if (Number.isInteger(value)) return String(value);
|
|
5698
|
+
return String(round(value));
|
|
5699
|
+
}
|
|
5618
5700
|
function getMaxValue(series) {
|
|
5619
5701
|
let max = 0;
|
|
5620
5702
|
for (const s of series) {
|
package/package.json
CHANGED