@pixerate/editor 0.162.0 → 0.163.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 +1 -1
- package/dist/agent/index.cjs +34 -34
- package/dist/agent/index.js +5 -5
- package/dist/animation-editor/index.cjs +57 -57
- package/dist/animation-editor/index.js +3 -3
- package/dist/{brushstrokeRender-Q4LI22QV.js → brushstrokeRender-7LXCU7PD.js} +2 -2
- package/dist/{brushstrokeRender-5WANQIG4.cjs → brushstrokeRender-VO4ZTWVA.cjs} +5 -5
- package/dist/canvas/index.cjs +14 -14
- package/dist/canvas/index.js +3 -3
- package/dist/chart/commands.d.cts +2 -1
- package/dist/chart/commands.d.ts +2 -1
- package/dist/chart/controller.d.cts +109 -1
- package/dist/chart/controller.d.ts +109 -1
- package/dist/chart/index.cjs +605 -130
- package/dist/chart/index.d.cts +1 -0
- package/dist/chart/index.d.ts +1 -0
- package/dist/chart/index.js +490 -15
- package/dist/chart/layout-annotations.d.cts +29 -0
- package/dist/chart/layout-annotations.d.ts +29 -0
- package/dist/chart/layout-families.d.cts +23 -0
- package/dist/chart/layout-families.d.ts +23 -0
- package/dist/chart/mermaid.d.cts +2 -1
- package/dist/chart/mermaid.d.ts +2 -1
- package/dist/chart/schemas.d.cts +13 -0
- package/dist/chart/schemas.d.ts +13 -0
- package/dist/chart/spec.d.cts +30 -5
- package/dist/chart/spec.d.ts +30 -5
- package/dist/chart/stats.d.cts +9 -1
- package/dist/chart/stats.d.ts +9 -1
- package/dist/chart/tools.d.cts +1 -1
- package/dist/chart/tools.d.ts +1 -1
- package/dist/chart/tree.d.cts +62 -0
- package/dist/chart/tree.d.ts +62 -0
- package/dist/chart/types.d.cts +77 -3
- package/dist/chart/types.d.ts +77 -3
- package/dist/{chunk-7YBDRJXT.cjs → chunk-22KLZZGH.cjs} +8 -8
- package/dist/{chunk-LQLZHP6F.js → chunk-36OKQDCF.js} +7 -4
- package/dist/{chunk-HLOKB7WW.js → chunk-3NEZJCBY.js} +366 -6
- package/dist/{chunk-GWV4T5NZ.js → chunk-3VLOOO3G.js} +1 -1
- package/dist/{chunk-3GJH2MGH.cjs → chunk-5AARIS3R.cjs} +35 -35
- package/dist/{chunk-EVPZ5IKN.js → chunk-5FNGLV2D.js} +65 -3
- package/dist/{chunk-EQ46F75O.cjs → chunk-D54JHQK2.cjs} +51 -51
- package/dist/{chunk-NO5QQDHZ.js → chunk-DAPQGA55.js} +2 -2
- package/dist/{chunk-3IHNXRUV.cjs → chunk-GKNEJ6EN.cjs} +399 -67
- package/dist/{chunk-236TKNUR.cjs → chunk-JCXMJUQO.cjs} +2 -2
- package/dist/{chunk-4NGVHHJB.cjs → chunk-NZD2MZJC.cjs} +80 -18
- package/dist/{chunk-CX6JLDFC.js → chunk-OXERWBKE.js} +3 -3
- package/dist/{chunk-32MMQYCQ.cjs → chunk-PA3IMPPS.cjs} +81 -81
- package/dist/{chunk-7CKPUUN4.js → chunk-POR54B55.js} +61 -10
- package/dist/{chunk-KGTF55R3.js → chunk-QY4KUDMY.js} +1 -1
- package/dist/{chunk-LL3H5XUH.cjs → chunk-R3P2M7HU.cjs} +367 -7
- package/dist/{chunk-HDGBM7EA.cjs → chunk-SBQVEBSU.cjs} +580 -53
- package/dist/{chunk-2TKHLX6H.js → chunk-SET6KJOP.js} +1 -1
- package/dist/{chunk-XDCFJEU7.js → chunk-SZA42R4N.js} +346 -14
- package/dist/{chunk-GLMP22FT.js → chunk-TALQONZ5.js} +3 -3
- package/dist/{chunk-GNK7SESQ.cjs → chunk-V7GWZIZK.cjs} +7 -4
- package/dist/{chunk-EYVVIZJ3.cjs → chunk-WTW7QRTS.cjs} +1 -1
- package/dist/{chunk-NA4UVVJ4.cjs → chunk-YGG7WDWT.cjs} +76 -25
- package/dist/{chunk-JGKCC5OF.cjs → chunk-YQ5C5BVK.cjs} +10 -9
- package/dist/{chunk-EY22AY6R.cjs → chunk-YSAGBIRW.cjs} +16 -16
- package/dist/{chunk-A5UCR2TO.js → chunk-YXKLVD56.js} +10 -9
- package/dist/{chunk-DTSTNRPH.js → chunk-Z4P2RBMJ.js} +536 -9
- package/dist/{chunk-45XXVHVG.js → chunk-ZZGPYNS2.js} +1 -1
- package/dist/export/index.cjs +4 -4
- package/dist/export/index.js +3 -3
- package/dist/extensions/index.cjs +7 -7
- package/dist/extensions/index.js +6 -6
- package/dist/image-editor/index.cjs +638 -638
- package/dist/image-editor/index.js +4 -4
- package/dist/index.cjs +13 -13
- package/dist/index.js +7 -7
- package/dist/paint/index.cjs +2 -2
- package/dist/paint/index.js +1 -1
- package/dist/paint-editor/index.cjs +129 -149
- package/dist/paint-editor/index.js +5 -25
- package/dist/vector/index.cjs +948 -948
- package/dist/vector/index.js +3 -3
- package/package.json +3 -3
- package/skills/pixerate-canvas/SKILL.md +16 -0
- package/skills/pixerate-chart/SKILL.md +92 -9
- package/skills/pixerate-paint-editor/SKILL.md +0 -2
|
@@ -7,27 +7,36 @@ import {
|
|
|
7
7
|
r2,
|
|
8
8
|
sameChartSelection,
|
|
9
9
|
summarizeChartSeries
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-Z4P2RBMJ.js";
|
|
11
11
|
import {
|
|
12
12
|
CHART_TYPE_LABELS,
|
|
13
13
|
DEFAULT_CHART_PALETTE,
|
|
14
14
|
DEFAULT_CHART_THEME,
|
|
15
|
-
chartColorsByCategory,
|
|
16
15
|
chartDataFromTable,
|
|
17
16
|
chartFamily,
|
|
18
17
|
chartSeriesColor,
|
|
19
18
|
chartSliceColor,
|
|
19
|
+
chartSpecColorsByCategory,
|
|
20
|
+
chartTreeDepth,
|
|
21
|
+
chartTreeFromRows,
|
|
22
|
+
chartTreeFromTable,
|
|
23
|
+
chartTreeToRows,
|
|
20
24
|
checkChartColors,
|
|
21
25
|
cloneChartSeries,
|
|
22
26
|
cloneChartSpec,
|
|
27
|
+
cloneChartTree,
|
|
23
28
|
defaultChartAxis,
|
|
24
29
|
describeChartErrorBars,
|
|
30
|
+
findChartTreeNode,
|
|
31
|
+
normalizeChartReferenceBand,
|
|
32
|
+
normalizeChartReferenceLine,
|
|
25
33
|
normalizeChartSpec,
|
|
34
|
+
normalizeChartTree,
|
|
26
35
|
normalizeChartTrendline,
|
|
27
36
|
parseChartValue,
|
|
28
37
|
shadeChartColor,
|
|
29
38
|
uniqueChartSeriesId
|
|
30
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-3NEZJCBY.js";
|
|
31
40
|
import {
|
|
32
41
|
parseCsv,
|
|
33
42
|
parseTsv
|
|
@@ -64,7 +73,7 @@ function splitOutside(text, sep) {
|
|
|
64
73
|
}
|
|
65
74
|
function chartMermaidFamily(spec) {
|
|
66
75
|
const family = chartFamily(spec);
|
|
67
|
-
if (family === "pie" || family === "funnel" || family === "treemap") return "pie";
|
|
76
|
+
if (family === "pie" || family === "funnel" || family === "treemap" || family === "parliament" || family === "sunburst") return "pie";
|
|
68
77
|
if (family === "radar") return "radar";
|
|
69
78
|
return "xychart";
|
|
70
79
|
}
|
|
@@ -430,7 +439,7 @@ function findMermaidCharts(text) {
|
|
|
430
439
|
function summarizeChart(spec, size = { width: 640, height: 400 }, scene = layoutChart(spec, { ...size, selection: null })) {
|
|
431
440
|
const visible = spec.series.filter((s) => !s.hidden);
|
|
432
441
|
const series = visible.map((s) => summarizeChartSeries(s, spec.categories));
|
|
433
|
-
const colors =
|
|
442
|
+
const colors = chartSpecColorsByCategory(spec) ? spec.categories.map((_, i) => visible[0]?.pointColors?.[i] || chartSliceColor(spec, i)) : visible.map((s) => chartSeriesColor(spec, spec.series.indexOf(s)));
|
|
434
443
|
const check = checkChartColors(colors, { surface: scene.theme.surface.startsWith("var(") ? "#ffffff" : scene.theme.surface });
|
|
435
444
|
const axis = (s, a) => s ? { min: s.domain[0], max: s.domain[1], ticks: [...s.ticks], ...s.logBase ? { logBase: s.logBase } : {}, ...a.reverse ? { reversed: true } : {} } : void 0;
|
|
436
445
|
return {
|
|
@@ -454,14 +463,50 @@ function describeChart(spec, series = spec.series.filter((s) => !s.hidden).map((
|
|
|
454
463
|
const fmt = (v) => v === null ? "blank" : formatChartNumber(v, format);
|
|
455
464
|
const head = spec.title ? `${spec.title}: a ${kind} chart` : `A ${kind} chart`;
|
|
456
465
|
if (!series.length || series.every((s) => s.count === 0)) return `${head} with no data.`;
|
|
457
|
-
if (family === "pie" || family === "funnel" || family === "treemap") {
|
|
466
|
+
if (family === "pie" || family === "funnel" || family === "treemap" || family === "sunburst") {
|
|
458
467
|
const s = series[0];
|
|
459
468
|
const total = s.sum ?? 0;
|
|
460
469
|
const share = (v) => total && v !== null ? ` (${Math.round(v / total * 100)}%)` : "";
|
|
461
470
|
parts.push(`${head} of ${s.name} across ${spec.categories.length} categories, totalling ${fmt(s.sum)}.`);
|
|
462
471
|
parts.push(`The largest is ${s.maxAt} at ${fmt(s.max)}${share(s.max)}; the smallest is ${s.minAt} at ${fmt(s.min)}${share(s.min)}.`);
|
|
472
|
+
if (spec.tree.length) {
|
|
473
|
+
const depth = chartTreeDepth(spec.tree);
|
|
474
|
+
const items = chartTreeToRows(spec.tree).length;
|
|
475
|
+
parts.push(`The data is grouped ${depth} level${depth === 1 ? "" : "s"} deep with ${items} item${items === 1 ? "" : "s"}.`);
|
|
476
|
+
}
|
|
463
477
|
return parts.join(" ");
|
|
464
478
|
}
|
|
479
|
+
if (family === "parliament") {
|
|
480
|
+
const s = spec.series.find((x) => !x.hidden);
|
|
481
|
+
const seats = spec.categories.map((c, i) => ({ c, n: Math.max(0, Math.round(s.values[i] ?? 0)) }));
|
|
482
|
+
const total = seats.reduce((t, x) => t + x.n, 0);
|
|
483
|
+
const majority = spec.parliamentMajority ?? Math.floor(total / 2) + 1;
|
|
484
|
+
const largest = [...seats].sort((a, b) => b.n - a.n)[0];
|
|
485
|
+
const verdict = largest && largest.n >= majority ? `${largest.c} holds a majority with ${largest.n}` : `no group reaches the majority of ${majority}; the largest is ${largest?.c} with ${largest?.n}`;
|
|
486
|
+
return `${head} of ${total} seats across ${seats.length} groups: ${verdict}.`;
|
|
487
|
+
}
|
|
488
|
+
if (family === "slope") {
|
|
489
|
+
const [a, b] = spec.series.filter((x) => !x.hidden);
|
|
490
|
+
if (!b) return `${head} that needs a second series to compare against.`;
|
|
491
|
+
const moves = spec.categories.map((c, i) => ({ c, d: a.values[i] !== null && b.values[i] !== null ? b.values[i] - a.values[i] : null })).filter((m) => m.d !== null);
|
|
492
|
+
const up = moves.filter((m) => m.d > 0).length;
|
|
493
|
+
const down = moves.filter((m) => m.d < 0).length;
|
|
494
|
+
const top = [...moves].sort((x, y) => y.d - x.d);
|
|
495
|
+
const extremes = top.length ? ` The biggest rise is ${top[0].c} (${fmt(top[0].d)}); the biggest fall is ${top[top.length - 1].c} (${fmt(top[top.length - 1].d)}).` : "";
|
|
496
|
+
return `${head} from ${a.name} to ${b.name}: ${up} of ${moves.length} rose and ${down} fell.${extremes}`;
|
|
497
|
+
}
|
|
498
|
+
if (family === "likert") {
|
|
499
|
+
const answers = spec.series.filter((x) => !x.hidden);
|
|
500
|
+
const side = Math.floor(answers.length / 2);
|
|
501
|
+
const agree = spec.categories.map((c, i) => {
|
|
502
|
+
const vals = answers.map((x) => Math.max(0, x.values[i] ?? 0));
|
|
503
|
+
const total = vals.reduce((t, v) => t + v, 0);
|
|
504
|
+
return { c, share: total ? vals.slice(answers.length - side).reduce((t, v) => t + v, 0) / total : 0 };
|
|
505
|
+
});
|
|
506
|
+
const best = [...agree].sort((x, y) => y.share - x.share);
|
|
507
|
+
const pct = (v) => `${Math.round(v * 100)}%`;
|
|
508
|
+
return `${head} of ${spec.categories.length} questions with ${answers.length} answers from ${answers[0]?.name} to ${answers[answers.length - 1]?.name}. Most positive: ${best[0]?.c} (${pct(best[0]?.share ?? 0)}); least: ${best[best.length - 1]?.c} (${pct(best[best.length - 1]?.share ?? 0)}).`;
|
|
509
|
+
}
|
|
465
510
|
if (family === "waterfall") {
|
|
466
511
|
const s = spec.series.find((x) => !x.hidden);
|
|
467
512
|
const steps = s.values.filter((v, i) => v !== null && Number.isFinite(v) && !/\b(sub)?total\b/i.test(spec.categories[i] ?? ""));
|
|
@@ -632,6 +677,10 @@ function chartPresetPatch(options) {
|
|
|
632
677
|
}
|
|
633
678
|
|
|
634
679
|
// src/chart/controller.ts
|
|
680
|
+
function pointLists(s) {
|
|
681
|
+
const lists = [s.values, s.x, s.sizes, s.pointColors, s.callouts, s.errorBars?.plus, s.errorBars?.minus, s.xErrorBars?.plus, s.xErrorBars?.minus];
|
|
682
|
+
return lists.filter((l) => Array.isArray(l));
|
|
683
|
+
}
|
|
635
684
|
function sameChartJson(a, b) {
|
|
636
685
|
if (a === b) return true;
|
|
637
686
|
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return Number.isNaN(a) && Number.isNaN(b);
|
|
@@ -861,13 +910,16 @@ var ChartController = class {
|
|
|
861
910
|
* to the categories; all-or-nothing like `update`.
|
|
862
911
|
*/
|
|
863
912
|
setData(data, options = {}) {
|
|
864
|
-
|
|
913
|
+
const clear = (data.categories || data.series) && this.spec.tree.length ? { tree: [] } : {};
|
|
914
|
+
return this.update({ ...data.categories ? { categories: data.categories } : {}, ...data.series ? { series: data.series } : {}, ...clear }, { label: options.label ?? "Edit chart data" });
|
|
865
915
|
}
|
|
866
916
|
/**
|
|
867
917
|
* Sets one value (numbers, numeric text such as `1,200` or `12%`, or blank for a gap).
|
|
868
918
|
* One undo step, or part of the current gesture.
|
|
869
919
|
*/
|
|
870
920
|
setValue(seriesId, index, value, options = {}) {
|
|
921
|
+
const grouped = this.treeLocked();
|
|
922
|
+
if (grouped) return grouped;
|
|
871
923
|
const series = this.getSeries(seriesId);
|
|
872
924
|
if (!series) return this.unknownSeries(seriesId);
|
|
873
925
|
if (!Number.isInteger(index) || index < 0 || index >= this.spec.categories.length) return this.badIndex(index);
|
|
@@ -888,6 +940,8 @@ var ChartController = class {
|
|
|
888
940
|
}
|
|
889
941
|
/** Several values as one undo step (all-or-nothing). */
|
|
890
942
|
setValues(cells, options = {}) {
|
|
943
|
+
const grouped = this.treeLocked();
|
|
944
|
+
if (grouped) return grouped;
|
|
891
945
|
const issues = [];
|
|
892
946
|
const parsed = [];
|
|
893
947
|
for (const cell of cells) {
|
|
@@ -925,6 +979,7 @@ var ChartController = class {
|
|
|
925
979
|
`Delete series "${series.name}"`,
|
|
926
980
|
this.edited((d) => {
|
|
927
981
|
d.series = d.series.filter((s) => s.id !== id);
|
|
982
|
+
d.highlight = d.highlight.filter((h) => h !== id && (h !== series.name || d.series.some((s) => s.name === h) || d.categories.includes(h)));
|
|
928
983
|
})
|
|
929
984
|
);
|
|
930
985
|
return { ok: true };
|
|
@@ -942,7 +997,7 @@ var ChartController = class {
|
|
|
942
997
|
if (patch.hidden) next.hidden = true;
|
|
943
998
|
else delete next.hidden;
|
|
944
999
|
}
|
|
945
|
-
const resettable = ["axis", "x", "sizes", "pointColors", "marker", "curve", "dataLabels", "format", "errorBars", "xErrorBars"];
|
|
1000
|
+
const resettable = ["axis", "x", "sizes", "pointColors", "callouts", "marker", "curve", "dataLabels", "format", "errorBars", "xErrorBars"];
|
|
946
1001
|
for (const key of resettable) {
|
|
947
1002
|
if (!(key in patch) || patch[key] === void 0) continue;
|
|
948
1003
|
if (patch[key] === null) delete next[key];
|
|
@@ -950,7 +1005,9 @@ var ChartController = class {
|
|
|
950
1005
|
}
|
|
951
1006
|
if (next.axis === "primary") delete next.axis;
|
|
952
1007
|
const series2 = this.spec.series.map((s) => s.id === id ? next : s);
|
|
953
|
-
const
|
|
1008
|
+
const renamed = next.name !== series.name && this.spec.highlight.includes(series.name) && !this.spec.categories.includes(series.name);
|
|
1009
|
+
const highlight = renamed ? this.spec.highlight.map((h) => h === series.name ? next.name : h) : this.spec.highlight;
|
|
1010
|
+
const { spec, issues } = normalizeChartSpec({ ...this.spec, series: series2, highlight });
|
|
954
1011
|
if (issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
|
|
955
1012
|
if (JSON.stringify(spec.series) === JSON.stringify(this.spec.series)) return { ok: true };
|
|
956
1013
|
const label = patch.hidden !== void 0 && Object.keys(patch).length === 1 ? `${patch.hidden ? "Hide" : "Show"} "${series.name}"` : patch.name !== void 0 && Object.keys(patch).length === 1 ? "Rename series" : `Change series "${series.name}"`;
|
|
@@ -979,41 +1036,52 @@ var ChartController = class {
|
|
|
979
1036
|
}
|
|
980
1037
|
/** Inserts a category (with gaps in every series) at `at` (default: the end). Returns its index. */
|
|
981
1038
|
addCategory(label, options = {}) {
|
|
1039
|
+
const grouped = this.treeLocked();
|
|
1040
|
+
if (grouped) return grouped;
|
|
982
1041
|
const at = Math.max(0, Math.min(this.spec.categories.length, options.at ?? this.spec.categories.length));
|
|
983
1042
|
const name = label ?? `${this.spec.categories.length + 1}`;
|
|
984
1043
|
this.commit(
|
|
985
1044
|
`Add category "${name}"`,
|
|
986
1045
|
this.edited((d) => {
|
|
987
1046
|
d.categories.splice(at, 0, name);
|
|
988
|
-
for (const s of d.series) s.
|
|
1047
|
+
for (const s of d.series) for (const list of pointLists(s)) list.splice(at, 0, null);
|
|
989
1048
|
}),
|
|
990
1049
|
{ kind: "category", index: at }
|
|
991
1050
|
);
|
|
992
1051
|
return { ok: true, index: at };
|
|
993
1052
|
}
|
|
994
1053
|
removeCategory(index) {
|
|
1054
|
+
const grouped = this.treeLocked();
|
|
1055
|
+
if (grouped) return grouped;
|
|
995
1056
|
if (!Number.isInteger(index) || index < 0 || index >= this.spec.categories.length) return this.badIndex(index);
|
|
996
1057
|
this.commit(
|
|
997
1058
|
`Delete category "${this.spec.categories[index]}"`,
|
|
998
1059
|
this.edited((d) => {
|
|
999
|
-
d.categories.splice(index, 1);
|
|
1000
|
-
for (const s of d.series) s.
|
|
1060
|
+
const [label] = d.categories.splice(index, 1);
|
|
1061
|
+
for (const s of d.series) for (const list of pointLists(s)) list.splice(index, 1);
|
|
1062
|
+
if (!d.categories.includes(label)) d.highlight = d.highlight.filter((h) => h !== label);
|
|
1001
1063
|
})
|
|
1002
1064
|
);
|
|
1003
1065
|
return { ok: true };
|
|
1004
1066
|
}
|
|
1005
1067
|
renameCategory(index, label) {
|
|
1068
|
+
const grouped = this.treeLocked();
|
|
1069
|
+
if (grouped) return grouped;
|
|
1006
1070
|
if (!Number.isInteger(index) || index < 0 || index >= this.spec.categories.length) return this.badIndex(index);
|
|
1007
1071
|
if (this.spec.categories[index] === label) return { ok: true };
|
|
1008
1072
|
this.commit(
|
|
1009
1073
|
"Rename category",
|
|
1010
1074
|
this.edited((d) => {
|
|
1075
|
+
const before = d.categories[index];
|
|
1011
1076
|
d.categories[index] = String(label);
|
|
1077
|
+
if (d.highlight.includes(before) && !d.categories.includes(before)) d.highlight = d.highlight.map((h) => h === before ? String(label) : h);
|
|
1012
1078
|
})
|
|
1013
1079
|
);
|
|
1014
1080
|
return { ok: true };
|
|
1015
1081
|
}
|
|
1016
1082
|
moveCategory(from, to) {
|
|
1083
|
+
const grouped = this.treeLocked();
|
|
1084
|
+
if (grouped) return grouped;
|
|
1017
1085
|
if (!Number.isInteger(from) || from < 0 || from >= this.spec.categories.length) return this.badIndex(from);
|
|
1018
1086
|
const target = Math.max(0, Math.min(this.spec.categories.length - 1, to));
|
|
1019
1087
|
if (target === from) return { ok: true };
|
|
@@ -1025,7 +1093,7 @@ var ChartController = class {
|
|
|
1025
1093
|
"Reorder categories",
|
|
1026
1094
|
this.edited((d) => {
|
|
1027
1095
|
move(d.categories);
|
|
1028
|
-
for (const s of d.series)
|
|
1096
|
+
for (const s of d.series) for (const list of pointLists(s)) move(list);
|
|
1029
1097
|
})
|
|
1030
1098
|
);
|
|
1031
1099
|
return { ok: true };
|
|
@@ -1036,6 +1104,8 @@ var ChartController = class {
|
|
|
1036
1104
|
* grid grows to fit. Non-numeric value cells become gaps and are reported in `issues`.
|
|
1037
1105
|
*/
|
|
1038
1106
|
pasteCells(row, column, cells) {
|
|
1107
|
+
const grouped = this.treeLocked();
|
|
1108
|
+
if (grouped) return grouped;
|
|
1039
1109
|
const rows = typeof cells === "string" ? cells.includes(" ") ? parseTsv(cells) : parseCsv(cells) : cells.map((r) => [...r]);
|
|
1040
1110
|
if (!rows.length) return { ok: true };
|
|
1041
1111
|
const issues = [];
|
|
@@ -1083,7 +1153,7 @@ var ChartController = class {
|
|
|
1083
1153
|
const old = byName.get(s.name);
|
|
1084
1154
|
return { ...s, ...old?.color ? { color: old.color } : {}, ...old?.type ? { type: old.type } : {}, ...old ? { id: old.id } : {} };
|
|
1085
1155
|
});
|
|
1086
|
-
const { spec } = normalizeChartSpec({ ...this.spec, categories: data.categories, series });
|
|
1156
|
+
const { spec } = normalizeChartSpec({ ...this.spec, categories: data.categories, series, tree: [] });
|
|
1087
1157
|
this.commit("Import data", spec);
|
|
1088
1158
|
if (data.issues.length) {
|
|
1089
1159
|
this.issues = data.issues;
|
|
@@ -1098,6 +1168,252 @@ var ChartController = class {
|
|
|
1098
1168
|
return this.importTable(parseCsv(text, { delimiter }), options);
|
|
1099
1169
|
}
|
|
1100
1170
|
// -------------------------------------------------------------------------
|
|
1171
|
+
// Grouped data (tree)
|
|
1172
|
+
// -------------------------------------------------------------------------
|
|
1173
|
+
/** A refusal while the data is a tree (cell edits would be overwritten by its totals), else null. */
|
|
1174
|
+
treeLocked() {
|
|
1175
|
+
if (!this.spec.tree.length) return null;
|
|
1176
|
+
return this.reject(
|
|
1177
|
+
"This chart's data is grouped (a tree): its categories and values are the top-level groups and their totals. Change it with setTree / the tree node edits (set_chart_tree, add_chart_tree_node, update_chart_tree_node, remove_chart_tree_node), or replace it with new data."
|
|
1178
|
+
);
|
|
1179
|
+
}
|
|
1180
|
+
/** Replaces the grouped data (an empty list goes back to the table, keeping the totals as values). One undo step. */
|
|
1181
|
+
setTree(tree, options = {}) {
|
|
1182
|
+
const issues = [];
|
|
1183
|
+
const next = normalizeChartTree(tree, issues);
|
|
1184
|
+
if (issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
|
|
1185
|
+
return this.update({ tree: next }, { label: options.label ?? (next.length ? "Replace grouped data" : "Ungroup data") });
|
|
1186
|
+
}
|
|
1187
|
+
/**
|
|
1188
|
+
* Replaces the grouped data from rows of names and a value (group, …, item, value), as the
|
|
1189
|
+
* data grid edits it; colours of nodes whose path is unchanged are kept. All-or-nothing.
|
|
1190
|
+
*/
|
|
1191
|
+
setTreeRows(rows, options = {}) {
|
|
1192
|
+
const issues = [];
|
|
1193
|
+
const next = chartTreeFromRows(rows, issues);
|
|
1194
|
+
if (issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
|
|
1195
|
+
const keepColors = (list, prefix) => {
|
|
1196
|
+
for (const n of list) {
|
|
1197
|
+
const old = findChartTreeNode(this.spec.tree, [...prefix, n.name]);
|
|
1198
|
+
if (old?.color) n.color = old.color;
|
|
1199
|
+
if (n.children) keepColors(n.children, [...prefix, n.name]);
|
|
1200
|
+
}
|
|
1201
|
+
};
|
|
1202
|
+
keepColors(next, []);
|
|
1203
|
+
return this.update({ tree: next }, { label: options.label ?? "Edit grouped data" });
|
|
1204
|
+
}
|
|
1205
|
+
/** Replaces the data with a table of names and values (group, …, item, value); see `chartTreeFromTable`. */
|
|
1206
|
+
importTreeTable(rows, options = {}) {
|
|
1207
|
+
const issues = [];
|
|
1208
|
+
const next = chartTreeFromTable(rows, options, issues);
|
|
1209
|
+
if (issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
|
|
1210
|
+
if (!next.length) return this.reject('There is no grouped data to import: expected rows of names followed by a value, e.g. "Europe,France,40".');
|
|
1211
|
+
return this.update({ tree: next }, { label: "Import grouped data" });
|
|
1212
|
+
}
|
|
1213
|
+
nodeAt(path) {
|
|
1214
|
+
const node = findChartTreeNode(this.spec.tree, path);
|
|
1215
|
+
if (node) return node;
|
|
1216
|
+
const known = path.length > 1 ? findChartTreeNode(this.spec.tree, path.slice(0, -1)) : null;
|
|
1217
|
+
const siblings = (path.length > 1 ? known?.children : this.spec.tree) ?? [];
|
|
1218
|
+
return fail(
|
|
1219
|
+
`There is no node ${JSON.stringify(path)}. ${siblings.length ? `${path.length > 1 && known ? `"${known.name}" contains` : "The top level has"} ${siblings.map((n) => `"${n.name}"`).slice(0, 20).join(", ")}.` : "The chart has no grouped data yet; add nodes with add_chart_tree_node or set_chart_tree."}`
|
|
1220
|
+
);
|
|
1221
|
+
}
|
|
1222
|
+
/** Adds a node under the group at `parent` ([] for the top level) at `at` (default: the end). Returns its path. */
|
|
1223
|
+
addTreeNode(parent, input, options = {}) {
|
|
1224
|
+
let siblings = this.spec.tree;
|
|
1225
|
+
if (parent.length) {
|
|
1226
|
+
const group = this.nodeAt(parent);
|
|
1227
|
+
if ("ok" in group) return group;
|
|
1228
|
+
if (!group.children) return this.reject(`"${group.name}" is an item with its own value, not a group; add the node next to it, or turn it into a group by replacing the tree with set_chart_tree.`);
|
|
1229
|
+
siblings = group.children;
|
|
1230
|
+
}
|
|
1231
|
+
const issues = [];
|
|
1232
|
+
const [node] = normalizeChartTree([input], issues, "node");
|
|
1233
|
+
if (!node || issues.length) return this.reject(issues.map((i) => i.message).join(" ") || "The node is not valid.", issues);
|
|
1234
|
+
if (siblings.some((n) => n.name === node.name)) return this.reject(`"${node.name}" is already there; names under one group must be unique.`);
|
|
1235
|
+
const at = Math.max(0, Math.min(siblings.length, options.at ?? siblings.length));
|
|
1236
|
+
const tree = cloneChartTree(this.spec.tree);
|
|
1237
|
+
const list = parent.length ? findChartTreeNode(tree, parent).children : tree;
|
|
1238
|
+
list.splice(at, 0, node);
|
|
1239
|
+
const result = this.update({ tree }, { label: `Add "${node.name}"` });
|
|
1240
|
+
if (!result.ok) return result;
|
|
1241
|
+
return { ok: true, path: [...parent, node.name] };
|
|
1242
|
+
}
|
|
1243
|
+
/** Renames, revalues (items only) or recolours a node; `color: null` goes back to the palette. Returns its path. */
|
|
1244
|
+
updateTreeNode(path, patch) {
|
|
1245
|
+
const node = this.nodeAt(path);
|
|
1246
|
+
if ("ok" in node) return node;
|
|
1247
|
+
if (patch.value !== void 0 && node.children) return this.reject(`"${node.name}" is a group: its value is the sum of its items. Change the items instead.`);
|
|
1248
|
+
const name = patch.name !== void 0 ? String(patch.name).trim() : node.name;
|
|
1249
|
+
const siblings = (path.length > 1 ? findChartTreeNode(this.spec.tree, path.slice(0, -1)).children : this.spec.tree).filter((n) => n !== node);
|
|
1250
|
+
if (siblings.some((n) => n.name === name)) return this.reject(`There is already a "${name}" next to "${node.name}"; names under one group must be unique.`);
|
|
1251
|
+
const tree = cloneChartTree(this.spec.tree);
|
|
1252
|
+
const target = findChartTreeNode(tree, path);
|
|
1253
|
+
const merged = { ...target, name };
|
|
1254
|
+
if (patch.value !== void 0) merged.value = patch.value;
|
|
1255
|
+
if (patch.color === null) delete merged.color;
|
|
1256
|
+
else if (patch.color !== void 0) merged.color = patch.color;
|
|
1257
|
+
const issues = [];
|
|
1258
|
+
const [checked] = normalizeChartTree([merged], issues, "node");
|
|
1259
|
+
if (!checked || issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
|
|
1260
|
+
Object.assign(target, checked);
|
|
1261
|
+
if (patch.color === null) delete target.color;
|
|
1262
|
+
const result = this.update({ tree }, { label: patch.name !== void 0 && patch.name !== node.name ? "Rename group" : `Change "${node.name}"` });
|
|
1263
|
+
if (!result.ok) return result;
|
|
1264
|
+
return { ok: true, path: [...path.slice(0, -1), name] };
|
|
1265
|
+
}
|
|
1266
|
+
/** Removes a node (with everything under it). A group left empty becomes an item with value 0. */
|
|
1267
|
+
removeTreeNode(path) {
|
|
1268
|
+
const node = this.nodeAt(path);
|
|
1269
|
+
if ("ok" in node) return node;
|
|
1270
|
+
const tree = cloneChartTree(this.spec.tree);
|
|
1271
|
+
const parent = path.length > 1 ? findChartTreeNode(tree, path.slice(0, -1)) : null;
|
|
1272
|
+
const list = parent ? parent.children : tree;
|
|
1273
|
+
list.splice(
|
|
1274
|
+
list.findIndex((n) => n.name === node.name),
|
|
1275
|
+
1
|
|
1276
|
+
);
|
|
1277
|
+
if (parent && !parent.children.length) {
|
|
1278
|
+
delete parent.children;
|
|
1279
|
+
parent.value = 0;
|
|
1280
|
+
}
|
|
1281
|
+
return this.update({ tree }, { label: `Delete "${node.name}"` });
|
|
1282
|
+
}
|
|
1283
|
+
// -------------------------------------------------------------------------
|
|
1284
|
+
// Annotations
|
|
1285
|
+
// -------------------------------------------------------------------------
|
|
1286
|
+
/** Sets the note written under the chart ("" removes it). */
|
|
1287
|
+
setNote(note) {
|
|
1288
|
+
return this.update({ note }, { label: note ? "Edit note" : "Remove note" });
|
|
1289
|
+
}
|
|
1290
|
+
/**
|
|
1291
|
+
* Draws the named categories (labels) and series (names or ids) normally and mutes the
|
|
1292
|
+
* rest; an empty list shows everything. Unknown names change nothing. Returns the category
|
|
1293
|
+
* indexes and series ids that matched.
|
|
1294
|
+
*/
|
|
1295
|
+
setHighlight(names) {
|
|
1296
|
+
const s = this.spec;
|
|
1297
|
+
const list = [...new Set(names.map(String))];
|
|
1298
|
+
const unknown = list.filter((n) => !s.categories.includes(n) && !s.series.some((x) => x.id === n || x.name === n));
|
|
1299
|
+
if (unknown.length)
|
|
1300
|
+
return this.reject(
|
|
1301
|
+
`${unknown.map((u) => `"${u}"`).join(", ")} ${unknown.length === 1 ? "is" : "are"} neither a category nor a series. Categories: ${s.categories.slice(0, 20).map((c) => `"${c}"`).join(", ") || "(none)"}; series: ${s.series.map((x) => `"${x.name}"`).join(", ") || "(none)"}.`
|
|
1302
|
+
);
|
|
1303
|
+
const result = this.update({ highlight: list }, { label: list.length ? "Highlight" : "Clear highlight" });
|
|
1304
|
+
if (!result.ok) return result;
|
|
1305
|
+
return {
|
|
1306
|
+
ok: true,
|
|
1307
|
+
categories: s.categories.flatMap((c, i) => list.includes(c) ? [i] : []),
|
|
1308
|
+
series: s.series.filter((x) => list.includes(x.id) || list.includes(x.name)).map((x) => x.id)
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
/** Why a reference on `axis` at `values` cannot be drawn on this chart, or null. */
|
|
1312
|
+
referenceProblem(axis, values) {
|
|
1313
|
+
const s = this.spec;
|
|
1314
|
+
const family = chartFamily(s);
|
|
1315
|
+
if (family !== "cartesian" && family !== "xy") return `Reference lines and bands are drawn on bar, line, area, scatter and bubble charts; this is a ${s.type} chart.`;
|
|
1316
|
+
if (axis === "x" && family !== "xy") return 'axis "x" needs a scatter or bubble chart with numeric x values; use "y" for the value axis.';
|
|
1317
|
+
if (axis === "y2" && (family !== "cartesian" || !s.series.some((x) => !x.hidden && x.axis === "secondary")))
|
|
1318
|
+
return 'axis "y2" needs a visible series on the secondary axis (set one with update_chart_series axis "secondary"), or use "y".';
|
|
1319
|
+
const scale = (axis === "x" ? s.xAxis : axis === "y2" ? s.yAxis2 : s.yAxis).scale;
|
|
1320
|
+
if (scale === "log" && values.some((v) => !(v > 0))) return "That axis is logarithmic, so the values must be above 0.";
|
|
1321
|
+
return null;
|
|
1322
|
+
}
|
|
1323
|
+
editReferences(kind, label, edit, select) {
|
|
1324
|
+
this.commit(
|
|
1325
|
+
label,
|
|
1326
|
+
this.edited((d) => edit(d[kind])),
|
|
1327
|
+
select
|
|
1328
|
+
);
|
|
1329
|
+
}
|
|
1330
|
+
/** Adds a reference line (a target, floor or average). Returns its index. */
|
|
1331
|
+
addReferenceLine(input) {
|
|
1332
|
+
const issues = [];
|
|
1333
|
+
const line = normalizeChartReferenceLine(input, "referenceLine", issues);
|
|
1334
|
+
if (!line || issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
|
|
1335
|
+
const problem = this.referenceProblem(line.axis, [line.value]);
|
|
1336
|
+
if (problem) return this.reject(problem);
|
|
1337
|
+
const index = this.spec.referenceLines.length;
|
|
1338
|
+
this.editReferences("referenceLines", `Add reference line${line.label ? ` "${line.label}"` : ""}`, (list) => list.push(line));
|
|
1339
|
+
return { ok: true, index };
|
|
1340
|
+
}
|
|
1341
|
+
/** Changes a reference line (all-or-nothing); `null` removes its label, colour or axis. */
|
|
1342
|
+
updateReferenceLine(index, patch) {
|
|
1343
|
+
const current = this.spec.referenceLines[index];
|
|
1344
|
+
if (!current) return fail(this.noReference("reference line", this.spec.referenceLines.length, index));
|
|
1345
|
+
const merged = { ...current, ...patch };
|
|
1346
|
+
for (const [k, v] of Object.entries(patch)) if (v === null) delete merged[k];
|
|
1347
|
+
const issues = [];
|
|
1348
|
+
const line = normalizeChartReferenceLine(merged, "referenceLine", issues);
|
|
1349
|
+
if (!line || issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
|
|
1350
|
+
const problem = this.referenceProblem(line.axis, [line.value]);
|
|
1351
|
+
if (problem) return this.reject(problem);
|
|
1352
|
+
this.editReferences("referenceLines", "Change reference line", (list) => list[index] = line);
|
|
1353
|
+
return { ok: true };
|
|
1354
|
+
}
|
|
1355
|
+
removeReferenceLine(index) {
|
|
1356
|
+
if (!this.spec.referenceLines[index]) return fail(this.noReference("reference line", this.spec.referenceLines.length, index));
|
|
1357
|
+
this.editReferences("referenceLines", "Delete reference line", (list) => list.splice(index, 1));
|
|
1358
|
+
return { ok: true };
|
|
1359
|
+
}
|
|
1360
|
+
/** Adds a reference band (a target range). Returns its index. */
|
|
1361
|
+
addReferenceBand(input) {
|
|
1362
|
+
const issues = [];
|
|
1363
|
+
const band = normalizeChartReferenceBand(input, "referenceBand", issues);
|
|
1364
|
+
if (!band || issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
|
|
1365
|
+
const problem = this.referenceProblem(band.axis, [band.from, band.to]);
|
|
1366
|
+
if (problem) return this.reject(problem);
|
|
1367
|
+
const index = this.spec.referenceBands.length;
|
|
1368
|
+
this.editReferences("referenceBands", `Add reference band${band.label ? ` "${band.label}"` : ""}`, (list) => list.push(band));
|
|
1369
|
+
return { ok: true, index };
|
|
1370
|
+
}
|
|
1371
|
+
/** Changes a reference band (all-or-nothing); `null` removes its label, colour or axis. */
|
|
1372
|
+
updateReferenceBand(index, patch) {
|
|
1373
|
+
const current = this.spec.referenceBands[index];
|
|
1374
|
+
if (!current) return fail(this.noReference("reference band", this.spec.referenceBands.length, index));
|
|
1375
|
+
const merged = { ...current, ...patch };
|
|
1376
|
+
for (const [k, v] of Object.entries(patch)) if (v === null) delete merged[k];
|
|
1377
|
+
const issues = [];
|
|
1378
|
+
const band = normalizeChartReferenceBand(merged, "referenceBand", issues);
|
|
1379
|
+
if (!band || issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
|
|
1380
|
+
const problem = this.referenceProblem(band.axis, [band.from, band.to]);
|
|
1381
|
+
if (problem) return this.reject(problem);
|
|
1382
|
+
this.editReferences("referenceBands", "Change reference band", (list) => list[index] = band);
|
|
1383
|
+
return { ok: true };
|
|
1384
|
+
}
|
|
1385
|
+
removeReferenceBand(index) {
|
|
1386
|
+
if (!this.spec.referenceBands[index]) return fail(this.noReference("reference band", this.spec.referenceBands.length, index));
|
|
1387
|
+
this.editReferences("referenceBands", "Delete reference band", (list) => list.splice(index, 1));
|
|
1388
|
+
return { ok: true };
|
|
1389
|
+
}
|
|
1390
|
+
noReference(what, count, index) {
|
|
1391
|
+
return `There is no ${what} ${index}; ${count ? `expected 0 to ${count - 1}` : `the chart has none yet`}.`;
|
|
1392
|
+
}
|
|
1393
|
+
/**
|
|
1394
|
+
* Pins a label to one bar or point (`null` or "" removes it) and selects the point. The
|
|
1395
|
+
* point must have a value, on a bar, line, area, scatter or bubble chart.
|
|
1396
|
+
*/
|
|
1397
|
+
setCallout(seriesId, index, label) {
|
|
1398
|
+
const series = this.getSeries(seriesId);
|
|
1399
|
+
if (!series) return this.unknownSeries(seriesId);
|
|
1400
|
+
if (!Number.isInteger(index) || index < 0 || index >= this.spec.categories.length) return this.badIndex(index);
|
|
1401
|
+
const family = chartFamily(this.spec);
|
|
1402
|
+
const text = label === null ? "" : String(label).trim();
|
|
1403
|
+
if (text && family !== "cartesian" && family !== "xy") return this.reject(`Callouts are drawn on bar, line, area, scatter and bubble charts; this is a ${this.spec.type} chart.`);
|
|
1404
|
+
if (text && (series.values[index] === null || series.values[index] === void 0))
|
|
1405
|
+
return this.reject(`"${series.name}" has no value at "${this.spec.categories[index]}", so there is nothing to point at; set a value first.`);
|
|
1406
|
+
const next = this.edited((d) => {
|
|
1407
|
+
const s = d.series.find((x) => x.id === seriesId);
|
|
1408
|
+
const labels = s.callouts ? [...s.callouts] : d.categories.map(() => null);
|
|
1409
|
+
labels[index] = text || null;
|
|
1410
|
+
if (labels.some((l) => l)) s.callouts = labels;
|
|
1411
|
+
else delete s.callouts;
|
|
1412
|
+
});
|
|
1413
|
+
this.commit(text ? "Add callout" : "Remove callout", next, { kind: "point", seriesId, index });
|
|
1414
|
+
return { ok: true };
|
|
1415
|
+
}
|
|
1416
|
+
// -------------------------------------------------------------------------
|
|
1101
1417
|
// Trendlines
|
|
1102
1418
|
// -------------------------------------------------------------------------
|
|
1103
1419
|
/** Adds a trendline to a series and selects it. Returns its index (and the fit, when it can be computed). */
|
|
@@ -1182,6 +1498,8 @@ var ChartController = class {
|
|
|
1182
1498
|
* undo step). Series colours, types and other per-series settings are dropped.
|
|
1183
1499
|
*/
|
|
1184
1500
|
transpose() {
|
|
1501
|
+
const grouped = this.treeLocked();
|
|
1502
|
+
if (grouped) return grouped;
|
|
1185
1503
|
const s = this.spec;
|
|
1186
1504
|
if (!s.series.length || !s.categories.length) return this.reject("There is no data to switch: the chart needs at least one series and one category.");
|
|
1187
1505
|
const used = /* @__PURE__ */ new Set();
|
|
@@ -1235,6 +1553,8 @@ var ChartController = class {
|
|
|
1235
1553
|
}
|
|
1236
1554
|
/** Adds `delta` to a value (a gap counts as 0). */
|
|
1237
1555
|
nudgeValue(seriesId, index, delta) {
|
|
1556
|
+
const grouped = this.treeLocked();
|
|
1557
|
+
if (grouped) return grouped;
|
|
1238
1558
|
const series = this.getSeries(seriesId);
|
|
1239
1559
|
if (!series) return this.unknownSeries(seriesId);
|
|
1240
1560
|
const current = series.values[index] ?? 0;
|
|
@@ -1343,6 +1663,18 @@ function updateLabel(patch) {
|
|
|
1343
1663
|
overlap: "Change bar overlap",
|
|
1344
1664
|
bins: "Change histogram bins",
|
|
1345
1665
|
showTotal: "Toggle waterfall total",
|
|
1666
|
+
note: "Edit note",
|
|
1667
|
+
highlight: "Change highlight",
|
|
1668
|
+
referenceLines: "Change reference lines",
|
|
1669
|
+
referenceBands: "Change reference bands",
|
|
1670
|
+
varyColors: "Toggle colour by category",
|
|
1671
|
+
likertNeutral: "Change neutral answer",
|
|
1672
|
+
likertSort: "Sort questions",
|
|
1673
|
+
parliamentArc: "Change hemicycle sweep",
|
|
1674
|
+
parliamentMajority: "Change majority",
|
|
1675
|
+
parliamentLabel: "Edit seat caption",
|
|
1676
|
+
sunburstLabel: "Edit centre caption",
|
|
1677
|
+
tree: "Replace grouped data",
|
|
1346
1678
|
radarTicks: "Change radar rings",
|
|
1347
1679
|
radarGraticule: "Change radar grid",
|
|
1348
1680
|
legend: "Change legend",
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
CHART_OOXML_NS,
|
|
9
9
|
chartFromOoxml
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-POR54B55.js";
|
|
11
11
|
import {
|
|
12
12
|
compareDocuments
|
|
13
13
|
} from "./chunk-V5OOP74P.js";
|
|
@@ -34,10 +34,10 @@ import {
|
|
|
34
34
|
searchDocument,
|
|
35
35
|
sliceContainsTable,
|
|
36
36
|
syncInitialContent
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-OXERWBKE.js";
|
|
38
38
|
import {
|
|
39
39
|
chartDataToTable
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-3NEZJCBY.js";
|
|
41
41
|
import {
|
|
42
42
|
PIXERATE_DOCUMENT_MIME,
|
|
43
43
|
getClipboardData,
|
|
@@ -2358,12 +2358,15 @@ var WetMedium = (_class2 = class {
|
|
|
2358
2358
|
sample(rect, cov) {
|
|
2359
2359
|
const s = this.world.sample(this.laneId, rect, cov);
|
|
2360
2360
|
if (!(s.wet > 0) || !(s.weight > 0)) return null;
|
|
2361
|
-
const
|
|
2362
|
-
const
|
|
2361
|
+
const k0 = s.pigment[0] + s.settled[0];
|
|
2362
|
+
const k1 = s.pigment[1] + s.settled[1];
|
|
2363
|
+
const k2 = s.pigment[2] + s.settled[2];
|
|
2364
|
+
const k3 = s.pigment[3] + s.settled[3];
|
|
2365
|
+
const kmax = Math.max(k0, k1, k2);
|
|
2363
2366
|
if (!(kmax > 1e-9)) return null;
|
|
2364
2367
|
const f = SAMPLE_K / kmax;
|
|
2365
|
-
const rgb = pigmentColor(
|
|
2366
|
-
const density = Math.min(1, (
|
|
2368
|
+
const rgb = pigmentColor(k0 * f, k1 * f, k2 * f, k3 * f);
|
|
2369
|
+
const density = Math.min(1, (k0 + k1 + k2) / s.wet / 0.06);
|
|
2367
2370
|
return { rgb, contact: s.wet / s.weight * density };
|
|
2368
2371
|
}
|
|
2369
2372
|
deposit(rect, cov, dep, paint, _pigment, field = null) {
|
|
@@ -74,7 +74,7 @@ function profileAlongPolyline(points, values, count = 32) {
|
|
|
74
74
|
var round3 = (v) => Math.round(v * 1e3) / 1e3;
|
|
75
75
|
var loading = null;
|
|
76
76
|
function loadCanvasBrushstrokeRenderer() {
|
|
77
|
-
loading ??= Promise.resolve().then(() => _interopRequireWildcard(require("./brushstrokeRender-
|
|
77
|
+
loading ??= Promise.resolve().then(() => _interopRequireWildcard(require("./brushstrokeRender-VO4ZTWVA.cjs"))).catch((error) => {
|
|
78
78
|
loading = null;
|
|
79
79
|
throw error;
|
|
80
80
|
});
|