UNPKG

@teachinglab/omd

Version:

omd

141 lines (115 loc) 4.44 kB
import { getRulesForNode } from './simplificationRules.js'; import { setSimplifyStep } from '../nodes/omdNode.js'; /** * Finds all simplification opportunities within an expression tree. * Now much faster - only checks relevant rules for each node type! * @param {omdNode} rootNode - The root of the expression tree to search. * @returns {Array<Object>} Array of simplification opportunities. */ export function findSimplificationOpportunities(rootNode) { const opportunities = []; const visitedNodes = new Set(); function traverse(node) { if (!node || visitedNodes.has(node)) return; visitedNodes.add(node); // Traverse children first (depth-first) if (node.childList && node.childList.length > 0) { for (const child of node.childList) { traverse(child); } } // Only check rules that apply to this node type const relevantRules = getRulesForNode(node); for (const rule of relevantRules) { const ruleData = rule.canApply(node); if (ruleData) { opportunities.push({ node, rule, ruleData }); break; // Only apply the first matching rule } } } traverse(rootNode); return opportunities; } export function simplifyStep(rootNode) { let foldedCount = 0; let newRoot = rootNode; let historyEntry = null; // Find all simplification opportunities const opportunities = findSimplificationOpportunities(rootNode); if (opportunities.length > 0) { // Apply the first opportunity const opportunity = opportunities[0]; try { const result = opportunity.rule.apply(opportunity.node, opportunity.ruleData, rootNode); if (result && result.success && result.newRoot) { newRoot = result.newRoot; foldedCount = 1; historyEntry = result.historyEntry || { name: opportunity.rule.name, affectedNodes: [rootNode.id], message: `Applied ${opportunity.rule.name}` }; } } catch (error) { console.error(`Error applying rule '${opportunity.rule.name}':`, error); } } return { newRoot, foldedCount, historyEntry }; } /** * Simplifies an entire mathematical expression tree by repeatedly applying rules. * @param {omdNode} rootNode - The root node of the expression to simplify. * @returns {{foldedCount: number, newRoot: omdNode}} */ export function simplifyExpression(rootNode) { let totalFolded = 0; let currentRoot = rootNode; let iterations = 0; const maxIterations = 50; // Prevent infinite loops while (iterations < maxIterations) { const { foldedCount, newRoot } = simplifyStep(currentRoot); if (foldedCount === 0) { break; // No more simplifications possible } currentRoot = newRoot; totalFolded += foldedCount; iterations++; } // Return a fresh clone if any simplifications occurred if (totalFolded > 0) { const cleanRoot = currentRoot.clone(); cleanRoot.setFontSize(rootNode.getFontSize()); return { foldedCount: totalFolded, newRoot: cleanRoot }; } return { foldedCount: totalFolded, newRoot: currentRoot }; } /** * Debug function to show which rules are available for a node * @param {omdNode} node - The node to check * @returns {Array<string>} Array of rule names */ export function getAvailableRulesForNode(node) { const rules = getRulesForNode(node); return rules.map(rule => rule.name); } /** * Debug function to show which rules can be applied to a node * @param {omdNode} node - The node to check * @returns {Array<string>} Array of applicable rule names */ export function getApplicableRulesForNode(node) { const rules = getRulesForNode(node); const applicable = []; for (const rule of rules) { try { const ruleData = rule.canApply(node); if (ruleData) { applicable.push(rule.name); } } catch (error) { } } return applicable; } setSimplifyStep(simplifyStep);