/// <summary> /// Calculate the equation. /// </summary> private void Calculate() { double value = 0.0d; try { Equation equation = Equation.ParseEquation(_equation); value = equation.GetValue(); _history.Add(_equation + "=" + value.ToString("G5")); _windowPos.height = 0.0f; _windowPos.width = 0.0f; if (_history.Count > MAX_HISTORY) { _history.RemoveRange(0, _history.Count - MAX_HISTORY); } } catch (ParsingException e) { Debug.Log(_equation + ":" + e.Message); _history.Add(_equation + "\n" + e.Message); } }
/// <summary> /// Raises the window event. /// </summary> /// <param name="windowId">Window identifier.</param> private void OnWindow(int windowId) { GUILayout.BeginHorizontal(GUILayout.MinWidth(300.0f)); GUILayout.BeginVertical(GUILayout.MaxWidth(300.0f + (_width * 50.0f))); GUILayout.BeginHorizontal(); GUILayout.Label("History:", _labelStyle); if (GUILayout.Button("Clear", _buttonStyle)) { _history.Clear(); _windowPos.height = 0.0f; _windowPos.width = 0.0f; } GUILayout.EndHorizontal(); GUILayout.TextArea(String.Join("\n", _history.ToArray()), _textAreaStyle); GUILayout.BeginHorizontal(GUILayout.MinWidth(300.0f + (_width * 50.0f))); GUILayout.Label("Equation Input:", _labelStyle); if (GUILayout.Button("Wider", _buttonStyle) && _width < 6) { _width++; } if (GUILayout.Button("Narrower", _buttonStyle) && _width > 0) { _width--; _windowPos.width = 0.0f; } GUILayout.EndHorizontal(); GUI.SetNextControlName("EquationIn"); _equation = GUILayout.TextField(_equation, _textFieldStyle); if (Event.current.isKey && GUI.GetNameOfFocusedControl() == "EquationIn") { if (Event.current.keyCode == KeyCode.UpArrow || Event.current.keyCode == KeyCode.DownArrow) { if (Event.current.keyCode == KeyCode.UpArrow) { if (_hIdx < 0) { _hIdx = _history.Count - 1; } else { _hIdx--; } } else if (Event.current.keyCode == KeyCode.DownArrow) { _hIdx++; if (_hIdx >= _history.Count) { _hIdx = -1; } } if (_hIdx < 0) { _hIdx = -1; _equation = ""; } else { string hist = _history [_hIdx]; if (hist.IndexOf('=') > 0) { hist = hist.Remove(hist.IndexOf('=')); } if (hist.IndexOf('\n') > 0) { hist = hist.Remove(hist.IndexOf('\n')); } _equation = hist; } } else if (Event.current.keyCode == KeyCode.Return) { Calculate(); } } GUILayout.BeginHorizontal(); if (GUILayout.Button("Calculate", _buttonStyle)) { Calculate(); } if (GUILayout.Button(_simple ? "More" : "Less", _buttonStyle)) { _simple = !_simple; _windowPos.width = 0.0f; _windowPos.height = 0.0f; } if (GUILayout.Button("Close", _buttonStyle)) { Hide(); } GUILayout.EndHorizontal(); GUILayout.EndVertical(); if (!_simple) { GUILayout.BeginVertical(GUILayout.MaxWidth(300.0f)); int oldMenu = _menuSelection; _menuSelection = GUILayout.SelectionGrid(_menuSelection, new string[] { "Graph", "Cheat Sheet", "Notes", "Help" }, 2, _buttonStyle, GUILayout.MinWidth(300.0f)); if (_menuSelection == 0) { GUILayout.BeginHorizontal(); GUILayout.Label("X Min:", _labelStyle, GUILayout.MaxWidth(50.0f)); _xmin = GUILayout.TextField(_xmin, _textFieldStyle, GUILayout.MaxWidth(250f)); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("X Max:", _labelStyle, GUILayout.MaxWidth(50.0f)); _xmax = GUILayout.TextField(_xmax, _textFieldStyle, GUILayout.MaxWidth(250f)); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); if (GUILayout.Button("Graph", _buttonStyle)) { Graph(); } if (GUILayout.Button("Reset Graph", _buttonStyle)) { _graph = null; _geq = null; _windowPos.height = 0.0f; _windowPos.width = 0.0f; } GUILayout.EndHorizontal(); if (_graph != null) { GUILayout.Label("Y max:" + _ymax.ToString("G10"), _labelStyle); GUILayout.Box(_graph.getImage()); Rect boxRect = GUILayoutUtility.GetLastRect(); string mouseOverMessage = ""; if (boxRect.Contains(Event.current.mousePosition)) { float x = Event.current.mousePosition.x - boxRect.x - 25.0f; if (x >= 0 && x <= 250.0f) { double delta = (_gxmax - _gxmin) / 250.0d; mouseOverMessage = "X:" + (_gxmin + x * delta).ToString("G5") + " Y:" + _geq.GetValue(_gxmin + x * delta).ToString("G5"); } } GUILayout.Label("Y min:" + _ymin.ToString("G10"), _labelStyle); GUILayout.Label(mouseOverMessage, _labelStyle); } } else if (_menuSelection == 1) { GUILayout.Label("Cheat Sheet", _labelStyle); GUILayout.TextArea(CHEAT_SHEET, _textAreaStyle); } else if (_menuSelection == 2) { GUILayout.Label("Notes", _labelStyle); _globalScratchPad = GUILayout.TextArea(_globalScratchPad, _textAreaStyle); } else if (_menuSelection == 3) { GUILayout.Label("Help", _labelStyle); GUILayout.TextArea(HELP, _textAreaStyle); } if (_menuSelection != oldMenu) { _windowPos.height = 0.0f; } GUILayout.EndVertical(); } GUILayout.EndHorizontal(); GUI.DragWindow(); }
/// <summary> /// Parses the equation string returning an Equation object. Error checking is minimal and will evaluate /// invalid strings in unique and meaningless ways. /// </summary> /// <returns>The equation object.</returns> /// <param name="eqText">Equation text.</param> static public Equation ParseEquation(string eqText) { char[] eq = eqText.ToCharArray(); string constant = ""; Equation equation = new Equation(); for (int index = 0; index < eqText.Length; index++) { // Ignore whitespace. if (Char.IsWhiteSpace(eq[index])) { continue; } if (eq [index] == ',') { continue; } if (eq [index] == '=') { continue; } bool addConstant = false; // Determine if this looks like a constant (0-9, -, ., or e surrounded by digits). if (Char.IsDigit(eq [index]) || (Char.ToLower(eq [index]) == 'e' && constant != "") || eq[index] == '.') { constant += eq [index]; addConstant = true; // We are at the end of the constant, make it a term. } else if (constant != "") { ConstantTerm ct = new ConstantTerm(Double.Parse(constant)); equation.SetNextTerm(ct); constant = ""; } // We are at the end of the string. Turn the last constant into a term if applicable. if (index + 1 == eqText.Length && constant != "") { ConstantTerm ct = new ConstantTerm(Double.Parse(constant)); equation.SetNextTerm(ct); constant = ""; } if (eq [index] == '-') { int k = index + 1; while (k < eqText.Length) { if (!Char.IsWhiteSpace(eq [k])) { break; } k++; } if (k >= eqText.Length) { throw new ParsingException("Ends with a - sign."); } // First term is negative or it is part of a negative exponent in a constant. if (Char.IsDigit(eq [k]) && (equation.GetPrevTerm(-1) == null || constant != "")) { constant += eq [index]; addConstant = true; } else { int j = index - 1; while (j > 0) { if (!Char.IsWhiteSpace(eq [j])) { break; } j--; } // First term, but not followed by digit. if (j < 0) { j = 0; } // Not a digit and not a paren, must be an operation, so this must be a negation. if (!Char.IsDigit(eq [j]) && eq[j] != ')') { if (Char.IsDigit(eq [k])) { constant += eq [index]; addConstant = true; } else { NegationTerm neg = new NegationTerm(); equation.SetNextTerm(neg); continue; } } } } // We have a parenthetical statement. Find the end of this parenthesis and parse it as its own equation. if (eq [index] == '(') { int nesting = 1; int j = index + 1; for (; j < eqText.Length; j++) { if (eq [j] == '(') { nesting++; } if (eq [j] == ')') { nesting--; } if (nesting == 0) { break; } } equation.SetNextTerm(ParseEquation(eqText.Substring(index + 1, j - index - 1))); index = j; // Ignore the end parenthesis. } else if (eq [index] == ')') { } else if (eq [index] == '^') { ExponentTerm exp = new ExponentTerm(equation.GetPrevTerm((int)OperatorPriority.EXPONENT)); equation.SetNextTerm(exp); } else if (eq [index] == '*') { MultiplyTerm mult = new MultiplyTerm(equation.GetPrevTerm((int)OperatorPriority.MULTIPLY)); equation.SetNextTerm(mult); } else if (eq [index] == '/') { DivideTerm div = new DivideTerm(equation.GetPrevTerm((int)OperatorPriority.DIVISION)); equation.SetNextTerm(div); } else if (eq [index] == '%') { ModuloTerm div = new ModuloTerm(equation.GetPrevTerm((int)OperatorPriority.DIVISION)); equation.SetNextTerm(div); } else if (eq [index] == '+') { AdditionTerm addition = new AdditionTerm(equation.GetPrevTerm((int)OperatorPriority.ADDITION)); equation.SetNextTerm(addition); } else if (eq [index] == '-' && constant == "") { SubtractionTerm sub = new SubtractionTerm(equation.GetPrevTerm((int)OperatorPriority.SUBTRACTION)); equation.SetNextTerm(sub); } else if (eq [index] == 'l' && eq [index + 1] == 'n') { NaturalLogTerm ln = new NaturalLogTerm(); equation.SetNextTerm(ln); index += 1; } else if (eq [index] == 's' && eq [index + 1] == 'i' && eq [index + 2] == 'n') { if (eq [index + 3] == 'h') { SinhTerm sin = new SinhTerm(); equation.SetNextTerm(sin); index += 1; } else { SinTerm sin = new SinTerm(); equation.SetNextTerm(sin); } index += 2; } else if (eq [index] == 's' && eq [index + 1] == 'q' && eq [index + 2] == 'r' && eq [index + 3] == 't') { SqrtTerm sqrt = new SqrtTerm(); equation.SetNextTerm(sqrt); index += 3; } else if (eq [index] == 'c' && eq [index + 1] == 'o' && eq [index + 2] == 's') { if (eq [index + 3] == 'h') { CoshTerm cosh = new CoshTerm(); equation.SetNextTerm(cosh); index += 1; } else { CosTerm cos = new CosTerm(); equation.SetNextTerm(cos); } index += 2; } else if (eq [index] == 't' && eq [index + 1] == 'a' && eq [index + 2] == 'n') { if (eq [index + 3] == 'h') { TanhTerm tanh = new TanhTerm(); equation.SetNextTerm(tanh); index += 1; } else { TanTerm tan = new TanTerm(); equation.SetNextTerm(tan); } index += 2; } else if (eq [index] == 'p' && eq [index + 1] == 'i') { ConstantTerm ct = new ConstantTerm(Math.PI); equation.SetNextTerm(ct); index += 1; } else if (eq [index] == 'x') { XTerm xt = new XTerm(); equation.SetNextTerm(xt); } else if (eq [index] == 'e' && constant == "") { ConstantTerm ct = new ConstantTerm(Math.E); equation.SetNextTerm(ct); } else if (eq [index] == 'a') { index += 1; if (eq [index] == 's' && eq [index + 1] == 'i' && eq [index + 2] == 'n') { ASinTerm asin = new ASinTerm(); equation.SetNextTerm(asin); index += 2; } else if (eq [index] == 'c' && eq [index + 1] == 'o' && eq [index + 2] == 's') { ACosTerm acos = new ACosTerm(); equation.SetNextTerm(acos); index += 2; } else if (eq [index] == 't' && eq [index + 1] == 'a' && eq [index + 2] == 'n') { ATanTerm atan = new ATanTerm(); equation.SetNextTerm(atan); index += 2; } } else if (!addConstant) { int start = index; if (start > 5) { start -= 5; } else { start = 0; } int end = index; if (end + 5 < eqText.Length) { end += 5; } else { end = eqText.Length - 1; } throw new ParsingException("Unable to parse equation at " + index + ". Unknown character near:" + eqText.Substring(start, end)); } } if (!equation.IsComplete()) { throw new ParsingException("Incomplete equation, maybe missing a term?"); } return(equation); }
private void OnGraphWindow(int windowId) { bool draw = false; GUILayout.BeginVertical(); GUILayout.BeginHorizontal(); GUILayout.Label("Lines", _labelStyle); if (GUILayout.Button("+", _buttonStyle) && _graphLineCnt < _lineColor.Length) { _graphLineCnt++; } if (GUILayout.Button("-", _buttonStyle) && _graphLineCnt > 1) { _graphLineCnt--; _graphPos.height = 0.0f; if (_graphLines.ContainsKey(_graphLineCnt)) { _graphLines.Remove(_graphLineCnt); } draw = true; } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); bool lockX = GUILayout.Toggle(_lockX, "Lock X"); if (lockX != _lockX) { _refreshGraph = true; _lockX = lockX; } bool lockY = GUILayout.Toggle(_lockY, "Lock Y"); if (lockY != _lockY) { _refreshGraph = true; _lockY = lockY; } GUILayout.EndHorizontal(); try { if (_lockX) { GUILayout.BeginHorizontal(); string updated = ""; string minPos = "XMin"; GUILayout.Label("Xmin:", _labelStyle, GUILayout.Width(40.0f)); GUI.SetNextControlName(minPos); updated = GUILayout.TextField( _graphLineEdit == -1 ? _xmin : GetValue(_xmin), _textFieldStyle, GUILayout.MinWidth(40.0f)); if (GUI.GetNameOfFocusedControl() == minPos) { if (_graphLineEdit == -1 && updated != _xmin) { _xmin = updated; draw = true; } _graphLineEdit = -1; } GUILayout.Label("Xmax:", _labelStyle, GUILayout.Width(40.0f)); string maxPos = "XMax"; GUI.SetNextControlName(maxPos); updated = GUILayout.TextField( _graphLineEdit == -1 ? _xmax : GetValue(_xmax), _textFieldStyle, GUILayout.MinWidth(40.0f)); if (GUI.GetNameOfFocusedControl() == maxPos) { if (_graphLineEdit == -1 && updated != _xmax) { _xmax = updated; draw = true; } _graphLineEdit = -1; } GUILayout.EndHorizontal(); } for (int line = 0; line < _graphLineCnt; line++) { if (!_graphLines.ContainsKey(line)) { _graphLines.Add(line, new GraphLine()); _graphLines [line].LineColor = _lineColor [line]; } GUILayout.BeginHorizontal(); GUIStyle temp = new GUIStyle(_labelStyle); Texture2D color = new Texture2D(1, 1); color.wrapMode = TextureWrapMode.Repeat; color.SetPixel(0, 0, _graphLines [line].LineColor); color.Apply(); temp.normal.background = color; GUILayout.Label("", temp, GUILayout.Width(20.0f)); string cPos = "CG" + line; GUI.SetNextControlName(cPos); string updated = GUILayout.TextField( _graphLines [line].Content, _textFieldStyle, GUILayout.MinWidth(150.0f)); if (GUI.GetNameOfFocusedControl() == cPos) { if (_graphLineEdit == line) { _graphLines [line].Content = updated; } _graphLineEdit = line; } if (!_lockX) { string minPos = "MinG" + line; GUI.SetNextControlName(minPos); updated = GUILayout.TextField( _graphLineEdit == line ? _graphLines [line].XMin : GetValue(_graphLines [line].XMin), _textFieldStyle, GUILayout.MinWidth(20.0f)); if (GUI.GetNameOfFocusedControl() == minPos) { if (_graphLineEdit == line) { _graphLines [line].XMin = updated; } _graphLineEdit = line; } string maxPos = "MaxG" + line; GUI.SetNextControlName(maxPos); updated = GUILayout.TextField( _graphLineEdit == line ? _graphLines [line].XMax : GetValue(_graphLines [line].XMax), _textFieldStyle, GUILayout.MinWidth(20.0f)); if (GUI.GetNameOfFocusedControl() == maxPos) { if (_graphLineEdit == line) { _graphLines [line].XMax = updated; } _graphLineEdit = line; } } else { _graphLines [line].XMin = _xmin; _graphLines [line].XMax = _xmax; } GUILayout.EndHorizontal(); if (_slider > 1e-2d) { GUILayout.BeginHorizontal(); GUILayout.Label("X:" + _graphLines[line].x.ToString("0.0###"), _labelStyle); GUILayout.Label("Y:" + _graphLines[line].y.ToString("0.0###"), _labelStyle); GUILayout.EndHorizontal(); } draw = draw || _graphLines [line].Dirty; } float slider = GUILayout.HorizontalSlider(_slider, 0.0f, 300.0f); if (slider != _slider) { _slider = slider; draw = true; _graphPos.height = 0.0f; } if (draw) { _ymax = Double.NaN; _ymin = Double.NaN; } if (draw || _refreshGraph) { _refreshGraph = false; _graph.reset(); for (int line = 0; line < _graphLineCnt; line++) { _graphLines[line].Dirty = false; if (_graphLines [line].Content.Length == 0) { continue; } if (_graphLines [line].XMin.Length == 0) { continue; } if (_graphLines [line].XMax.Length == 0) { continue; } Equation equation = Equation.ParseEquation(EvalCell(_graphLines [line].Content)); Equation xmin = Equation.ParseEquation(EvalCell(_graphLines [line].XMin)); Equation xmax = Equation.ParseEquation(EvalCell(_graphLines [line].XMax)); double min = xmin.GetValue(); double max = xmax.GetValue(); double ymin = Double.NaN; double ymax = Double.NaN; if (max <= min || Double.IsNaN(max) || Double.IsNaN(min)) { continue; } double delta = (max - min) / 300.0d; int px = 0; for (double x = min; x <= max; x += delta) { double y = equation.GetValue(x); if (Double.IsNaN(y)) { continue; } if (Double.IsNaN(ymin) || y < ymin) { ymin = y; } if (Double.IsNaN(ymax) || y > ymax) { ymax = y; } if (px == (int)_slider) { _graphLines[line].x = x; _graphLines[line].y = y; } px++; } if (Double.IsNaN(_ymin) || ymin < _ymin) { _ymin = ymin; _refreshGraph = true; } if (Double.IsNaN(_ymax) || ymax > _ymax) { _ymax = ymax; _refreshGraph = true; } if (_lockY) { ymin = _ymin; ymax = _ymax; } _graph.drawLineOnGraph(x => equation.GetValue(min + x * delta), ymax, ymin, 300, _graphLines [line].LineColor); } if (_slider > 1e-2d) { _graph.drawVerticalLine((int)_slider, Color.grey); } _graph.Apply(); } } catch (Exception e) { Debug.Log(e.Message + "\n" + e.StackTrace); } if (_graph != null) { GUILayout.Label("Y max:" + _ymax.ToString("G10"), _labelStyle); GUILayout.Box(_graph.getImage()); GUILayout.Label("Y min:" + _ymin.ToString("G10"), _labelStyle); } GUILayout.EndVertical(); GUI.DragWindow(); }