@trebco/treb 23.6.2 → 25.0.0-rc1
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/.eslintignore +8 -0
- package/.eslintrc.js +164 -0
- package/README-shadow-DOM.md +88 -0
- package/README.md +37 -130
- package/api-config.json +29 -0
- package/api-generator/api-generator-types.ts +82 -0
- package/api-generator/api-generator.ts +1172 -0
- package/api-generator/package.json +3 -0
- package/build/treb-spreadsheet.mjs +14 -0
- package/{treb.d.ts → build/treb.d.ts} +293 -299
- package/esbuild-custom-element.mjs +336 -0
- package/esbuild.js +305 -0
- package/package.json +43 -14
- package/treb-base-types/package.json +5 -0
- package/treb-base-types/src/api_types.ts +36 -0
- package/treb-base-types/src/area.ts +583 -0
- package/treb-base-types/src/basic_types.ts +45 -0
- package/treb-base-types/src/cell.ts +612 -0
- package/treb-base-types/src/cells.ts +1066 -0
- package/treb-base-types/src/color.ts +124 -0
- package/treb-base-types/src/import.ts +71 -0
- package/treb-base-types/src/index-standalone.ts +29 -0
- package/treb-base-types/src/index.ts +42 -0
- package/treb-base-types/src/layout.ts +47 -0
- package/treb-base-types/src/localization.ts +187 -0
- package/treb-base-types/src/rectangle.ts +145 -0
- package/treb-base-types/src/render_text.ts +72 -0
- package/treb-base-types/src/style.ts +545 -0
- package/treb-base-types/src/table.ts +109 -0
- package/treb-base-types/src/text_part.ts +54 -0
- package/treb-base-types/src/theme.ts +608 -0
- package/treb-base-types/src/union.ts +152 -0
- package/treb-base-types/src/value-type.ts +164 -0
- package/treb-base-types/style/resizable.css +59 -0
- package/treb-calculator/modern.tsconfig.json +11 -0
- package/treb-calculator/package.json +5 -0
- package/treb-calculator/src/calculator.ts +2546 -0
- package/treb-calculator/src/complex-math.ts +558 -0
- package/treb-calculator/src/dag/array-vertex.ts +198 -0
- package/treb-calculator/src/dag/graph.ts +951 -0
- package/treb-calculator/src/dag/leaf_vertex.ts +118 -0
- package/treb-calculator/src/dag/spreadsheet_vertex.ts +327 -0
- package/treb-calculator/src/dag/spreadsheet_vertex_base.ts +44 -0
- package/treb-calculator/src/dag/vertex.ts +352 -0
- package/treb-calculator/src/descriptors.ts +162 -0
- package/treb-calculator/src/expression-calculator.ts +1069 -0
- package/treb-calculator/src/function-error.ts +103 -0
- package/treb-calculator/src/function-library.ts +103 -0
- package/treb-calculator/src/functions/base-functions.ts +1214 -0
- package/treb-calculator/src/functions/checkbox.ts +164 -0
- package/treb-calculator/src/functions/complex-functions.ts +253 -0
- package/treb-calculator/src/functions/finance-functions.ts +399 -0
- package/treb-calculator/src/functions/information-functions.ts +102 -0
- package/treb-calculator/src/functions/matrix-functions.ts +182 -0
- package/treb-calculator/src/functions/sparkline.ts +335 -0
- package/treb-calculator/src/functions/statistics-functions.ts +350 -0
- package/treb-calculator/src/functions/text-functions.ts +298 -0
- package/treb-calculator/src/index.ts +27 -0
- package/treb-calculator/src/notifier-types.ts +59 -0
- package/treb-calculator/src/primitives.ts +428 -0
- package/treb-calculator/src/utilities.ts +305 -0
- package/treb-charts/package.json +5 -0
- package/treb-charts/src/chart-functions.ts +156 -0
- package/treb-charts/src/chart-types.ts +230 -0
- package/treb-charts/src/chart.ts +1288 -0
- package/treb-charts/src/index.ts +24 -0
- package/treb-charts/src/main.ts +37 -0
- package/treb-charts/src/rectangle.ts +52 -0
- package/treb-charts/src/renderer.ts +1841 -0
- package/treb-charts/src/util.ts +122 -0
- package/treb-charts/style/charts.scss +221 -0
- package/treb-charts/style/old-charts.scss +250 -0
- package/treb-embed/markup/layout.html +137 -0
- package/treb-embed/markup/toolbar.html +175 -0
- package/treb-embed/modern.tsconfig.json +25 -0
- package/treb-embed/src/custom-element/content-types.d.ts +18 -0
- package/treb-embed/src/custom-element/global.d.ts +11 -0
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +1227 -0
- package/treb-embed/src/custom-element/treb-global.ts +44 -0
- package/treb-embed/src/custom-element/treb-spreadsheet-element.ts +52 -0
- package/treb-embed/src/embedded-spreadsheet.ts +5362 -0
- package/treb-embed/src/index.ts +16 -0
- package/treb-embed/src/language-model.ts +41 -0
- package/treb-embed/src/options.ts +320 -0
- package/treb-embed/src/progress-dialog.ts +228 -0
- package/treb-embed/src/selection-state.ts +16 -0
- package/treb-embed/src/spinner.ts +42 -0
- package/treb-embed/src/toolbar-message.ts +96 -0
- package/treb-embed/src/types.ts +167 -0
- package/treb-embed/style/autocomplete.scss +103 -0
- package/treb-embed/style/dark-theme.scss +114 -0
- package/treb-embed/style/defaults.scss +36 -0
- package/treb-embed/style/dialog.scss +181 -0
- package/treb-embed/style/dropdown-select.scss +101 -0
- package/treb-embed/style/formula-bar.scss +193 -0
- package/treb-embed/style/grid.scss +374 -0
- package/treb-embed/style/layout.scss +424 -0
- package/treb-embed/style/mouse-mask.scss +67 -0
- package/treb-embed/style/note.scss +92 -0
- package/treb-embed/style/overlay-editor.scss +102 -0
- package/treb-embed/style/spinner.scss +92 -0
- package/treb-embed/style/tab-bar.scss +228 -0
- package/treb-embed/style/table.scss +80 -0
- package/treb-embed/style/theme-defaults.scss +444 -0
- package/treb-embed/style/toolbar.scss +416 -0
- package/treb-embed/style/tooltip.scss +68 -0
- package/treb-embed/style/treb-icons.scss +130 -0
- package/treb-embed/style/treb-spreadsheet-element.scss +20 -0
- package/treb-embed/style/z-index.scss +43 -0
- package/treb-export/docs/charts.md +68 -0
- package/treb-export/modern.tsconfig.json +19 -0
- package/treb-export/package.json +4 -0
- package/treb-export/src/address-type.ts +77 -0
- package/treb-export/src/base-template.ts +22 -0
- package/treb-export/src/column-width.ts +85 -0
- package/treb-export/src/drawing2/chart-template-components2.ts +389 -0
- package/treb-export/src/drawing2/chart2.ts +282 -0
- package/treb-export/src/drawing2/column-chart-template2.ts +521 -0
- package/treb-export/src/drawing2/donut-chart-template2.ts +296 -0
- package/treb-export/src/drawing2/drawing2.ts +355 -0
- package/treb-export/src/drawing2/embedded-image.ts +71 -0
- package/treb-export/src/drawing2/scatter-chart-template2.ts +555 -0
- package/treb-export/src/export-worker/export-worker.ts +99 -0
- package/treb-export/src/export-worker/index-modern.ts +22 -0
- package/treb-export/src/export2.ts +2204 -0
- package/treb-export/src/import2.ts +882 -0
- package/treb-export/src/relationship.ts +36 -0
- package/treb-export/src/shared-strings2.ts +128 -0
- package/treb-export/src/template-2.ts +22 -0
- package/treb-export/src/unescape_xml.ts +47 -0
- package/treb-export/src/workbook-sheet2.ts +182 -0
- package/treb-export/src/workbook-style2.ts +1285 -0
- package/treb-export/src/workbook-theme2.ts +88 -0
- package/treb-export/src/workbook2.ts +491 -0
- package/treb-export/src/xml-utils.ts +201 -0
- package/treb-export/template/base/[Content_Types].xml +2 -0
- package/treb-export/template/base/_rels/.rels +2 -0
- package/treb-export/template/base/docProps/app.xml +2 -0
- package/treb-export/template/base/docProps/core.xml +12 -0
- package/treb-export/template/base/xl/_rels/workbook.xml.rels +2 -0
- package/treb-export/template/base/xl/sharedStrings.xml +2 -0
- package/treb-export/template/base/xl/styles.xml +2 -0
- package/treb-export/template/base/xl/theme/theme1.xml +2 -0
- package/treb-export/template/base/xl/workbook.xml +2 -0
- package/treb-export/template/base/xl/worksheets/sheet1.xml +2 -0
- package/treb-export/template/base.xlsx +0 -0
- package/treb-format/package.json +8 -0
- package/treb-format/src/format.test.ts +213 -0
- package/treb-format/src/format.ts +942 -0
- package/treb-format/src/format_cache.ts +199 -0
- package/treb-format/src/format_parser.ts +723 -0
- package/treb-format/src/index.ts +25 -0
- package/treb-format/src/number_format_section.ts +100 -0
- package/treb-format/src/value_parser.ts +337 -0
- package/treb-grid/package.json +5 -0
- package/treb-grid/src/editors/autocomplete.ts +394 -0
- package/treb-grid/src/editors/autocomplete_matcher.ts +260 -0
- package/treb-grid/src/editors/formula_bar.ts +473 -0
- package/treb-grid/src/editors/formula_editor_base.ts +910 -0
- package/treb-grid/src/editors/overlay_editor.ts +511 -0
- package/treb-grid/src/index.ts +37 -0
- package/treb-grid/src/layout/base_layout.ts +2618 -0
- package/treb-grid/src/layout/grid_layout.ts +299 -0
- package/treb-grid/src/layout/rectangle_cache.ts +86 -0
- package/treb-grid/src/render/selection-renderer.ts +414 -0
- package/treb-grid/src/render/svg_header_overlay.ts +93 -0
- package/treb-grid/src/render/svg_selection_block.ts +187 -0
- package/treb-grid/src/render/tile_renderer.ts +2122 -0
- package/treb-grid/src/types/annotation.ts +216 -0
- package/treb-grid/src/types/border_constants.ts +34 -0
- package/treb-grid/src/types/clipboard_data.ts +31 -0
- package/treb-grid/src/types/data_model.ts +334 -0
- package/treb-grid/src/types/drag_mask.ts +81 -0
- package/treb-grid/src/types/grid.ts +7743 -0
- package/treb-grid/src/types/grid_base.ts +3644 -0
- package/treb-grid/src/types/grid_command.ts +470 -0
- package/treb-grid/src/types/grid_events.ts +124 -0
- package/treb-grid/src/types/grid_options.ts +97 -0
- package/treb-grid/src/types/grid_selection.ts +60 -0
- package/treb-grid/src/types/named_range.ts +369 -0
- package/treb-grid/src/types/scale-control.ts +202 -0
- package/treb-grid/src/types/serialize_options.ts +72 -0
- package/treb-grid/src/types/set_range_options.ts +52 -0
- package/treb-grid/src/types/sheet.ts +3099 -0
- package/treb-grid/src/types/sheet_types.ts +95 -0
- package/treb-grid/src/types/tab_bar.ts +464 -0
- package/treb-grid/src/types/tile.ts +59 -0
- package/treb-grid/src/types/update_flags.ts +75 -0
- package/treb-grid/src/util/dom_utilities.ts +44 -0
- package/treb-grid/src/util/fontmetrics2.ts +179 -0
- package/treb-grid/src/util/ua.ts +104 -0
- package/treb-logo.svg +18 -0
- package/treb-parser/package.json +5 -0
- package/treb-parser/src/csv-parser.ts +122 -0
- package/treb-parser/src/index.ts +25 -0
- package/treb-parser/src/md-parser.ts +526 -0
- package/treb-parser/src/parser-types.ts +397 -0
- package/treb-parser/src/parser.test.ts +298 -0
- package/treb-parser/src/parser.ts +2673 -0
- package/treb-utils/package.json +5 -0
- package/treb-utils/src/dispatch.ts +57 -0
- package/treb-utils/src/event_source.ts +147 -0
- package/treb-utils/src/ievent_source.ts +33 -0
- package/treb-utils/src/index.ts +31 -0
- package/treb-utils/src/measurement.ts +174 -0
- package/treb-utils/src/resizable.ts +160 -0
- package/treb-utils/src/scale.ts +137 -0
- package/treb-utils/src/serialize_html.ts +124 -0
- package/treb-utils/src/template.ts +70 -0
- package/treb-utils/src/validate_uri.ts +61 -0
- package/tsconfig.json +10 -0
- package/tsproject.json +30 -0
- package/util/license-plugin-esbuild.js +86 -0
- package/util/list-css-vars.sh +46 -0
- package/README-esm.md +0 -37
- package/treb-bundle.css +0 -2
- package/treb-bundle.mjs +0 -15
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of TREB.
|
|
3
|
+
*
|
|
4
|
+
* TREB is free software: you can redistribute it and/or modify it under the
|
|
5
|
+
* terms of the GNU General Public License as published by the Free Software
|
|
6
|
+
* Foundation, either version 3 of the License, or (at your option) any
|
|
7
|
+
* later version.
|
|
8
|
+
*
|
|
9
|
+
* TREB is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
10
|
+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
11
|
+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
12
|
+
* details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU General Public License along
|
|
15
|
+
* with TREB. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*
|
|
17
|
+
* Copyright 2022-2023 trebco, llc.
|
|
18
|
+
* info@treb.app
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* moved from sparkline module -- not sure why this needed a separate
|
|
24
|
+
* module. although on reflection it might be better placed in the charts
|
|
25
|
+
* module? (...)
|
|
26
|
+
*
|
|
27
|
+
* perhaps not because we use that module elsewhere and this one is only
|
|
28
|
+
* ever used in spreadsheets.
|
|
29
|
+
*
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
import type { Cell, Style } from 'treb-base-types';
|
|
34
|
+
|
|
35
|
+
export interface SparklineRenderOptions {
|
|
36
|
+
context: CanvasRenderingContext2D;
|
|
37
|
+
width: number;
|
|
38
|
+
height: number;
|
|
39
|
+
cell: Cell;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
enum LineOperation {
|
|
43
|
+
move,
|
|
44
|
+
line,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class Sparkline {
|
|
48
|
+
|
|
49
|
+
// public static SingleColor = ['#333'];
|
|
50
|
+
// public static TwoColors = ['green', 'red'];
|
|
51
|
+
|
|
52
|
+
public static readonly default_color = '#888'; // should never be used, but jic
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* three possible cases:
|
|
56
|
+
*
|
|
57
|
+
* (1) array of numbers, possibly including missing values
|
|
58
|
+
* (2) typed array
|
|
59
|
+
* (3) nested array, so we need to recurse and concatenate
|
|
60
|
+
*
|
|
61
|
+
* there's actually one more case, where data comes in from serialized
|
|
62
|
+
* representation (loading file); in that case it's an object with numeric
|
|
63
|
+
* indexes (sadly)
|
|
64
|
+
*/
|
|
65
|
+
protected static UnpackValues(underlying: any): number[] {
|
|
66
|
+
|
|
67
|
+
if (Array.isArray(underlying)) {
|
|
68
|
+
const test = underlying[0];
|
|
69
|
+
if (Array.isArray(test) || test instanceof Float64Array || test instanceof Float32Array) {
|
|
70
|
+
return underlying.reduce((a, subset) => a.concat(this.UnpackValues(subset)), []);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
return underlying.map(test => isNaN(test) ? undefined : test);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else if (underlying instanceof Float32Array || underlying instanceof Float64Array) {
|
|
77
|
+
return Array.prototype.slice.call(underlying);
|
|
78
|
+
}
|
|
79
|
+
else if (underlying && typeof underlying === 'object') {
|
|
80
|
+
|
|
81
|
+
const keys = Object.keys(underlying);
|
|
82
|
+
const len = keys.length;
|
|
83
|
+
|
|
84
|
+
// this is maybe overdoing it? (...) there's probably a smarter test
|
|
85
|
+
// if (keys.every(key => !isNaN(Number(key)))) {
|
|
86
|
+
|
|
87
|
+
// check first, last
|
|
88
|
+
if (typeof underlying['0'] !== 'undefined' && typeof underlying[(len - 1).toString()] !== 'undefined') {
|
|
89
|
+
const data: number[] = [];
|
|
90
|
+
|
|
91
|
+
// we probably don't have to explicitly use strings -- although it's not
|
|
92
|
+
// clear that it would be any faster because someone still has to do the
|
|
93
|
+
// conversion
|
|
94
|
+
|
|
95
|
+
for (let i = 0; i < len; i++) {
|
|
96
|
+
data[i] = underlying[i.toString()];
|
|
97
|
+
}
|
|
98
|
+
return data;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return [];
|
|
104
|
+
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
protected static SparklineCommon(cell: Cell, style: Style.Properties): {
|
|
108
|
+
values: Array<number|undefined>,
|
|
109
|
+
colors: string[],
|
|
110
|
+
} {
|
|
111
|
+
|
|
112
|
+
// the cell function echoes back arguments. the first argument
|
|
113
|
+
// should be an array, but it will be 2D...
|
|
114
|
+
|
|
115
|
+
let values: Array<number|undefined> = [];
|
|
116
|
+
|
|
117
|
+
// use text color, or default. because this is called from renderer,
|
|
118
|
+
// theme default _should_ always be passed in, so we should (theoretically)
|
|
119
|
+
// never need our default.
|
|
120
|
+
|
|
121
|
+
const text_color = style.text?.text || this.default_color;
|
|
122
|
+
const colors: string[] = [ text_color, text_color ];
|
|
123
|
+
|
|
124
|
+
if (Array.isArray(cell.calculated)) {
|
|
125
|
+
values = this.UnpackValues(cell.calculated[0]);
|
|
126
|
+
if (typeof cell.calculated[1] === 'string') {
|
|
127
|
+
colors[0] = cell.calculated[1];
|
|
128
|
+
}
|
|
129
|
+
if (typeof cell.calculated[2] === 'string') {
|
|
130
|
+
colors[1] = cell.calculated[2];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return { values, colors };
|
|
135
|
+
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
public static RenderLine(
|
|
139
|
+
width: number,
|
|
140
|
+
height: number,
|
|
141
|
+
context: CanvasRenderingContext2D,
|
|
142
|
+
cell: Cell,
|
|
143
|
+
style: Style.Properties,
|
|
144
|
+
): void {
|
|
145
|
+
|
|
146
|
+
const {values, colors} = this.SparklineCommon(cell, style);
|
|
147
|
+
|
|
148
|
+
const x_margin = 0.05; // FIXME: parameterize? (...)
|
|
149
|
+
const y_margin = 0.10;
|
|
150
|
+
|
|
151
|
+
let line_width = 1;
|
|
152
|
+
if (Array.isArray(cell.calculated) && typeof cell.calculated[2] === 'number') {
|
|
153
|
+
line_width = cell.calculated[2];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
let min = 0;
|
|
157
|
+
let max = 0;
|
|
158
|
+
let first_index = -1;
|
|
159
|
+
|
|
160
|
+
for (let i = 0; i < values.length; i++) {
|
|
161
|
+
const value = values[i];
|
|
162
|
+
if (typeof value === 'number') {
|
|
163
|
+
if (first_index >= 0) {
|
|
164
|
+
min = Math.min(min, value);
|
|
165
|
+
max = Math.max(max, value);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
first_index = i;
|
|
169
|
+
min = max = value;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (min !== max) {
|
|
175
|
+
|
|
176
|
+
const step = (width * (1 - 2 * x_margin)) / (values.length - 1);
|
|
177
|
+
const range = max - min;
|
|
178
|
+
const pixel_range = height * (1 - 2 * y_margin); // ?
|
|
179
|
+
const base = height * y_margin;
|
|
180
|
+
|
|
181
|
+
context.strokeStyle = colors[0];
|
|
182
|
+
context.lineWidth = line_width;
|
|
183
|
+
context.lineCap = 'round';
|
|
184
|
+
context.lineJoin = 'round';
|
|
185
|
+
|
|
186
|
+
// let x = width * x_margin + step * first_index;
|
|
187
|
+
// let y = height - ((values[first_index] as number) - min) * pixel_range / range - base;
|
|
188
|
+
|
|
189
|
+
context.beginPath();
|
|
190
|
+
|
|
191
|
+
let op = LineOperation.move;
|
|
192
|
+
|
|
193
|
+
for (let i = first_index; i < values.length; i++) {
|
|
194
|
+
const value = values[i];
|
|
195
|
+
if (typeof value === 'number') {
|
|
196
|
+
const x = width * x_margin + step * i;
|
|
197
|
+
const y = height - (value - min) * pixel_range / range - base;
|
|
198
|
+
if (op === LineOperation.move) {
|
|
199
|
+
context.moveTo(x, y);
|
|
200
|
+
op = LineOperation.line;
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
context.lineTo(x, y);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
op = LineOperation.move;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
context.stroke();
|
|
212
|
+
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
public static RenderColumn(
|
|
219
|
+
width: number,
|
|
220
|
+
height: number,
|
|
221
|
+
context: CanvasRenderingContext2D,
|
|
222
|
+
cell: Cell,
|
|
223
|
+
style: Style.Properties,
|
|
224
|
+
): void {
|
|
225
|
+
|
|
226
|
+
const {values, colors} = this.SparklineCommon(cell, style);
|
|
227
|
+
|
|
228
|
+
// const x_margin = 0.05; // FIXME: parameterize? (...)
|
|
229
|
+
// const y_margin = 0.10;
|
|
230
|
+
// const y_margin = Math.max(1, Math.min(0.10 * height, 2));
|
|
231
|
+
|
|
232
|
+
const x_margin = 3;
|
|
233
|
+
const y_margin = 2.5;
|
|
234
|
+
|
|
235
|
+
let min = 0;
|
|
236
|
+
let max = 0;
|
|
237
|
+
let first_value = false;
|
|
238
|
+
|
|
239
|
+
for (const value of values) {
|
|
240
|
+
if (typeof value === 'number') {
|
|
241
|
+
if (first_value) {
|
|
242
|
+
min = Math.min(min, value);
|
|
243
|
+
max = Math.max(max, value);
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
first_value = true;
|
|
247
|
+
min = max = value;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (values.length) {
|
|
253
|
+
|
|
254
|
+
// const step = (width - 2 * x_margin - 2) / (values.length-1);
|
|
255
|
+
const step = (width - 2 * x_margin - 2) / (values.length-0);
|
|
256
|
+
const pixel_range = (height - 2 * y_margin); // ?
|
|
257
|
+
const base = y_margin;
|
|
258
|
+
|
|
259
|
+
// let x = Math.round(width * x_margin);
|
|
260
|
+
|
|
261
|
+
if (min !== max) {
|
|
262
|
+
|
|
263
|
+
if (min < 0 && max > 0) {
|
|
264
|
+
|
|
265
|
+
const range = max - min;
|
|
266
|
+
const zero = base + max / range * pixel_range;
|
|
267
|
+
|
|
268
|
+
// use an indexed loop so we can multiply to get x instead of adding
|
|
269
|
+
for (let i = 0; i < values.length; i++) {
|
|
270
|
+
const value = values[i];
|
|
271
|
+
if (typeof value === 'number') {
|
|
272
|
+
const x = (x_margin + i * step);
|
|
273
|
+
const bar_height = (Math.abs(value) / range) * pixel_range;
|
|
274
|
+
|
|
275
|
+
if (value >= 0) {
|
|
276
|
+
context.fillStyle = colors[0];
|
|
277
|
+
const top = zero - bar_height;
|
|
278
|
+
context.fillRect(x + 2, top, step - 2, bar_height);
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
context.fillStyle = colors[1];
|
|
282
|
+
const top = zero;
|
|
283
|
+
context.fillRect(x + 2, top, step - 2, bar_height);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
}
|
|
290
|
+
else if (max > 0) {
|
|
291
|
+
|
|
292
|
+
// all positive
|
|
293
|
+
|
|
294
|
+
context.fillStyle = colors[0];
|
|
295
|
+
const range = max - min;
|
|
296
|
+
|
|
297
|
+
// use an indexed loop so we can multiply to get x instead of adding
|
|
298
|
+
for (let i = 0; i < values.length; i++) {
|
|
299
|
+
const value = values[i];
|
|
300
|
+
if (typeof value === 'number') {
|
|
301
|
+
const x = (x_margin + i * step);
|
|
302
|
+
const bar_height = Math.max(1, ((value - min) / range) * pixel_range);
|
|
303
|
+
const top = height - base - bar_height;
|
|
304
|
+
context.fillRect(x + 2, top, step - 2, bar_height);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
|
|
311
|
+
// all negative
|
|
312
|
+
|
|
313
|
+
context.fillStyle = colors[1];
|
|
314
|
+
const range = max - min;
|
|
315
|
+
|
|
316
|
+
// use an indexed loop so we can multiply to get x instead of adding
|
|
317
|
+
for (let i = 0; i < values.length; i++) {
|
|
318
|
+
const value = values[i];
|
|
319
|
+
if (typeof value === 'number') {
|
|
320
|
+
const x = (x_margin + i * step);
|
|
321
|
+
const bar_height = Math.max( 1, (Math.abs(max - value) / range) * pixel_range);
|
|
322
|
+
const top = base;
|
|
323
|
+
context.fillRect(x + 2, top, step - 2, bar_height);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of TREB.
|
|
3
|
+
*
|
|
4
|
+
* TREB is free software: you can redistribute it and/or modify it under the
|
|
5
|
+
* terms of the GNU General Public License as published by the Free Software
|
|
6
|
+
* Foundation, either version 3 of the License, or (at your option) any
|
|
7
|
+
* later version.
|
|
8
|
+
*
|
|
9
|
+
* TREB is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
10
|
+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
11
|
+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
12
|
+
* details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU General Public License along
|
|
15
|
+
* with TREB. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*
|
|
17
|
+
* Copyright 2022-2023 trebco, llc.
|
|
18
|
+
* info@treb.app
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type { FunctionMap } from '../descriptors';
|
|
23
|
+
import * as Utils from '../utilities';
|
|
24
|
+
import { ValueError, ArgumentError, NAError } from '../function-error';
|
|
25
|
+
import { Complex, UnionValue, ValueType } from 'treb-base-types';
|
|
26
|
+
import * as ComplexMath from '../complex-math';
|
|
27
|
+
|
|
28
|
+
export const Variance = (data: number[], sample = false) => {
|
|
29
|
+
|
|
30
|
+
const len = data.length;
|
|
31
|
+
|
|
32
|
+
let m = 0;
|
|
33
|
+
let v = 0;
|
|
34
|
+
// let k = 0;
|
|
35
|
+
// let s = 0;
|
|
36
|
+
|
|
37
|
+
for (let i = 0; i < len; i++) m += data[i];
|
|
38
|
+
|
|
39
|
+
m /= len;
|
|
40
|
+
// const mean = m;
|
|
41
|
+
|
|
42
|
+
for (let i = 0; i < len; i++) {
|
|
43
|
+
const d = data[i] - m;
|
|
44
|
+
v += (d * d);
|
|
45
|
+
// s += (d * d * d);
|
|
46
|
+
// k += (d * d * d * d);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// const N = len;
|
|
50
|
+
// const variance = v / len;
|
|
51
|
+
// const stdev = Math.sqrt(v / len);
|
|
52
|
+
return sample ? (v / (len-1)) : (v/len);
|
|
53
|
+
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const StatisticsFunctionLibrary: FunctionMap = {
|
|
57
|
+
|
|
58
|
+
'StDev.P': {
|
|
59
|
+
description: 'Returns the standard deviation of a set of values, corresponding to a population',
|
|
60
|
+
arguments: [{ name: 'data', }],
|
|
61
|
+
fn: (...args: any[]): UnionValue => {
|
|
62
|
+
return { type: ValueType.number, value: Math.sqrt(Variance(Utils.FlattenUnboxed(args), false)) };
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
'StDev.S': {
|
|
67
|
+
description: 'Returns the standard deviation of a set of values, corresponding to a sample of a population',
|
|
68
|
+
arguments: [{ name: 'data', }],
|
|
69
|
+
fn: (...args: any[]): UnionValue => {
|
|
70
|
+
return { type: ValueType.number, value: Math.sqrt(Variance(Utils.FlattenUnboxed(args), true)) };
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
'Var.P': {
|
|
75
|
+
description: 'Returns the variance of a set of values, corresponding to a population',
|
|
76
|
+
arguments: [{ name: 'data', }],
|
|
77
|
+
fn: (...args: any[]): UnionValue => {
|
|
78
|
+
return { type: ValueType.number, value: Variance(Utils.FlattenUnboxed(args), false) };
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
'Var.S': {
|
|
83
|
+
description: 'Returns the variance of a set of values, corresponding to a sample of a population',
|
|
84
|
+
arguments: [{ name: 'data', }],
|
|
85
|
+
fn: (...args: any[]): UnionValue => {
|
|
86
|
+
return { type: ValueType.number, value: Variance(Utils.FlattenUnboxed(args), true) };
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
Covar: {
|
|
91
|
+
description: 'Returns the covariance between two ranges of values',
|
|
92
|
+
arguments: [{
|
|
93
|
+
name: 'A',
|
|
94
|
+
}, {
|
|
95
|
+
name: 'B',
|
|
96
|
+
}],
|
|
97
|
+
fn: (x: any[], y: any[]): UnionValue => {
|
|
98
|
+
|
|
99
|
+
// both must be 2d arrays, we're assuming the same or mostly similar shape
|
|
100
|
+
if (!Array.isArray(x) || !Array.isArray(y)) { return ValueError(); }
|
|
101
|
+
if (!Array.isArray(x[0]) || !Array.isArray(y[0])) { return ValueError(); }
|
|
102
|
+
|
|
103
|
+
if (x.length !== y.length) {
|
|
104
|
+
return ArgumentError();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let sum = 0;
|
|
108
|
+
let length = 0;
|
|
109
|
+
|
|
110
|
+
const mean = { x: 0, y: 0 };
|
|
111
|
+
const data: { x: number[], y: number[] } = { x: [], y: [] };
|
|
112
|
+
|
|
113
|
+
for (let j = 0; j < x.length; j++) {
|
|
114
|
+
|
|
115
|
+
if (!x[j] || !y[j]) { continue; }
|
|
116
|
+
if (x[j].length !== y[j].length) {
|
|
117
|
+
return ArgumentError();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const len = x[j].length;
|
|
121
|
+
length += len;
|
|
122
|
+
|
|
123
|
+
for (let i = 0; i < len; i++) {
|
|
124
|
+
mean.x += (x[j][i] || 0);
|
|
125
|
+
mean.y += (y[j][i] || 0);
|
|
126
|
+
|
|
127
|
+
data.x.push(x[j][i] || 0);
|
|
128
|
+
data.y.push(y[j][i] || 0);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (length === 0) {
|
|
134
|
+
return NAError();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
mean.x /= length;
|
|
138
|
+
mean.y /= length;
|
|
139
|
+
|
|
140
|
+
for (let i = 0; i < length; i++) {
|
|
141
|
+
sum += (data.x[i] - mean.x) * (data.y[i] - mean.y);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return { type: ValueType.number, value: sum / length, };
|
|
145
|
+
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
Correl: {
|
|
150
|
+
description: 'Returns the correlation between two ranges of values',
|
|
151
|
+
arguments: [{
|
|
152
|
+
name: 'A',
|
|
153
|
+
}, {
|
|
154
|
+
name: 'B',
|
|
155
|
+
}],
|
|
156
|
+
fn: (x: any[], y: any[]): UnionValue => {
|
|
157
|
+
|
|
158
|
+
// both must be 2d arrays, we're assuming the same or mostly similar shape
|
|
159
|
+
if (!Array.isArray(x) || !Array.isArray(y)) { return ValueError(); }
|
|
160
|
+
if (!Array.isArray(x[0]) || !Array.isArray(y[0])) { return ValueError(); }
|
|
161
|
+
|
|
162
|
+
let rslt = 0;
|
|
163
|
+
let sumProduct = 0;
|
|
164
|
+
let sumX = 0;
|
|
165
|
+
let sumY = 0;
|
|
166
|
+
let sumSquaredX = 0;
|
|
167
|
+
let sumSquaredY = 0;
|
|
168
|
+
let count = 0;
|
|
169
|
+
|
|
170
|
+
for (let j = 0; j < x.length; j++) {
|
|
171
|
+
if (!x[j] || !y[j]) { continue; }
|
|
172
|
+
|
|
173
|
+
const len = x[j].length;
|
|
174
|
+
for (let i = 0; i < len; i++) {
|
|
175
|
+
const a = Number(x[j][i]);
|
|
176
|
+
const b = Number(y[j][i]);
|
|
177
|
+
|
|
178
|
+
if (isNaN(a) || isNaN(b)) { continue; }
|
|
179
|
+
|
|
180
|
+
sumProduct += (a * b);
|
|
181
|
+
sumX += a;
|
|
182
|
+
sumY += b;
|
|
183
|
+
sumSquaredX += (a * a);
|
|
184
|
+
sumSquaredY += (b * b);
|
|
185
|
+
count++;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
rslt = ((count * sumProduct) - (sumX * sumY));
|
|
190
|
+
if (rslt) {
|
|
191
|
+
rslt /= Math.sqrt(((count * sumSquaredX) - (sumX * sumX)) * ((count * sumSquaredY) - (sumY * sumY)));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return { type: ValueType.number, value: rslt };
|
|
195
|
+
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
GeoMean: {
|
|
200
|
+
|
|
201
|
+
description: 'Returns the geometric mean of all numeric arguments',
|
|
202
|
+
arguments: [{ boxed: true }],
|
|
203
|
+
|
|
204
|
+
fn: (...args: any[]): UnionValue => {
|
|
205
|
+
|
|
206
|
+
args = Utils.FlattenBoxed(args);
|
|
207
|
+
|
|
208
|
+
let count = 0;
|
|
209
|
+
let product: Complex = {real: 1, imaginary: 0};
|
|
210
|
+
let complex = false;
|
|
211
|
+
let negative = false;
|
|
212
|
+
|
|
213
|
+
for (const arg of args as UnionValue[]) {
|
|
214
|
+
|
|
215
|
+
if (arg.type === ValueType.complex) {
|
|
216
|
+
complex = true;
|
|
217
|
+
product = ComplexMath.Multiply(product, arg.value);
|
|
218
|
+
count++;
|
|
219
|
+
}
|
|
220
|
+
else if (arg.type === ValueType.number) {
|
|
221
|
+
if (arg.value < 0) {
|
|
222
|
+
negative = true;
|
|
223
|
+
}
|
|
224
|
+
count++;
|
|
225
|
+
// product = ComplexMath.Multiply(product, {real: arg.value, imaginary: 0});
|
|
226
|
+
product.real *= arg.value;
|
|
227
|
+
product.imaginary *= arg.value;
|
|
228
|
+
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (complex) {
|
|
234
|
+
const value = ComplexMath.Power(product, {real: 1/count, imaginary: 0});
|
|
235
|
+
if (value.imaginary) {
|
|
236
|
+
return { type: ValueType.complex, value };
|
|
237
|
+
}
|
|
238
|
+
return { type: ValueType.number, value: value.real };
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
if (negative) {
|
|
242
|
+
return ValueError();
|
|
243
|
+
}
|
|
244
|
+
return { type: ValueType.number, value: Math.pow(product.real, 1 / count) };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/*
|
|
248
|
+
for (const arg of args) {
|
|
249
|
+
if (typeof arg === 'undefined') { continue; }
|
|
250
|
+
const value = Number(arg);
|
|
251
|
+
if (value < 0) { return ValueError(); }
|
|
252
|
+
count++;
|
|
253
|
+
product *= value;
|
|
254
|
+
}
|
|
255
|
+
return { type: ValueType.number, value: Math.pow(product, 1 / count) };
|
|
256
|
+
*/
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
Average: {
|
|
264
|
+
|
|
265
|
+
description: 'Returns the arithmetic mean of all numeric arguments',
|
|
266
|
+
arguments: [{ boxed: true }],
|
|
267
|
+
|
|
268
|
+
fn: (...args: any[]): UnionValue => {
|
|
269
|
+
args = Utils.FlattenBoxed(args);
|
|
270
|
+
|
|
271
|
+
const result = { real: 0, imaginary: 0 };
|
|
272
|
+
let count = 0;
|
|
273
|
+
|
|
274
|
+
for (const ref of args as UnionValue[]) {
|
|
275
|
+
if (ref.type === ValueType.error) {
|
|
276
|
+
return ref;
|
|
277
|
+
}
|
|
278
|
+
if (ref.type === ValueType.number) {
|
|
279
|
+
result.real += ref.value;
|
|
280
|
+
count++;
|
|
281
|
+
}
|
|
282
|
+
if (ref.type === ValueType.complex) {
|
|
283
|
+
result.real += ref.value.real;
|
|
284
|
+
result.imaginary += ref.value.imaginary;
|
|
285
|
+
count++;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
result.real /= count;
|
|
290
|
+
result.imaginary /= count;
|
|
291
|
+
|
|
292
|
+
if (result.imaginary) {
|
|
293
|
+
return { type: ValueType.complex, value: result, };
|
|
294
|
+
}
|
|
295
|
+
return { type: ValueType.number, value: result.real };
|
|
296
|
+
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
|
|
300
|
+
Percentile: {
|
|
301
|
+
description: 'Returns the kth percentile value from the range of data',
|
|
302
|
+
arguments: [
|
|
303
|
+
{ name: 'range' },
|
|
304
|
+
{ name: 'percentile' },
|
|
305
|
+
],
|
|
306
|
+
fn: (range: number[][], percentile: number): UnionValue => {
|
|
307
|
+
|
|
308
|
+
const flat = Utils.FlattenUnboxed(range).filter((test) => typeof test === 'number');
|
|
309
|
+
flat.sort((a, b) => a - b);
|
|
310
|
+
const n = flat.length;
|
|
311
|
+
|
|
312
|
+
// try to stabilize this number
|
|
313
|
+
const factor = Math.pow(10,8);
|
|
314
|
+
const x = Math.round((1 + (n-1) * percentile) * factor)/factor;
|
|
315
|
+
|
|
316
|
+
const lo = Math.floor(x);
|
|
317
|
+
const hi = Math.ceil(x);
|
|
318
|
+
|
|
319
|
+
return { type: ValueType.number, value: (flat[lo-1] + flat[hi-1]) / 2 };
|
|
320
|
+
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
Median: {
|
|
325
|
+
description: 'Returns the median value of the range of data',
|
|
326
|
+
arguments: [
|
|
327
|
+
{ name: 'range' },
|
|
328
|
+
],
|
|
329
|
+
fn: (...args: any[]): UnionValue => {
|
|
330
|
+
|
|
331
|
+
const flat = Utils.FlattenUnboxed(args).filter((test) => typeof test === 'number');
|
|
332
|
+
flat.sort((a, b) => a - b);
|
|
333
|
+
const n = flat.length;
|
|
334
|
+
|
|
335
|
+
const x = 1 + (n-1) * .5;
|
|
336
|
+
const lo = Math.floor(x);
|
|
337
|
+
const hi = Math.ceil(x);
|
|
338
|
+
|
|
339
|
+
return { type: ValueType.number, value: (flat[lo-1] + flat[hi-1]) / 2 };
|
|
340
|
+
|
|
341
|
+
},
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
export const StatisticsFunctionAliases: {[index: string]: string} = {
|
|
347
|
+
Mean: 'Average',
|
|
348
|
+
'StDev': 'StDev.S',
|
|
349
|
+
'Var': 'Var.S',
|
|
350
|
+
};
|