@reekon-tools/boldr-utils 1.4.15 → 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.
|
@@ -1,5 +1,54 @@
|
|
|
1
1
|
import { Units } from '../types/firestore.js';
|
|
2
|
-
import {
|
|
2
|
+
import { create, all } from 'mathjs';
|
|
3
|
+
// Helper to convert degrees to radians
|
|
4
|
+
const degToRad = (degrees) => (degrees * Math.PI) / 180;
|
|
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
|
+
}
|
|
50
|
+
const compile = math.compile.bind(math);
|
|
51
|
+
const mathUnit = math.unit.bind(math);
|
|
3
52
|
// Cache for evaluated formulas to improve performance
|
|
4
53
|
const formulaCache = new Map();
|
|
5
54
|
function normalizeUnitForMathJS(unit) {
|
|
@@ -120,6 +169,30 @@ export function evaluateFormula({ expression, mappings, valueMap, unit, formulas
|
|
|
120
169
|
}
|
|
121
170
|
evaluationStack.add(formulaId);
|
|
122
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
|
+
}
|
|
123
196
|
const scope = {};
|
|
124
197
|
const normalizedUnit = normalizeUnitForMathJS(unit);
|
|
125
198
|
// Build scope by resolving each variable
|
|
@@ -127,6 +200,7 @@ export function evaluateFormula({ expression, mappings, valueMap, unit, formulas
|
|
|
127
200
|
const mapping = normalizeMapping(rawMapping);
|
|
128
201
|
if (mapping.type === 'measurement') {
|
|
129
202
|
// Handle measurement/column reference
|
|
203
|
+
// At this point we know it exists (validated above), but use ?? 0 as fallback
|
|
130
204
|
const micrometers = valueMap[mapping.id] ?? 0;
|
|
131
205
|
if (micrometers === 0) {
|
|
132
206
|
console.warn(`⚠️ Zero or missing value for mapping ID "${mapping.id}"`);
|
|
@@ -141,6 +215,7 @@ export function evaluateFormula({ expression, mappings, valueMap, unit, formulas
|
|
|
141
215
|
throw new Error(`Referenced formula not found: ${mapping.id}`);
|
|
142
216
|
}
|
|
143
217
|
// Recursively evaluate the referenced formula
|
|
218
|
+
// If it has missing inputs, it will throw an error which we'll catch
|
|
144
219
|
const nestedResult = evaluateNestedFormula(referencedFormula.id, referencedFormula.expression, referencedFormula.variableToColumnMap);
|
|
145
220
|
// Convert result to current unit with error handling
|
|
146
221
|
let resultInUnit;
|
|
@@ -217,8 +292,14 @@ export function evaluateFormula({ expression, mappings, valueMap, unit, formulas
|
|
|
217
292
|
return result;
|
|
218
293
|
}
|
|
219
294
|
catch (err) {
|
|
220
|
-
|
|
221
|
-
return
|
|
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
|
+
}
|
|
301
|
+
console.warn('❌ Formula evaluation failed:', err);
|
|
302
|
+
return errorMessage;
|
|
222
303
|
}
|
|
223
304
|
}
|
|
224
305
|
// Legacy createFormulaScope function for backward compatibility
|