@reekon-tools/boldr-utils 1.4.16 → 1.4.17
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.
|
@@ -102,24 +102,6 @@ export const calculateFormula = (formula, formulas, columns, tableConfig, measur
|
|
|
102
102
|
currentMappings[variable] = mapping;
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
|
-
// Validate that all required inputs are filled out
|
|
106
|
-
const missingInputs = [];
|
|
107
|
-
for (const [variable, mapping] of Object.entries(currentMappings)) {
|
|
108
|
-
const mappingObj = typeof mapping === 'string'
|
|
109
|
-
? { id: mapping, type: 'column' }
|
|
110
|
-
: mapping;
|
|
111
|
-
// Only check column references (formula references are handled separately)
|
|
112
|
-
if (mappingObj.type === 'column' || !mappingObj.type) {
|
|
113
|
-
const columnId = mappingObj.id;
|
|
114
|
-
if (!(columnId in valueMap)) {
|
|
115
|
-
missingInputs.push(variable);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
if (missingInputs.length > 0) {
|
|
120
|
-
console.warn(`Not all inputs are filled out. Missing values for: ${missingInputs.join(', ')}`);
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
105
|
const result = evaluateFormula({
|
|
124
106
|
expression: formula.expression,
|
|
125
107
|
mappings: currentMappings,
|
|
@@ -1,19 +1,52 @@
|
|
|
1
1
|
import { Units } from '../types/firestore.js';
|
|
2
2
|
import { create, all } from 'mathjs';
|
|
3
|
-
// Create a custom mathjs instance with degree-based trigonometric functions
|
|
4
|
-
const math = create(all);
|
|
5
3
|
// Helper to convert degrees to radians
|
|
6
4
|
const degToRad = (degrees) => (degrees * Math.PI) / 180;
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
5
|
+
// Helper to convert radians to degrees
|
|
6
|
+
const radToDeg = (radians) => (radians * 180) / Math.PI;
|
|
7
|
+
// Create a custom mathjs instance with degree-based trigonometric functions
|
|
8
|
+
const math = create(all);
|
|
9
|
+
// Create degree-based trigonometric functions that accept degrees as input
|
|
10
|
+
// and return results in degrees for inverse functions
|
|
11
|
+
// These functions handle mathjs types by converting to numbers
|
|
12
|
+
const degreeTrigFunctions = {
|
|
13
|
+
sin: (x) => {
|
|
14
|
+
const degrees = typeof x === 'number' ? x : Number(x);
|
|
15
|
+
return Math.sin(degToRad(degrees));
|
|
16
|
+
},
|
|
17
|
+
cos: (x) => {
|
|
18
|
+
const degrees = typeof x === 'number' ? x : Number(x);
|
|
19
|
+
return Math.cos(degToRad(degrees));
|
|
20
|
+
},
|
|
21
|
+
tan: (x) => {
|
|
22
|
+
const degrees = typeof x === 'number' ? x : Number(x);
|
|
23
|
+
return Math.tan(degToRad(degrees));
|
|
24
|
+
},
|
|
25
|
+
asin: (x) => {
|
|
26
|
+
const value = typeof x === 'number' ? x : Number(x);
|
|
27
|
+
return radToDeg(Math.asin(value));
|
|
28
|
+
},
|
|
29
|
+
acos: (x) => {
|
|
30
|
+
const value = typeof x === 'number' ? x : Number(x);
|
|
31
|
+
return radToDeg(Math.acos(value));
|
|
32
|
+
},
|
|
33
|
+
atan: (x) => {
|
|
34
|
+
const value = typeof x === 'number' ? x : Number(x);
|
|
35
|
+
return radToDeg(Math.atan(value));
|
|
36
|
+
},
|
|
37
|
+
atan2: (y, x) => {
|
|
38
|
+
const yVal = typeof y === 'number' ? y : Number(y);
|
|
39
|
+
const xVal = typeof x === 'number' ? x : Number(x);
|
|
40
|
+
return radToDeg(Math.atan2(yVal, xVal));
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
// Override the trigonometric functions in mathjs using import
|
|
44
|
+
// The override: true option ensures existing functions are replaced
|
|
45
|
+
math.import(degreeTrigFunctions, { override: true });
|
|
46
|
+
// Verify the functions are overridden
|
|
47
|
+
if (typeof math.sin !== 'function') {
|
|
48
|
+
console.warn('Warning: math.sin override may have failed');
|
|
49
|
+
}
|
|
17
50
|
const compile = math.compile.bind(math);
|
|
18
51
|
const mathUnit = math.unit.bind(math);
|
|
19
52
|
// Cache for evaluated formulas to improve performance
|
|
@@ -136,6 +169,30 @@ export function evaluateFormula({ expression, mappings, valueMap, unit, formulas
|
|
|
136
169
|
}
|
|
137
170
|
evaluationStack.add(formulaId);
|
|
138
171
|
try {
|
|
172
|
+
// Validate that all required inputs are available before building scope
|
|
173
|
+
const missingInputs = [];
|
|
174
|
+
for (const [variable, rawMapping] of Object.entries(currentMappings)) {
|
|
175
|
+
const mapping = normalizeMapping(rawMapping);
|
|
176
|
+
if (mapping.type === 'measurement') {
|
|
177
|
+
// Check if column reference exists in valueMap
|
|
178
|
+
if (!(mapping.id in valueMap)) {
|
|
179
|
+
missingInputs.push(variable);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else if (mapping.type === 'formula') {
|
|
183
|
+
// Check if formula reference exists
|
|
184
|
+
const referencedFormula = formulas.find((f) => f.id === mapping.id);
|
|
185
|
+
if (!referencedFormula) {
|
|
186
|
+
missingInputs.push(variable);
|
|
187
|
+
}
|
|
188
|
+
// Note: We don't validate nested formula inputs here to avoid double evaluation
|
|
189
|
+
// The nested formula will validate its own inputs when evaluated
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// If any direct inputs are missing, throw an error
|
|
193
|
+
if (missingInputs.length > 0) {
|
|
194
|
+
throw new Error(`Missing required inputs for formula ${formulaId}: ${missingInputs.join(', ')}`);
|
|
195
|
+
}
|
|
139
196
|
const scope = {};
|
|
140
197
|
const normalizedUnit = normalizeUnitForMathJS(unit);
|
|
141
198
|
// Build scope by resolving each variable
|
|
@@ -143,6 +200,7 @@ export function evaluateFormula({ expression, mappings, valueMap, unit, formulas
|
|
|
143
200
|
const mapping = normalizeMapping(rawMapping);
|
|
144
201
|
if (mapping.type === 'measurement') {
|
|
145
202
|
// Handle measurement/column reference
|
|
203
|
+
// At this point we know it exists (validated above), but use ?? 0 as fallback
|
|
146
204
|
const micrometers = valueMap[mapping.id] ?? 0;
|
|
147
205
|
if (micrometers === 0) {
|
|
148
206
|
console.warn(`⚠️ Zero or missing value for mapping ID "${mapping.id}"`);
|
|
@@ -157,6 +215,7 @@ export function evaluateFormula({ expression, mappings, valueMap, unit, formulas
|
|
|
157
215
|
throw new Error(`Referenced formula not found: ${mapping.id}`);
|
|
158
216
|
}
|
|
159
217
|
// Recursively evaluate the referenced formula
|
|
218
|
+
// If it has missing inputs, it will throw an error which we'll catch
|
|
160
219
|
const nestedResult = evaluateNestedFormula(referencedFormula.id, referencedFormula.expression, referencedFormula.variableToColumnMap);
|
|
161
220
|
// Convert result to current unit with error handling
|
|
162
221
|
let resultInUnit;
|
|
@@ -233,8 +292,14 @@ export function evaluateFormula({ expression, mappings, valueMap, unit, formulas
|
|
|
233
292
|
return result;
|
|
234
293
|
}
|
|
235
294
|
catch (err) {
|
|
295
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
296
|
+
// If the error is about missing inputs, return null to match the behavior in temp.ts
|
|
297
|
+
if (errorMessage.includes('Missing required inputs') || errorMessage.includes('missing')) {
|
|
298
|
+
console.warn('❌ Formula evaluation failed due to missing inputs:', errorMessage);
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
236
301
|
console.warn('❌ Formula evaluation failed:', err);
|
|
237
|
-
return
|
|
302
|
+
return errorMessage;
|
|
238
303
|
}
|
|
239
304
|
}
|
|
240
305
|
// Legacy createFormulaScope function for backward compatibility
|