/// <summary> /// Parses an atomic production.<para/> /// An atomic production can be represented as EBNF like: /// <code><atomic> := [ <literal> ] { <exp> } { <token> { <exp> } }</code><para/> /// This means that, in an equation such as <c>y=4x√2</c> the three tokens '4', 'x' and '√2' are correctly broken /// down into three separate multiplications. This is so the algebraic multiplication short-hand of omitting the /// cross symbol still works. Lastly, this also parses any exponents. /// </summary> /// <param name="enumerator">An enumerator containing the current location in the Expression of the parser.</param> /// <returns>Returns a parse tree node representing this sub-expression in the order it should be evaluated.</returns> protected ParseTreeNode ParseAtomic(ParserEnumerator enumerator) { ParseTreeNode currentNode = null; // parse the preceding literal, if there is one if (enumerator.Check <DigitToken>()) { currentNode = ParseLiteral(enumerator); } if (enumerator.Check <ExpToken>()) { if (currentNode != null) // we can't exponentiate a non-existent literal { currentNode = new BinaryParseTreeNode( ExpEvaluator, currentNode, ParseExponent(enumerator)); } else { throw new ParseException("Expected a valid token before an exponent.", enumerator.Current); } } while (enumerator.Accept <Token>(t => t is IParsable)) { // parse any other successive tokens ParseTreeNode currentNodeSubtoken = (enumerator.Current as IParsable).Parse(); if (enumerator.Check <ExpToken>()) { currentNodeSubtoken = new BinaryParseTreeNode( ExpEvaluator, currentNodeSubtoken, ParseExponent(enumerator)); } if (currentNode == null) { // if the current node is null, create it currentNode = currentNodeSubtoken; } else { // otherwise, multiply the existing current node with the new subnode currentNode = new BinaryParseTreeNode( MultiplyEvaluator, currentNode, currentNodeSubtoken); } } if (currentNode == null) { throw new ParseException("Expected a valid token.", enumerator.Current); } return(currentNode); }
/// <summary> /// Plots this Equation onto the specified <paramref name="graph"/> with the given <paramref name="plotParams"/>. /// This method will determine which method of plotting is most appropriate for this equation. This could be the /// implicit plotting method for implicit equations, or explicit plotting with respect to one of the two plotted /// variables in the graph. Explicit plotting will be attempted wherever possible. /// </summary> /// <param name="graph">The Graph to plot this IPlottable onto.</param> /// <param name="graphics">The GDI+ drawing surface to use for plotting this IPlottable.</param> /// <param name="graphSize">The size of the Graph on the screen. This is a property of the display rather than the /// graph and is thus not included in the graph's parameters.</param> /// <param name="plotParams">The parameters used to plot this IPlottable.</param> /// <param name="resolution">The plotting resolution to use. Using a coarser resolution may make the plotting /// process faster, and is thus more suitable when the display is being resized or moved.</param> public void PlotOnto(Graph graph, Graphics graphics, Size graphSize, PlottableParameters plotParams, PlotResolution resolution) { if (resolution == PlotResolution.Resize) { return; } if (ParseTree == null) { Parse(); } BinaryParseTreeNode parseTreeRoot = ParseTree as BinaryParseTreeNode; var originalSmoothingMode = graphics.SmoothingMode; graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; // smoother pen for the graph using (Pen graphPen = new Pen(plotParams.PlotColor, EquationPenWidth)) { if (parseTreeRoot.Left is VariableParseTreeNode && // if the only instance of a variable is on the left eg. y=x+1 !parseTreeRoot.Right.ContainsVariable((parseTreeRoot.Left as VariableParseTreeNode).Variable)) { PlotExplicit( graph, graphics, graphSize, graphPen, (parseTreeRoot.Left as VariableParseTreeNode).Variable, parseTreeRoot.Right, resolution); } else if (parseTreeRoot.Right is VariableParseTreeNode && // if the only instance of a variable is on the right eg. y-1=x !parseTreeRoot.Left.ContainsVariable((parseTreeRoot.Right as VariableParseTreeNode).Variable)) { PlotExplicit( graph, graphics, graphSize, graphPen, (parseTreeRoot.Right as VariableParseTreeNode).Variable, parseTreeRoot.Left, resolution); } else // if variables are equated implicitly eg. xy=x+y-x^2 { PlotImplicit(graph, graphics, graphSize, graphPen, resolution); } } graphics.SmoothingMode = originalSmoothingMode; }
/// <summary> /// Initialize a new instance of the <c>Graphmatic.Interaction.Equation</c> class from the given expression, /// representing an equation that describes this <c>Graphmatic.Interaction.Equation</c>. /// </summary> public Equation(Expression expression) : base() { Expression = expression; if (Expression.Count == 0) { // create a dummy parse tree equivalent to '0=1' // meaning it won't be plotted but it won't throw any exceptions either ParseTree = new BinaryParseTreeNode(Expression.EqualsEvaluator, new ConstantParseTreeNode(0), new ConstantParseTreeNode(1)); } else { ParseTree = Expression.Parse(); } }
/// <summary> /// Parses a series of <c>ExpToken</c>s, and returns the combined power. /// This function takes advantage of the mathematical identity: <c>(a^b)^c=a^(b*c)</c>. Successive <c>ExpToken</c>s /// have their powers multiplied together such to return a single power to which to raise the previous expression to. /// </summary> /// <param name="enumerator">An enumerator containing the current location in the Expression of the parser.</param> /// <returns>Returns the power that the token preceding the parsed <c>ExpToken</c>s should be raised to.</returns> protected ParseTreeNode ParseExponent(ParserEnumerator enumerator) { if (enumerator.Accept <ExpToken>()) { ParseTreeNode exponentNode = (enumerator.Current as ExpToken).Power.Parse(); while (enumerator.Accept <ExpToken>()) { exponentNode = new BinaryParseTreeNode( MultiplyEvaluator, exponentNode, (enumerator.Current as ExpToken).Power.Parse()); } return(exponentNode); } else { return(null); } }
/// <summary> /// Parses a summation production, handling addition and subtraction. /// </summary> /// <param name="enumerator">An enumerator containing the current location in the Expression of the parser.</param> /// <returns>Returns a parse tree node representing this sub-expression in the order it should be evaluated.</returns> protected ParseTreeNode ParseSummation(ParserEnumerator enumerator) { ParseTreeNode currentNode = ParseProduction(enumerator); while (enumerator.Accept <OperationToken>(t => t.Operation == OperationToken.OperationType.Add || t.Operation == OperationToken.OperationType.Subtract)) { OperationToken current = enumerator.Current as OperationToken; if (current.Operation == OperationToken.OperationType.Add) { currentNode = new BinaryParseTreeNode(AddEvaluator, currentNode, ParseProduction(enumerator)); } else if (current.Operation == OperationToken.OperationType.Subtract) { currentNode = new BinaryParseTreeNode(SubtractEvaluator, currentNode, ParseProduction(enumerator)); } } return(currentNode); }
/// <summary> /// Parses a production production (I might have called it that on purpose.)<para/> /// Basically, this handles multiplication and division. /// </summary> /// <param name="enumerator">An enumerator containing the current location in the Expression of the parser.</param> /// <returns>Returns a parse tree node representing this sub-expression in the order it should be evaluated.</returns> protected ParseTreeNode ParseProduction(ParserEnumerator enumerator) { ParseTreeNode currentNode = ParseUnary(enumerator); while (enumerator.Accept <OperationToken>(t => t.Operation == OperationToken.OperationType.Multiply || t.Operation == OperationToken.OperationType.Divide)) { OperationToken current = enumerator.Current as OperationToken; if (current.Operation == OperationToken.OperationType.Multiply) { currentNode = new BinaryParseTreeNode(MultiplyEvaluator, currentNode, ParseUnary(enumerator)); } else if (current.Operation == OperationToken.OperationType.Divide) { currentNode = new BinaryParseTreeNode(DivideEvaluator, currentNode, ParseUnary(enumerator)); } } return(currentNode); }