@teachinglab/omd 0.6.0 → 0.6.2
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 +257 -251
- package/README.old.md +137 -137
- package/canvas/core/canvasConfig.js +202 -202
- package/canvas/drawing/segment.js +167 -167
- package/canvas/drawing/stroke.js +385 -385
- package/canvas/events/eventManager.js +444 -444
- package/canvas/events/pointerEventHandler.js +262 -262
- package/canvas/index.js +48 -48
- package/canvas/tools/PointerTool.js +71 -71
- package/canvas/tools/tool.js +222 -222
- package/canvas/utils/boundingBox.js +377 -377
- package/canvas/utils/mathUtils.js +258 -258
- package/docs/api/configuration-options.md +198 -198
- package/docs/api/eventManager.md +82 -82
- package/docs/api/focusFrameManager.md +144 -144
- package/docs/api/index.md +105 -105
- package/docs/api/main.md +62 -62
- package/docs/api/omdBinaryExpressionNode.md +86 -86
- package/docs/api/omdCanvas.md +83 -83
- package/docs/api/omdConfigManager.md +112 -112
- package/docs/api/omdConstantNode.md +52 -52
- package/docs/api/omdDisplay.md +87 -87
- package/docs/api/omdEquationNode.md +174 -174
- package/docs/api/omdEquationSequenceNode.md +258 -258
- package/docs/api/omdEquationStack.md +192 -192
- package/docs/api/omdFunctionNode.md +82 -82
- package/docs/api/omdGroupNode.md +78 -78
- package/docs/api/omdHelpers.md +87 -87
- package/docs/api/omdLeafNode.md +85 -85
- package/docs/api/omdNode.md +201 -201
- package/docs/api/omdOperationDisplayNode.md +117 -117
- package/docs/api/omdOperatorNode.md +91 -91
- package/docs/api/omdParenthesisNode.md +133 -133
- package/docs/api/omdPopup.md +191 -191
- package/docs/api/omdPowerNode.md +131 -131
- package/docs/api/omdRationalNode.md +144 -144
- package/docs/api/omdSequenceNode.md +128 -128
- package/docs/api/omdSimplification.md +78 -78
- package/docs/api/omdSqrtNode.md +144 -144
- package/docs/api/omdStepVisualizer.md +146 -146
- package/docs/api/omdStepVisualizerHighlighting.md +65 -65
- package/docs/api/omdStepVisualizerInteractiveSteps.md +108 -108
- package/docs/api/omdStepVisualizerLayout.md +70 -70
- package/docs/api/omdStepVisualizerNodeUtils.md +140 -140
- package/docs/api/omdStepVisualizerTextBoxes.md +76 -76
- package/docs/api/omdToolbar.md +130 -130
- package/docs/api/omdTranscriptionService.md +95 -95
- package/docs/api/omdTreeDiff.md +169 -169
- package/docs/api/omdUnaryExpressionNode.md +137 -137
- package/docs/api/omdUtilities.md +82 -82
- package/docs/api/omdVariableNode.md +123 -123
- package/docs/api/selectTool.md +74 -74
- package/docs/api/simplificationEngine.md +97 -97
- package/docs/api/simplificationRules.md +76 -76
- package/docs/api/simplificationUtils.md +64 -64
- package/docs/api/transcribe.md +43 -43
- package/docs/api-reference.md +85 -85
- package/docs/index.html +453 -453
- package/docs/index.md +38 -38
- package/docs/omd-objects.md +258 -258
- package/index.js +79 -79
- package/jsvg/index.js +3 -0
- package/jsvg/jsvg.js +898 -898
- package/jsvg/jsvgComponents.js +357 -358
- package/npm-docs/DOCUMENTATION_SUMMARY.md +220 -220
- package/npm-docs/README.md +251 -251
- package/npm-docs/api/api-reference.md +85 -85
- package/npm-docs/api/configuration-options.md +198 -198
- package/npm-docs/api/eventManager.md +82 -82
- package/npm-docs/api/expression-nodes.md +561 -561
- package/npm-docs/api/focusFrameManager.md +144 -144
- package/npm-docs/api/index.md +105 -105
- package/npm-docs/api/main.md +62 -62
- package/npm-docs/api/omdBinaryExpressionNode.md +86 -86
- package/npm-docs/api/omdCanvas.md +83 -83
- package/npm-docs/api/omdConfigManager.md +112 -112
- package/npm-docs/api/omdConstantNode.md +52 -52
- package/npm-docs/api/omdDisplay.md +87 -87
- package/npm-docs/api/omdEquationNode.md +174 -174
- package/npm-docs/api/omdEquationSequenceNode.md +258 -258
- package/npm-docs/api/omdEquationStack.md +192 -192
- package/npm-docs/api/omdFunctionNode.md +82 -82
- package/npm-docs/api/omdGroupNode.md +78 -78
- package/npm-docs/api/omdHelpers.md +87 -87
- package/npm-docs/api/omdLeafNode.md +85 -85
- package/npm-docs/api/omdNode.md +201 -201
- package/npm-docs/api/omdOperationDisplayNode.md +117 -117
- package/npm-docs/api/omdOperatorNode.md +91 -91
- package/npm-docs/api/omdParenthesisNode.md +133 -133
- package/npm-docs/api/omdPopup.md +191 -191
- package/npm-docs/api/omdPowerNode.md +131 -131
- package/npm-docs/api/omdRationalNode.md +144 -144
- package/npm-docs/api/omdSequenceNode.md +128 -128
- package/npm-docs/api/omdSimplification.md +78 -78
- package/npm-docs/api/omdSqrtNode.md +144 -144
- package/npm-docs/api/omdStepVisualizer.md +146 -146
- package/npm-docs/api/omdStepVisualizerHighlighting.md +65 -65
- package/npm-docs/api/omdStepVisualizerInteractiveSteps.md +108 -108
- package/npm-docs/api/omdStepVisualizerLayout.md +70 -70
- package/npm-docs/api/omdStepVisualizerNodeUtils.md +140 -140
- package/npm-docs/api/omdStepVisualizerTextBoxes.md +76 -76
- package/npm-docs/api/omdToolbar.md +130 -130
- package/npm-docs/api/omdTranscriptionService.md +95 -95
- package/npm-docs/api/omdTreeDiff.md +169 -169
- package/npm-docs/api/omdUnaryExpressionNode.md +137 -137
- package/npm-docs/api/omdUtilities.md +82 -82
- package/npm-docs/api/omdVariableNode.md +123 -123
- package/npm-docs/api/selectTool.md +74 -74
- package/npm-docs/api/simplificationEngine.md +97 -97
- package/npm-docs/api/simplificationRules.md +76 -76
- package/npm-docs/api/simplificationUtils.md +64 -64
- package/npm-docs/api/transcribe.md +43 -43
- package/npm-docs/guides/equations.md +854 -854
- package/npm-docs/guides/factory-functions.md +354 -354
- package/npm-docs/guides/getting-started.md +318 -318
- package/npm-docs/guides/quick-examples.md +525 -525
- package/npm-docs/guides/visualizations.md +682 -682
- package/npm-docs/index.html +12 -0
- package/npm-docs/json-schemas.md +826 -826
- package/omd/config/omdConfigManager.js +279 -267
- package/omd/core/index.js +158 -158
- package/omd/core/omdEquationStack.js +546 -546
- package/omd/core/omdUtilities.js +113 -113
- package/omd/display/omdDisplay.js +969 -962
- package/omd/display/omdToolbar.js +501 -501
- package/omd/nodes/omdBinaryExpressionNode.js +459 -459
- package/omd/nodes/omdConstantNode.js +141 -141
- package/omd/nodes/omdEquationNode.js +1327 -1327
- package/omd/nodes/omdFunctionNode.js +351 -351
- package/omd/nodes/omdGroupNode.js +67 -67
- package/omd/nodes/omdLeafNode.js +76 -76
- package/omd/nodes/omdNode.js +556 -556
- package/omd/nodes/omdOperationDisplayNode.js +321 -321
- package/omd/nodes/omdOperatorNode.js +108 -108
- package/omd/nodes/omdParenthesisNode.js +292 -292
- package/omd/nodes/omdPowerNode.js +235 -235
- package/omd/nodes/omdRationalNode.js +295 -295
- package/omd/nodes/omdSqrtNode.js +307 -307
- package/omd/nodes/omdUnaryExpressionNode.js +227 -227
- package/omd/nodes/omdVariableNode.js +122 -122
- package/omd/simplification/omdSimplification.js +140 -140
- package/omd/simplification/omdSimplificationEngine.js +887 -887
- package/omd/simplification/package.json +5 -5
- package/omd/simplification/rules/binaryRules.js +1037 -1037
- package/omd/simplification/rules/functionRules.js +111 -111
- package/omd/simplification/rules/index.js +48 -48
- package/omd/simplification/rules/parenthesisRules.js +19 -19
- package/omd/simplification/rules/powerRules.js +143 -143
- package/omd/simplification/rules/rationalRules.js +725 -725
- package/omd/simplification/rules/sqrtRules.js +48 -48
- package/omd/simplification/rules/unaryRules.js +37 -37
- package/omd/simplification/simplificationRules.js +31 -31
- package/omd/simplification/simplificationUtils.js +1055 -1055
- package/omd/step-visualizer/omdStepVisualizer.js +947 -947
- package/omd/step-visualizer/omdStepVisualizerHighlighting.js +246 -246
- package/omd/step-visualizer/omdStepVisualizerLayout.js +892 -892
- package/omd/step-visualizer/omdStepVisualizerTextBoxes.js +200 -200
- package/omd/utils/aiNextEquationStep.js +106 -106
- package/omd/utils/omdNodeOverlay.js +638 -638
- package/omd/utils/omdPopup.js +1203 -1203
- package/omd/utils/omdStepVisualizerInteractiveSteps.js +684 -684
- package/omd/utils/omdStepVisualizerNodeUtils.js +267 -267
- package/omd/utils/omdTranscriptionService.js +123 -123
- package/omd/utils/omdTreeDiff.js +733 -733
- package/package.json +59 -56
- package/readme.html +184 -120
- package/src/index.js +74 -74
- package/src/json-schemas.md +576 -576
- package/src/omd-json-samples.js +147 -147
- package/src/omdApp.js +391 -391
- package/src/omdAppCanvas.js +335 -335
- package/src/omdBalanceHanger.js +199 -199
- package/src/omdColor.js +13 -13
- package/src/omdCoordinatePlane.js +541 -541
- package/src/omdExpression.js +115 -115
- package/src/omdFactory.js +150 -150
- package/src/omdFunction.js +114 -114
- package/src/omdMetaExpression.js +290 -290
- package/src/omdNaturalExpression.js +563 -563
- package/src/omdNode.js +383 -383
- package/src/omdNumber.js +52 -52
- package/src/omdNumberLine.js +114 -112
- package/src/omdNumberTile.js +118 -118
- package/src/omdOperator.js +72 -72
- package/src/omdPowerExpression.js +91 -91
- package/src/omdProblem.js +259 -259
- package/src/omdRatioChart.js +251 -251
- package/src/omdRationalExpression.js +114 -114
- package/src/omdSampleData.js +215 -215
- package/src/omdShapes.js +512 -512
- package/src/omdSpinner.js +151 -151
- package/src/omdString.js +49 -49
- package/src/omdTable.js +498 -498
- package/src/omdTapeDiagram.js +244 -244
- package/src/omdTerm.js +91 -91
- package/src/omdTileEquation.js +349 -349
- package/src/omdUtils.js +84 -84
- package/src/omdVariable.js +51 -51
|
@@ -1,228 +1,228 @@
|
|
|
1
|
-
import { omdNode } from "./omdNode.js";
|
|
2
|
-
import { getNodeForAST } from "../core/omdUtilities.js";
|
|
3
|
-
import { omdOperatorNode } from "./omdOperatorNode.js";
|
|
4
|
-
import { omdConstantNode } from "./omdConstantNode.js";
|
|
5
|
-
import { omdBinaryExpressionNode } from "./omdBinaryExpressionNode.js";
|
|
6
|
-
import { simplifyStep } from "../simplification/omdSimplification.js";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Represents a unary expression, like negation (-(x+y)).
|
|
10
|
-
* @extends omdNode
|
|
11
|
-
*/
|
|
12
|
-
export class omdUnaryExpressionNode extends omdNode {
|
|
13
|
-
/**
|
|
14
|
-
* @param {Object} ast - The AST node from math.js.
|
|
15
|
-
*/
|
|
16
|
-
constructor(ast) {
|
|
17
|
-
super(ast);
|
|
18
|
-
this.type = "omdUnaryExpressionNode";
|
|
19
|
-
if (!ast.args || ast.args.length !== 1) {
|
|
20
|
-
console.error("omdUnaryExpressionNode requires an AST node with exactly 1 argument", ast);
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
this.op = this.createOperatorNode(ast);
|
|
25
|
-
this.operand = this.createExpressionNode(ast.args[0]);
|
|
26
|
-
|
|
27
|
-
// Set the operation property for compatibility with SimplificationEngine
|
|
28
|
-
this.operation = ast.fn || ast.op;
|
|
29
|
-
|
|
30
|
-
// Populate the argumentNodeList for mathematical child nodes
|
|
31
|
-
this.operand = this.operand;
|
|
32
|
-
this.argument = this.operand;
|
|
33
|
-
this.argumentNodeList.operand = this.operand;
|
|
34
|
-
this.argumentNodeList.argument = this.argument;
|
|
35
|
-
|
|
36
|
-
this.addChild(this.op);
|
|
37
|
-
this.addChild(this.operand);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
createOperatorNode(ast) {
|
|
41
|
-
const opNode = new omdOperatorNode({ op: ast.op, fn: ast.fn });
|
|
42
|
-
opNode.parent = this;
|
|
43
|
-
return opNode;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
createExpressionNode(ast) {
|
|
47
|
-
const NodeClass = getNodeForAST(ast);
|
|
48
|
-
const node = new NodeClass(ast);
|
|
49
|
-
node.parent = this;
|
|
50
|
-
return node;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
computeDimensions() {
|
|
54
|
-
this.op.computeDimensions();
|
|
55
|
-
this.operand.computeDimensions();
|
|
56
|
-
|
|
57
|
-
const opWidth = this.op.width;
|
|
58
|
-
// No extra spacing for unary minus.
|
|
59
|
-
const argWidth = this.operand.width;
|
|
60
|
-
|
|
61
|
-
const totalWidth = opWidth + argWidth;
|
|
62
|
-
const totalHeight = Math.max(this.op.height, this.operand.height);
|
|
63
|
-
|
|
64
|
-
this.setWidthAndHeight(totalWidth, totalHeight);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
updateLayout() {
|
|
68
|
-
this.op.updateLayout();
|
|
69
|
-
this.operand.updateLayout();
|
|
70
|
-
|
|
71
|
-
const opY = (this.height - this.op.height) / 2;
|
|
72
|
-
const argY = (this.height - this.operand.height) / 2;
|
|
73
|
-
|
|
74
|
-
this.op.setPosition(0, opY);
|
|
75
|
-
this.operand.setPosition(this.op.width, argY);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
clone() {
|
|
79
|
-
// Create a new node. The constructor will add a backRect and temporary children.
|
|
80
|
-
const tempAst = { type: 'OperatorNode', op: '-', args: [{type: 'ConstantNode', value: 1}] };
|
|
81
|
-
const clone = new omdUnaryExpressionNode(tempAst);
|
|
82
|
-
|
|
83
|
-
// Keep the backRect, but get rid of the temporary children.
|
|
84
|
-
const backRect = clone.backRect;
|
|
85
|
-
clone.removeAllChildren();
|
|
86
|
-
clone.addChild(backRect);
|
|
87
|
-
|
|
88
|
-
// Manually clone the real children to ensure the entire tree has correct provenance.
|
|
89
|
-
clone.op = this.op.clone();
|
|
90
|
-
clone.addChild(clone.op);
|
|
91
|
-
|
|
92
|
-
clone.operand = this.operand.clone();
|
|
93
|
-
clone.addChild(clone.operand);
|
|
94
|
-
|
|
95
|
-
// Ensure `argument` alias exists on the clone for compatibility
|
|
96
|
-
clone.argument = clone.operand;
|
|
97
|
-
|
|
98
|
-
// Rebuild the argument list and copy AST data.
|
|
99
|
-
clone.argumentNodeList = { operand: clone.operand, argument: clone.argument };
|
|
100
|
-
clone.astNodeData = JSON.parse(JSON.stringify(this.astNodeData));
|
|
101
|
-
|
|
102
|
-
// The crucial step: link the clone to its origin.
|
|
103
|
-
clone.provenance.push(this.id);
|
|
104
|
-
|
|
105
|
-
return clone;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* A unary expression is constant if its operand is constant.
|
|
110
|
-
* @returns {boolean}
|
|
111
|
-
*/
|
|
112
|
-
isConstant() {
|
|
113
|
-
return !!(this.operand && typeof this.operand.isConstant === 'function' && this.operand.isConstant());
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Get numeric value for unary expression (handles unary minus)
|
|
118
|
-
* @returns {number}
|
|
119
|
-
*/
|
|
120
|
-
getValue() {
|
|
121
|
-
if (!this.isConstant()) throw new Error('Node is not a constant expression');
|
|
122
|
-
const val = this.operand.getValue();
|
|
123
|
-
if (this.operation === 'unaryMinus' || (this.op && this.op.opName === '-')) {
|
|
124
|
-
return -val;
|
|
125
|
-
}
|
|
126
|
-
return val;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* If the operand represents a rational constant, return its rational pair adjusted for sign.
|
|
131
|
-
*/
|
|
132
|
-
getRationalValue() {
|
|
133
|
-
if (!this.isConstant()) throw new Error('Node is not a constant expression');
|
|
134
|
-
|
|
135
|
-
// If operand supports getRationalValue, use it and adjust sign
|
|
136
|
-
if (typeof this.operand.getRationalValue === 'function') {
|
|
137
|
-
const { num, den } = this.operand.getRationalValue();
|
|
138
|
-
if (this.operation === 'unaryMinus' || (this.op && this.op.opName === '-')) {
|
|
139
|
-
return { num: -num, den };
|
|
140
|
-
}
|
|
141
|
-
return { num, den };
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Fallback: treat operand as integer rational
|
|
145
|
-
const raw = this.operand.getValue();
|
|
146
|
-
if (this.operation === 'unaryMinus' || (this.op && this.op.opName === '-')) {
|
|
147
|
-
return { num: -raw, den: 1 };
|
|
148
|
-
}
|
|
149
|
-
return { num: raw, den: 1 };
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Converts the omdUnaryExpressionNode to a math.js AST node.
|
|
154
|
-
* @returns {Object} A math.js-compatible AST node.
|
|
155
|
-
*/
|
|
156
|
-
toMathJSNode() {
|
|
157
|
-
const astNode = {
|
|
158
|
-
type: 'OperatorNode',
|
|
159
|
-
op: this.op.opName,
|
|
160
|
-
fn: this.operation,
|
|
161
|
-
args: [this.operand.toMathJSNode()],
|
|
162
|
-
id: this.id,
|
|
163
|
-
provenance: this.provenance
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
// Add a clone method to maintain compatibility with math.js's expectations.
|
|
167
|
-
astNode.clone = function() {
|
|
168
|
-
const clonedNode = { ...this };
|
|
169
|
-
clonedNode.args = this.args.map(arg => arg.clone());
|
|
170
|
-
return clonedNode;
|
|
171
|
-
};
|
|
172
|
-
return astNode;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Converts the unary expression node to a string.
|
|
177
|
-
* @returns {string} The string representation of the expression.
|
|
178
|
-
*/
|
|
179
|
-
toString() {
|
|
180
|
-
const operandStr = this.operand.toString();
|
|
181
|
-
if (this.needsParentheses()) {
|
|
182
|
-
return `${this.op.opName}(${operandStr})`;
|
|
183
|
-
}
|
|
184
|
-
return `${this.op.opName}${operandStr}`;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Check if the operand needs parentheses.
|
|
189
|
-
* @returns {boolean} Whether parentheses are required
|
|
190
|
-
*/
|
|
191
|
-
needsParentheses() {
|
|
192
|
-
// Parentheses are needed if the operand is a binary expression.
|
|
193
|
-
return this.operand.type === 'omdBinaryExpressionNode';
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Evaluate the negation.
|
|
198
|
-
* @param {Object} variables - Variable name to value mapping
|
|
199
|
-
* @returns {number} The negated result
|
|
200
|
-
*/
|
|
201
|
-
evaluate(variables = {}) {
|
|
202
|
-
if (!this.operand.evaluate) return NaN;
|
|
203
|
-
const value = this.operand.evaluate(variables);
|
|
204
|
-
if (this.op.opName === '-') {
|
|
205
|
-
return -value;
|
|
206
|
-
}
|
|
207
|
-
return value; // For other potential unary ops like '+'
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Create a negation node from a string.
|
|
212
|
-
* @static
|
|
213
|
-
* @param {string} expressionString - Expression with negation
|
|
214
|
-
* @returns {omdUnaryExpressionNode}
|
|
215
|
-
*/
|
|
216
|
-
static fromString(expressionString) {
|
|
217
|
-
try {
|
|
218
|
-
const ast = window.math.parse(expressionString);
|
|
219
|
-
if (ast.type === 'OperatorNode' && ast.fn === 'unaryMinus') {
|
|
220
|
-
return new omdUnaryExpressionNode(ast);
|
|
221
|
-
}
|
|
222
|
-
throw new Error("Expression is not a unary minus operation.");
|
|
223
|
-
} catch (error) {
|
|
224
|
-
console.error("Failed to create unary expression node from string:", error);
|
|
225
|
-
throw error;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
1
|
+
import { omdNode } from "./omdNode.js";
|
|
2
|
+
import { getNodeForAST } from "../core/omdUtilities.js";
|
|
3
|
+
import { omdOperatorNode } from "./omdOperatorNode.js";
|
|
4
|
+
import { omdConstantNode } from "./omdConstantNode.js";
|
|
5
|
+
import { omdBinaryExpressionNode } from "./omdBinaryExpressionNode.js";
|
|
6
|
+
import { simplifyStep } from "../simplification/omdSimplification.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Represents a unary expression, like negation (-(x+y)).
|
|
10
|
+
* @extends omdNode
|
|
11
|
+
*/
|
|
12
|
+
export class omdUnaryExpressionNode extends omdNode {
|
|
13
|
+
/**
|
|
14
|
+
* @param {Object} ast - The AST node from math.js.
|
|
15
|
+
*/
|
|
16
|
+
constructor(ast) {
|
|
17
|
+
super(ast);
|
|
18
|
+
this.type = "omdUnaryExpressionNode";
|
|
19
|
+
if (!ast.args || ast.args.length !== 1) {
|
|
20
|
+
console.error("omdUnaryExpressionNode requires an AST node with exactly 1 argument", ast);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this.op = this.createOperatorNode(ast);
|
|
25
|
+
this.operand = this.createExpressionNode(ast.args[0]);
|
|
26
|
+
|
|
27
|
+
// Set the operation property for compatibility with SimplificationEngine
|
|
28
|
+
this.operation = ast.fn || ast.op;
|
|
29
|
+
|
|
30
|
+
// Populate the argumentNodeList for mathematical child nodes
|
|
31
|
+
this.operand = this.operand;
|
|
32
|
+
this.argument = this.operand;
|
|
33
|
+
this.argumentNodeList.operand = this.operand;
|
|
34
|
+
this.argumentNodeList.argument = this.argument;
|
|
35
|
+
|
|
36
|
+
this.addChild(this.op);
|
|
37
|
+
this.addChild(this.operand);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
createOperatorNode(ast) {
|
|
41
|
+
const opNode = new omdOperatorNode({ op: ast.op, fn: ast.fn });
|
|
42
|
+
opNode.parent = this;
|
|
43
|
+
return opNode;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
createExpressionNode(ast) {
|
|
47
|
+
const NodeClass = getNodeForAST(ast);
|
|
48
|
+
const node = new NodeClass(ast);
|
|
49
|
+
node.parent = this;
|
|
50
|
+
return node;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
computeDimensions() {
|
|
54
|
+
this.op.computeDimensions();
|
|
55
|
+
this.operand.computeDimensions();
|
|
56
|
+
|
|
57
|
+
const opWidth = this.op.width;
|
|
58
|
+
// No extra spacing for unary minus.
|
|
59
|
+
const argWidth = this.operand.width;
|
|
60
|
+
|
|
61
|
+
const totalWidth = opWidth + argWidth;
|
|
62
|
+
const totalHeight = Math.max(this.op.height, this.operand.height);
|
|
63
|
+
|
|
64
|
+
this.setWidthAndHeight(totalWidth, totalHeight);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
updateLayout() {
|
|
68
|
+
this.op.updateLayout();
|
|
69
|
+
this.operand.updateLayout();
|
|
70
|
+
|
|
71
|
+
const opY = (this.height - this.op.height) / 2;
|
|
72
|
+
const argY = (this.height - this.operand.height) / 2;
|
|
73
|
+
|
|
74
|
+
this.op.setPosition(0, opY);
|
|
75
|
+
this.operand.setPosition(this.op.width, argY);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
clone() {
|
|
79
|
+
// Create a new node. The constructor will add a backRect and temporary children.
|
|
80
|
+
const tempAst = { type: 'OperatorNode', op: '-', args: [{type: 'ConstantNode', value: 1}] };
|
|
81
|
+
const clone = new omdUnaryExpressionNode(tempAst);
|
|
82
|
+
|
|
83
|
+
// Keep the backRect, but get rid of the temporary children.
|
|
84
|
+
const backRect = clone.backRect;
|
|
85
|
+
clone.removeAllChildren();
|
|
86
|
+
clone.addChild(backRect);
|
|
87
|
+
|
|
88
|
+
// Manually clone the real children to ensure the entire tree has correct provenance.
|
|
89
|
+
clone.op = this.op.clone();
|
|
90
|
+
clone.addChild(clone.op);
|
|
91
|
+
|
|
92
|
+
clone.operand = this.operand.clone();
|
|
93
|
+
clone.addChild(clone.operand);
|
|
94
|
+
|
|
95
|
+
// Ensure `argument` alias exists on the clone for compatibility
|
|
96
|
+
clone.argument = clone.operand;
|
|
97
|
+
|
|
98
|
+
// Rebuild the argument list and copy AST data.
|
|
99
|
+
clone.argumentNodeList = { operand: clone.operand, argument: clone.argument };
|
|
100
|
+
clone.astNodeData = JSON.parse(JSON.stringify(this.astNodeData));
|
|
101
|
+
|
|
102
|
+
// The crucial step: link the clone to its origin.
|
|
103
|
+
clone.provenance.push(this.id);
|
|
104
|
+
|
|
105
|
+
return clone;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* A unary expression is constant if its operand is constant.
|
|
110
|
+
* @returns {boolean}
|
|
111
|
+
*/
|
|
112
|
+
isConstant() {
|
|
113
|
+
return !!(this.operand && typeof this.operand.isConstant === 'function' && this.operand.isConstant());
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get numeric value for unary expression (handles unary minus)
|
|
118
|
+
* @returns {number}
|
|
119
|
+
*/
|
|
120
|
+
getValue() {
|
|
121
|
+
if (!this.isConstant()) throw new Error('Node is not a constant expression');
|
|
122
|
+
const val = this.operand.getValue();
|
|
123
|
+
if (this.operation === 'unaryMinus' || (this.op && this.op.opName === '-')) {
|
|
124
|
+
return -val;
|
|
125
|
+
}
|
|
126
|
+
return val;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* If the operand represents a rational constant, return its rational pair adjusted for sign.
|
|
131
|
+
*/
|
|
132
|
+
getRationalValue() {
|
|
133
|
+
if (!this.isConstant()) throw new Error('Node is not a constant expression');
|
|
134
|
+
|
|
135
|
+
// If operand supports getRationalValue, use it and adjust sign
|
|
136
|
+
if (typeof this.operand.getRationalValue === 'function') {
|
|
137
|
+
const { num, den } = this.operand.getRationalValue();
|
|
138
|
+
if (this.operation === 'unaryMinus' || (this.op && this.op.opName === '-')) {
|
|
139
|
+
return { num: -num, den };
|
|
140
|
+
}
|
|
141
|
+
return { num, den };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Fallback: treat operand as integer rational
|
|
145
|
+
const raw = this.operand.getValue();
|
|
146
|
+
if (this.operation === 'unaryMinus' || (this.op && this.op.opName === '-')) {
|
|
147
|
+
return { num: -raw, den: 1 };
|
|
148
|
+
}
|
|
149
|
+
return { num: raw, den: 1 };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Converts the omdUnaryExpressionNode to a math.js AST node.
|
|
154
|
+
* @returns {Object} A math.js-compatible AST node.
|
|
155
|
+
*/
|
|
156
|
+
toMathJSNode() {
|
|
157
|
+
const astNode = {
|
|
158
|
+
type: 'OperatorNode',
|
|
159
|
+
op: this.op.opName,
|
|
160
|
+
fn: this.operation,
|
|
161
|
+
args: [this.operand.toMathJSNode()],
|
|
162
|
+
id: this.id,
|
|
163
|
+
provenance: this.provenance
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// Add a clone method to maintain compatibility with math.js's expectations.
|
|
167
|
+
astNode.clone = function() {
|
|
168
|
+
const clonedNode = { ...this };
|
|
169
|
+
clonedNode.args = this.args.map(arg => arg.clone());
|
|
170
|
+
return clonedNode;
|
|
171
|
+
};
|
|
172
|
+
return astNode;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Converts the unary expression node to a string.
|
|
177
|
+
* @returns {string} The string representation of the expression.
|
|
178
|
+
*/
|
|
179
|
+
toString() {
|
|
180
|
+
const operandStr = this.operand.toString();
|
|
181
|
+
if (this.needsParentheses()) {
|
|
182
|
+
return `${this.op.opName}(${operandStr})`;
|
|
183
|
+
}
|
|
184
|
+
return `${this.op.opName}${operandStr}`;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Check if the operand needs parentheses.
|
|
189
|
+
* @returns {boolean} Whether parentheses are required
|
|
190
|
+
*/
|
|
191
|
+
needsParentheses() {
|
|
192
|
+
// Parentheses are needed if the operand is a binary expression.
|
|
193
|
+
return this.operand.type === 'omdBinaryExpressionNode';
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Evaluate the negation.
|
|
198
|
+
* @param {Object} variables - Variable name to value mapping
|
|
199
|
+
* @returns {number} The negated result
|
|
200
|
+
*/
|
|
201
|
+
evaluate(variables = {}) {
|
|
202
|
+
if (!this.operand.evaluate) return NaN;
|
|
203
|
+
const value = this.operand.evaluate(variables);
|
|
204
|
+
if (this.op.opName === '-') {
|
|
205
|
+
return -value;
|
|
206
|
+
}
|
|
207
|
+
return value; // For other potential unary ops like '+'
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Create a negation node from a string.
|
|
212
|
+
* @static
|
|
213
|
+
* @param {string} expressionString - Expression with negation
|
|
214
|
+
* @returns {omdUnaryExpressionNode}
|
|
215
|
+
*/
|
|
216
|
+
static fromString(expressionString) {
|
|
217
|
+
try {
|
|
218
|
+
const ast = window.math.parse(expressionString);
|
|
219
|
+
if (ast.type === 'OperatorNode' && ast.fn === 'unaryMinus') {
|
|
220
|
+
return new omdUnaryExpressionNode(ast);
|
|
221
|
+
}
|
|
222
|
+
throw new Error("Expression is not a unary minus operation.");
|
|
223
|
+
} catch (error) {
|
|
224
|
+
console.error("Failed to create unary expression node from string:", error);
|
|
225
|
+
throw error;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
228
|
}
|