@teachinglab/omd 0.6.1 → 0.6.3
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 +606 -547
- package/omd/core/omdUtilities.js +113 -113
- package/omd/display/omdDisplay.js +1045 -963
- 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 -57
- 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,247 +1,247 @@
|
|
|
1
|
-
import { omdColor } from '../../src/omdColor.js';
|
|
2
|
-
import { omdEquationNode } from '../nodes/omdEquationNode.js';
|
|
3
|
-
import { omdStepVisualizerNodeUtils } from '../utils/omdStepVisualizerNodeUtils.js';
|
|
4
|
-
import { omdTreeDiff } from '../utils/omdTreeDiff.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Step visualizer highlighting functionality using robust tree diff algorithm.
|
|
8
|
-
* This class implements optimal substructure matching to identify truly changed nodes
|
|
9
|
-
* between mathematical equation steps, eliminating the need for special cases.
|
|
10
|
-
*/
|
|
11
|
-
export class omdStepVisualizerHighlighting {
|
|
12
|
-
constructor(stepVisualizer) {
|
|
13
|
-
this.stepVisualizer = stepVisualizer;
|
|
14
|
-
this.highlightedNodes = new Set();
|
|
15
|
-
this.educationalMode = true; // Enable highlighting of pedagogical simplifications
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Main entry point for highlighting nodes based on robust tree diff.
|
|
20
|
-
* @param {number} dotIndex - Index of the dot/step.
|
|
21
|
-
*/
|
|
22
|
-
highlightAffectedNodes(dotIndex, isOperation = false) {
|
|
23
|
-
|
|
24
|
-
this.clearHighlights();
|
|
25
|
-
|
|
26
|
-
const dot = this.stepVisualizer.stepDots[dotIndex];
|
|
27
|
-
if (!dot || !dot.equationRef) {
|
|
28
|
-
console.error("Highlighting failed: No equation reference for dot", dotIndex);
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const currentEquation = dot.equationRef;
|
|
33
|
-
const equationIndex = this.stepVisualizer.steps.indexOf(currentEquation);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const previousEquation = this._findNearestVisibleEquationAbove(equationIndex);
|
|
37
|
-
|
|
38
|
-
if (!previousEquation) {
|
|
39
|
-
const leafNodes = omdStepVisualizerNodeUtils.findLeafNodes(currentEquation);
|
|
40
|
-
leafNodes.forEach(node => this._highlightNode(node));
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const previousIndex = this.stepVisualizer.steps.indexOf(previousEquation);
|
|
45
|
-
|
|
46
|
-
// Use robust tree diff algorithm to find changed nodes
|
|
47
|
-
const changedNodes = omdTreeDiff.findChangedNodes(previousEquation, currentEquation, {
|
|
48
|
-
educationalMode: this.educationalMode
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
// Apply highlighting to changed nodes
|
|
53
|
-
changedNodes.forEach(node => this._highlightNode(node));
|
|
54
|
-
|
|
55
|
-
// Use provenance to highlight related nodes in the previous equation
|
|
56
|
-
if (!isOperation) {
|
|
57
|
-
this._highlightProvenanceNodes(changedNodes, previousEquation);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Highlights a single node with the standard explanation color.
|
|
64
|
-
* @param {omdNode} node - The node to highlight.
|
|
65
|
-
* @private
|
|
66
|
-
*/
|
|
67
|
-
_highlightNode(node) {
|
|
68
|
-
if (node && typeof node.setExplainHighlight === 'function') {
|
|
69
|
-
node.setExplainHighlight(true);
|
|
70
|
-
this.highlightedNodes.add(node);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Finds the nearest visible equation above the given index.
|
|
76
|
-
* @param {number} currentIndex - Index of current equation.
|
|
77
|
-
* @returns {omdEquationNode|null} The nearest visible equation above, or null.
|
|
78
|
-
* @private
|
|
79
|
-
*/
|
|
80
|
-
_findNearestVisibleEquationAbove(currentIndex) {
|
|
81
|
-
for (let i = currentIndex - 1; i >= 0; i--) {
|
|
82
|
-
const step = this.stepVisualizer.steps[i];
|
|
83
|
-
if (step instanceof omdEquationNode && step.visible !== false) {
|
|
84
|
-
return step;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Clears all highlights managed by this class.
|
|
92
|
-
*/
|
|
93
|
-
clearHighlights() {
|
|
94
|
-
this.highlightedNodes.forEach(node => {
|
|
95
|
-
if (node && typeof node.setExplainHighlight === 'function') {
|
|
96
|
-
node.setExplainHighlight(false);
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
this.highlightedNodes.clear();
|
|
101
|
-
this.stepVisualizer.stepVisualizerHighlights.clear();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Clears ALL explain highlights from the entire sequence, not just tracked ones.
|
|
106
|
-
* This is more thorough than clearHighlights() and should be used when
|
|
107
|
-
* the step visualizer is disabled or when we need to ensure no stale highlights remain.
|
|
108
|
-
*/
|
|
109
|
-
clearAllExplainHighlights() {
|
|
110
|
-
// Clear tracked highlights first
|
|
111
|
-
this.clearHighlights();
|
|
112
|
-
|
|
113
|
-
// Also clear any explain highlights from the entire sequence tree
|
|
114
|
-
const rootNode = this.stepVisualizer.getRootNode ? this.stepVisualizer.getRootNode() : null;
|
|
115
|
-
if (rootNode) {
|
|
116
|
-
this._clearExplainHighlightsFromTree(rootNode);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Recursively clears explain highlights from an entire tree
|
|
122
|
-
* @param {omdNode} node - The root node to start clearing from
|
|
123
|
-
* @private
|
|
124
|
-
*/
|
|
125
|
-
_clearExplainHighlightsFromTree(node) {
|
|
126
|
-
if (node && typeof node.setExplainHighlight === 'function') {
|
|
127
|
-
node.setExplainHighlight(false);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Recursively clear from children
|
|
131
|
-
if (node && node.childList && Array.isArray(node.childList)) {
|
|
132
|
-
node.childList.forEach(child => {
|
|
133
|
-
this._clearExplainHighlightsFromTree(child);
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Also check argumentNodeList if it exists
|
|
138
|
-
if (node && node.argumentNodeList) {
|
|
139
|
-
Object.values(node.argumentNodeList).forEach(child => {
|
|
140
|
-
this._clearExplainHighlightsFromTree(child);
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Highlights nodes in the previous equation based on provenance from changed nodes.
|
|
147
|
-
* This creates a visual connection between the current changes and their origins.
|
|
148
|
-
* @param {Array} changedNodes - Array of changed nodes in the current equation
|
|
149
|
-
* @param {omdEquationNode} previousEquation - The previous equation to highlight nodes in
|
|
150
|
-
* @private
|
|
151
|
-
*/
|
|
152
|
-
_highlightProvenanceNodes(changedNodes, previousEquation) {
|
|
153
|
-
|
|
154
|
-
const rootNode = previousEquation.getRootNode();
|
|
155
|
-
if (!rootNode || !rootNode.nodeMap) {
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const currentIndex = this.stepVisualizer.steps.indexOf(previousEquation) + 1;
|
|
160
|
-
let targetEquation = null;
|
|
161
|
-
|
|
162
|
-
// Search backwards for the first visible equation
|
|
163
|
-
for (let i = currentIndex - 1; i >= 0; i--) {
|
|
164
|
-
const step = this.stepVisualizer.steps[i];
|
|
165
|
-
if (step instanceof omdEquationNode && step.visible !== false) {
|
|
166
|
-
targetEquation = step;
|
|
167
|
-
break;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (!targetEquation) {
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const visited = new Set();
|
|
176
|
-
const nodesToProcess = [];
|
|
177
|
-
|
|
178
|
-
// Start with the changed nodes' provenance
|
|
179
|
-
changedNodes.forEach(node => {
|
|
180
|
-
if (node.provenance && Array.isArray(node.provenance)) {
|
|
181
|
-
node.provenance.forEach(id => {
|
|
182
|
-
if (!visited.has(id)) {
|
|
183
|
-
visited.add(id);
|
|
184
|
-
nodesToProcess.push(id);
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
// Process provenance IDs
|
|
191
|
-
while (nodesToProcess.length > 0) {
|
|
192
|
-
const id = nodesToProcess.shift();
|
|
193
|
-
const provenanceNode = rootNode.nodeMap.get(id);
|
|
194
|
-
|
|
195
|
-
if (provenanceNode) {
|
|
196
|
-
if (this._belongsToEquation(provenanceNode, targetEquation)) {
|
|
197
|
-
this._highlightProvenanceNode(provenanceNode);
|
|
198
|
-
}
|
|
199
|
-
// Add this node's provenance to the processing queue
|
|
200
|
-
if (provenanceNode.provenance && Array.isArray(provenanceNode.provenance)) {
|
|
201
|
-
provenanceNode.provenance.forEach(subId => {
|
|
202
|
-
if (!visited.has(subId)) {
|
|
203
|
-
visited.add(subId);
|
|
204
|
-
nodesToProcess.push(subId);
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
} else {
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Checks if a node belongs to a specific equation by traversing up the tree
|
|
219
|
-
* @param {omdNode} node - The node to check
|
|
220
|
-
* @param {omdEquationNode} targetEquation - The equation to check against
|
|
221
|
-
* @returns {boolean} True if the node belongs to the equation
|
|
222
|
-
* @private
|
|
223
|
-
*/
|
|
224
|
-
_belongsToEquation(node, targetEquation) {
|
|
225
|
-
let current = node;
|
|
226
|
-
while (current) {
|
|
227
|
-
if (current === targetEquation) {
|
|
228
|
-
return true;
|
|
229
|
-
}
|
|
230
|
-
current = current.parent;
|
|
231
|
-
}
|
|
232
|
-
return false;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Highlights a provenance node with secondary highlighting style
|
|
237
|
-
* @param {omdNode} node - The node to highlight with provenance style
|
|
238
|
-
* @private
|
|
239
|
-
*/
|
|
240
|
-
_highlightProvenanceNode(node) {
|
|
241
|
-
if (node && typeof node.setExplainHighlight === 'function') {
|
|
242
|
-
// Use a slightly different color or style for provenance if desired
|
|
243
|
-
node.setExplainHighlight(true, omdColor.provenanceColor);
|
|
244
|
-
this.highlightedNodes.add(node);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
1
|
+
import { omdColor } from '../../src/omdColor.js';
|
|
2
|
+
import { omdEquationNode } from '../nodes/omdEquationNode.js';
|
|
3
|
+
import { omdStepVisualizerNodeUtils } from '../utils/omdStepVisualizerNodeUtils.js';
|
|
4
|
+
import { omdTreeDiff } from '../utils/omdTreeDiff.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Step visualizer highlighting functionality using robust tree diff algorithm.
|
|
8
|
+
* This class implements optimal substructure matching to identify truly changed nodes
|
|
9
|
+
* between mathematical equation steps, eliminating the need for special cases.
|
|
10
|
+
*/
|
|
11
|
+
export class omdStepVisualizerHighlighting {
|
|
12
|
+
constructor(stepVisualizer) {
|
|
13
|
+
this.stepVisualizer = stepVisualizer;
|
|
14
|
+
this.highlightedNodes = new Set();
|
|
15
|
+
this.educationalMode = true; // Enable highlighting of pedagogical simplifications
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Main entry point for highlighting nodes based on robust tree diff.
|
|
20
|
+
* @param {number} dotIndex - Index of the dot/step.
|
|
21
|
+
*/
|
|
22
|
+
highlightAffectedNodes(dotIndex, isOperation = false) {
|
|
23
|
+
|
|
24
|
+
this.clearHighlights();
|
|
25
|
+
|
|
26
|
+
const dot = this.stepVisualizer.stepDots[dotIndex];
|
|
27
|
+
if (!dot || !dot.equationRef) {
|
|
28
|
+
console.error("Highlighting failed: No equation reference for dot", dotIndex);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const currentEquation = dot.equationRef;
|
|
33
|
+
const equationIndex = this.stepVisualizer.steps.indexOf(currentEquation);
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
const previousEquation = this._findNearestVisibleEquationAbove(equationIndex);
|
|
37
|
+
|
|
38
|
+
if (!previousEquation) {
|
|
39
|
+
const leafNodes = omdStepVisualizerNodeUtils.findLeafNodes(currentEquation);
|
|
40
|
+
leafNodes.forEach(node => this._highlightNode(node));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const previousIndex = this.stepVisualizer.steps.indexOf(previousEquation);
|
|
45
|
+
|
|
46
|
+
// Use robust tree diff algorithm to find changed nodes
|
|
47
|
+
const changedNodes = omdTreeDiff.findChangedNodes(previousEquation, currentEquation, {
|
|
48
|
+
educationalMode: this.educationalMode
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
// Apply highlighting to changed nodes
|
|
53
|
+
changedNodes.forEach(node => this._highlightNode(node));
|
|
54
|
+
|
|
55
|
+
// Use provenance to highlight related nodes in the previous equation
|
|
56
|
+
if (!isOperation) {
|
|
57
|
+
this._highlightProvenanceNodes(changedNodes, previousEquation);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Highlights a single node with the standard explanation color.
|
|
64
|
+
* @param {omdNode} node - The node to highlight.
|
|
65
|
+
* @private
|
|
66
|
+
*/
|
|
67
|
+
_highlightNode(node) {
|
|
68
|
+
if (node && typeof node.setExplainHighlight === 'function') {
|
|
69
|
+
node.setExplainHighlight(true);
|
|
70
|
+
this.highlightedNodes.add(node);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Finds the nearest visible equation above the given index.
|
|
76
|
+
* @param {number} currentIndex - Index of current equation.
|
|
77
|
+
* @returns {omdEquationNode|null} The nearest visible equation above, or null.
|
|
78
|
+
* @private
|
|
79
|
+
*/
|
|
80
|
+
_findNearestVisibleEquationAbove(currentIndex) {
|
|
81
|
+
for (let i = currentIndex - 1; i >= 0; i--) {
|
|
82
|
+
const step = this.stepVisualizer.steps[i];
|
|
83
|
+
if (step instanceof omdEquationNode && step.visible !== false) {
|
|
84
|
+
return step;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Clears all highlights managed by this class.
|
|
92
|
+
*/
|
|
93
|
+
clearHighlights() {
|
|
94
|
+
this.highlightedNodes.forEach(node => {
|
|
95
|
+
if (node && typeof node.setExplainHighlight === 'function') {
|
|
96
|
+
node.setExplainHighlight(false);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
this.highlightedNodes.clear();
|
|
101
|
+
this.stepVisualizer.stepVisualizerHighlights.clear();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Clears ALL explain highlights from the entire sequence, not just tracked ones.
|
|
106
|
+
* This is more thorough than clearHighlights() and should be used when
|
|
107
|
+
* the step visualizer is disabled or when we need to ensure no stale highlights remain.
|
|
108
|
+
*/
|
|
109
|
+
clearAllExplainHighlights() {
|
|
110
|
+
// Clear tracked highlights first
|
|
111
|
+
this.clearHighlights();
|
|
112
|
+
|
|
113
|
+
// Also clear any explain highlights from the entire sequence tree
|
|
114
|
+
const rootNode = this.stepVisualizer.getRootNode ? this.stepVisualizer.getRootNode() : null;
|
|
115
|
+
if (rootNode) {
|
|
116
|
+
this._clearExplainHighlightsFromTree(rootNode);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Recursively clears explain highlights from an entire tree
|
|
122
|
+
* @param {omdNode} node - The root node to start clearing from
|
|
123
|
+
* @private
|
|
124
|
+
*/
|
|
125
|
+
_clearExplainHighlightsFromTree(node) {
|
|
126
|
+
if (node && typeof node.setExplainHighlight === 'function') {
|
|
127
|
+
node.setExplainHighlight(false);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Recursively clear from children
|
|
131
|
+
if (node && node.childList && Array.isArray(node.childList)) {
|
|
132
|
+
node.childList.forEach(child => {
|
|
133
|
+
this._clearExplainHighlightsFromTree(child);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Also check argumentNodeList if it exists
|
|
138
|
+
if (node && node.argumentNodeList) {
|
|
139
|
+
Object.values(node.argumentNodeList).forEach(child => {
|
|
140
|
+
this._clearExplainHighlightsFromTree(child);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Highlights nodes in the previous equation based on provenance from changed nodes.
|
|
147
|
+
* This creates a visual connection between the current changes and their origins.
|
|
148
|
+
* @param {Array} changedNodes - Array of changed nodes in the current equation
|
|
149
|
+
* @param {omdEquationNode} previousEquation - The previous equation to highlight nodes in
|
|
150
|
+
* @private
|
|
151
|
+
*/
|
|
152
|
+
_highlightProvenanceNodes(changedNodes, previousEquation) {
|
|
153
|
+
|
|
154
|
+
const rootNode = previousEquation.getRootNode();
|
|
155
|
+
if (!rootNode || !rootNode.nodeMap) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const currentIndex = this.stepVisualizer.steps.indexOf(previousEquation) + 1;
|
|
160
|
+
let targetEquation = null;
|
|
161
|
+
|
|
162
|
+
// Search backwards for the first visible equation
|
|
163
|
+
for (let i = currentIndex - 1; i >= 0; i--) {
|
|
164
|
+
const step = this.stepVisualizer.steps[i];
|
|
165
|
+
if (step instanceof omdEquationNode && step.visible !== false) {
|
|
166
|
+
targetEquation = step;
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (!targetEquation) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const visited = new Set();
|
|
176
|
+
const nodesToProcess = [];
|
|
177
|
+
|
|
178
|
+
// Start with the changed nodes' provenance
|
|
179
|
+
changedNodes.forEach(node => {
|
|
180
|
+
if (node.provenance && Array.isArray(node.provenance)) {
|
|
181
|
+
node.provenance.forEach(id => {
|
|
182
|
+
if (!visited.has(id)) {
|
|
183
|
+
visited.add(id);
|
|
184
|
+
nodesToProcess.push(id);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Process provenance IDs
|
|
191
|
+
while (nodesToProcess.length > 0) {
|
|
192
|
+
const id = nodesToProcess.shift();
|
|
193
|
+
const provenanceNode = rootNode.nodeMap.get(id);
|
|
194
|
+
|
|
195
|
+
if (provenanceNode) {
|
|
196
|
+
if (this._belongsToEquation(provenanceNode, targetEquation)) {
|
|
197
|
+
this._highlightProvenanceNode(provenanceNode);
|
|
198
|
+
}
|
|
199
|
+
// Add this node's provenance to the processing queue
|
|
200
|
+
if (provenanceNode.provenance && Array.isArray(provenanceNode.provenance)) {
|
|
201
|
+
provenanceNode.provenance.forEach(subId => {
|
|
202
|
+
if (!visited.has(subId)) {
|
|
203
|
+
visited.add(subId);
|
|
204
|
+
nodesToProcess.push(subId);
|
|
205
|
+
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Checks if a node belongs to a specific equation by traversing up the tree
|
|
219
|
+
* @param {omdNode} node - The node to check
|
|
220
|
+
* @param {omdEquationNode} targetEquation - The equation to check against
|
|
221
|
+
* @returns {boolean} True if the node belongs to the equation
|
|
222
|
+
* @private
|
|
223
|
+
*/
|
|
224
|
+
_belongsToEquation(node, targetEquation) {
|
|
225
|
+
let current = node;
|
|
226
|
+
while (current) {
|
|
227
|
+
if (current === targetEquation) {
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
current = current.parent;
|
|
231
|
+
}
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Highlights a provenance node with secondary highlighting style
|
|
237
|
+
* @param {omdNode} node - The node to highlight with provenance style
|
|
238
|
+
* @private
|
|
239
|
+
*/
|
|
240
|
+
_highlightProvenanceNode(node) {
|
|
241
|
+
if (node && typeof node.setExplainHighlight === 'function') {
|
|
242
|
+
// Use a slightly different color or style for provenance if desired
|
|
243
|
+
node.setExplainHighlight(true, omdColor.provenanceColor);
|
|
244
|
+
this.highlightedNodes.add(node);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
247
|
}
|