Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.TreeSimplifier/HeuristicLab.Problems.DataAnalysis.Symbolic.Views/3.4/InteractiveSymbolicExpressionTreeChart.cs @ 8935

Last change on this file since 8935 was 8935, checked in by bburlacu, 12 years ago

#1763: Bugfixes and refactoring as suggested in the comments above.

File size: 13.7 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Drawing;
24using System.Linq;
25using System.Windows.Forms;
26using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
27using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views;
28
29namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Views {
30  public partial class InteractiveSymbolicExpressionTreeChart : SymbolicExpressionTreeChart {
31    private ISymbolicExpressionTreeNode tempNode;
32    private VisualSymbolicExpressionTreeNode lastSelected; // previously selected node
33    private VisualSymbolicExpressionTreeNode currSelected; // currently selected node
34    private enum EditOp { NoOp, CopyNode, CopySubtree, CutNode, CutSubtree, DeleteNode, DeleteSubtree }
35    private enum TreeState { Valid, Invalid }
36    private EditOp lastOp = EditOp.NoOp;
37    private TreeState treeState = TreeState.Valid; // tree edit operations must leave the tree in a valid state
38
39    public InteractiveSymbolicExpressionTreeChart() {
40      InitializeComponent();
41      // add extra actions in the context menu strips
42
43
44      lastSelected = null;
45      currSelected = null;
46      tempNode = null;
47    }
48
49    public bool TreeValid { get { return TreeState.Valid == treeState; } }
50    // expose an additional event for signaling to the parent view when the tree structure was modified
51    // the emitting of the signal is conditional on the tree being valid, otherwise only a Repaint is issued
52    public event EventHandler SymbolicExpressionTreeChanged;
53    private void OnSymbolicExpressionTreeChanged(object sender, EventArgs e) {
54      if (IsValid(Tree)) {
55        treeStatusValue.Text = "Valid";
56        treeStatusValue.ForeColor = Color.Green;
57        treeState = TreeState.Valid;
58        var changed = SymbolicExpressionTreeChanged;
59        if (changed != null)
60          changed(sender, e);
61      } else {
62        treeStatusValue.Text = "Invalid";
63        treeStatusValue.ForeColor = Color.Red;
64        treeState = TreeState.Invalid;
65        Repaint();
66      }
67    }
68
69    private void contextMenuStrip_Opened(object sender, EventArgs e) {
70      var menu = sender as ContextMenuStrip;
71      if (menu == null) return;
72      if (currSelected == null) {
73        insertNodeToolStripMenuItem.Visible = false;
74        changeValueToolStripMenuItem.Visible = false;
75        copyToolStripMenuItem.Visible = false;
76        cutToolStripMenuItem.Visible = false;
77        deleteToolStripMenuItem.Visible = false;
78        pasteToolStripMenuItem.Visible = false;
79      } else {
80        var node = currSelected.SymbolicExpressionTreeNode;
81        changeValueToolStripMenuItem.Visible = (node is SymbolicExpressionTreeTerminalNode);
82        insertNodeToolStripMenuItem.Visible = !changeValueToolStripMenuItem.Visible;
83        copyToolStripMenuItem.Visible = true;
84        cutToolStripMenuItem.Visible = true;
85        deleteToolStripMenuItem.Visible = true;
86        pasteToolStripMenuItem.Visible = tempNode != null && insertNodeToolStripMenuItem.Visible;
87      }
88    }
89
90    protected override void OnSymbolicExpressionTreeNodeClicked(object sender, MouseEventArgs e) {
91      var visualTreeNode = FindVisualSymbolicExpressionTreeNodeAt(e.X, e.Y);
92      if (visualTreeNode == null || visualTreeNode == currSelected) return;
93      lastSelected = currSelected;
94      if (lastSelected != null)
95        lastSelected.LineColor = Color.Black;
96      currSelected = visualTreeNode;
97      currSelected.LineColor = Color.LightGreen;
98      Repaint();
99      base.OnSymbolicExpressionTreeNodeClicked(sender, e);
100    }
101
102    private void insertNodeToolStripMenuItem_Click(object sender, EventArgs e) {
103      if (currSelected == null || currSelected.SymbolicExpressionTreeNode is SymbolicExpressionTreeTerminalNode) return;
104      var node = currSelected.SymbolicExpressionTreeNode;
105
106      using (var dialog = new InsertNodeDialog()) {
107        dialog.SetAllowedSymbols(node.Grammar.AllowedSymbols.Where(s => s.Enabled && s.InitialFrequency > 0.0 && !(s is ProgramRootSymbol || s is StartSymbol || s is Defun)));
108        dialog.DialogValidated += InsertNodeDialog_Validated;
109        dialog.ShowDialog(this);
110      }
111    }
112
113    private void changeValueToolStripMenuItem_Click(object sender, EventArgs e) {
114      if (currSelected == null) return;
115      var node = currSelected.SymbolicExpressionTreeNode;
116      using (var dialog = new ValueChangeDialog()) {
117        dialog.SetContent(node);
118        dialog.DialogValidated += ChangeValueDialog_Validated;
119        dialog.ShowDialog(this);
120      }
121    }
122
123    public event EventHandler SymbolicExpressionTreeNodeChanged;
124    private void OnSymbolicExpressionTreeNodeChanged(object sender, EventArgs e) {
125      var changed = SymbolicExpressionTreeNodeChanged;
126      if (changed != null)
127        SymbolicExpressionTreeNodeChanged(sender, e);
128    }
129
130    private void ChangeValueDialog_Validated(object sender, EventArgs e) {
131      OnSymbolicExpressionTreeNodeChanged(sender, e);
132    }
133
134    private void InsertNodeDialog_Validated(object sender, EventArgs e) {
135      var dialog = (InsertNodeDialog)sender;
136      var symbol = dialog.SelectedSymbol();
137      var node = symbol.CreateTreeNode();
138      var parent = currSelected.SymbolicExpressionTreeNode;
139      if (node is ConstantTreeNode) {
140        var constant = node as ConstantTreeNode;
141        constant.Value = double.Parse(dialog.constantValueTextBox.Text);
142      } else if (node is VariableTreeNode) {
143        var variable = node as VariableTreeNode;
144        variable.Weight = double.Parse(dialog.variableWeightTextBox.Text);
145        variable.VariableName = dialog.variableNamesCombo.Text;
146      } else {
147        if (node.Symbol.MinimumArity <= parent.SubtreeCount && node.Symbol.MaximumArity >= parent.SubtreeCount) {
148          for (int i = parent.SubtreeCount - 1; i >= 0; --i) {
149            var child = parent.GetSubtree(i);
150            parent.RemoveSubtree(i);
151            node.AddSubtree(child);
152          }
153        }
154      }
155      if (parent.Symbol.MaximumArity > parent.SubtreeCount) {
156        parent.AddSubtree(node);
157        Tree = Tree;
158      }
159      OnSymbolicExpressionTreeChanged(sender, e);
160    }
161
162    private void cutNodeToolStripMenuItem_Click(object sender, EventArgs e) {
163      lastOp = EditOp.CutNode;
164      tempNode = currSelected.SymbolicExpressionTreeNode;
165      var visualNode = visualTreeNodes[tempNode];
166      visualNode.LineColor = Color.LightGray;
167      visualNode.TextColor = Color.LightGray;
168      Repaint();
169    }
170
171    private void cutSubtreeToolStripMenuItem_Click(object sender, EventArgs e) {
172      lastOp = EditOp.CutSubtree;
173      tempNode = currSelected.SymbolicExpressionTreeNode; // should never be null
174      foreach (var node in tempNode.IterateNodesPostfix()) {
175        var visualNode = visualTreeNodes[node];
176        visualNode.LineColor = Color.LightGray;
177        visualNode.TextColor = Color.LightGray;
178        if (node.SubtreeCount > 0) {
179          foreach (var subtree in node.Subtrees) {
180            var visualLine = visualLines[new Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>(node, subtree)];
181            visualLine.LineColor = Color.LightGray;
182          }
183        }
184      }
185      Repaint();
186    }
187
188    private void copyNodeToolStripMenuItem_Click(object sender, EventArgs e) {
189      lastOp = EditOp.CopyNode;
190      tempNode = currSelected.SymbolicExpressionTreeNode;
191      currSelected.LineColor = Color.LightGray;
192      currSelected.TextColor = Color.LightGray;
193      Repaint();
194    }
195
196    private void copySubtreeToolStripMenuItem_Click(object sender, EventArgs e) {
197      lastOp = EditOp.CopySubtree;
198      tempNode = currSelected.SymbolicExpressionTreeNode;
199      foreach (var node in tempNode.IterateNodesPostfix()) {
200        var visualNode = visualTreeNodes[node];
201        visualNode.LineColor = Color.LightGray;
202        visualNode.TextColor = Color.LightGray;
203        if (node.SubtreeCount <= 0) continue;
204        foreach (var subtree in node.Subtrees) {
205          var visualLine = visualLines[new Tuple<ISymbolicExpressionTreeNode, ISymbolicExpressionTreeNode>(node, subtree)];
206          visualLine.LineColor = Color.LightGray;
207        }
208      }
209      Repaint();
210    }
211
212    private void deleteNodeToolStripMenuItem_Click(object sender, EventArgs e) {
213      lastOp = EditOp.DeleteNode;
214      var node = currSelected.SymbolicExpressionTreeNode;
215      var parent = node.Parent;
216      if (parent == null || parent.Symbol is StartSymbol || parent.Symbol is ProgramRootSymbol)
217        return; // the operation would result in the deletion of the entire tree
218      if (parent.Symbol.MaximumArity >= node.SubtreeCount + parent.SubtreeCount - 1) { // -1 because tempNode will be removed
219        parent.RemoveSubtree(parent.IndexOfSubtree(node));
220        for (int i = node.SubtreeCount - 1; i >= 0; --i) {
221          var child = node.GetSubtree(i);
222          node.RemoveSubtree(i);
223          parent.AddSubtree(child);
224        }
225      }
226      currSelected = null; // because the currently selected node was just deleted
227      OnSymbolicExpressionTreeChanged(sender, e);
228    }
229
230    private void deleteSubtreeToolStripMenuItem_Click(object sender, EventArgs e) {
231      lastOp = EditOp.DeleteSubtree;
232      var node = currSelected.SymbolicExpressionTreeNode;
233      var parent = node.Parent;
234      if (parent == null || parent.Symbol is StartSymbol || parent.Symbol is ProgramRootSymbol)
235        return; // the operation makes no sense as it would result in the deletion of the entire tree
236      parent.RemoveSubtree(parent.IndexOfSubtree(node));
237      currSelected = null; // because the currently selected node was just deleted
238      OnSymbolicExpressionTreeChanged(sender, e);
239    }
240
241    private void pasteToolStripMenuItem_Clicked(object sender, EventArgs e) {
242      if (!(lastOp == EditOp.CopyNode || lastOp == EditOp.CopySubtree || lastOp == EditOp.CutNode || lastOp == EditOp.CutSubtree))
243        return;
244      // check if the copied/cut node (stored in the tempNode) can be inserted as a child of the current selected node
245      var node = currSelected.SymbolicExpressionTreeNode;
246      if (node is ConstantTreeNode || node is VariableTreeNode) return; // nothing to do
247      // check if the currently selected node can accept the copied node as a child
248      // no need to check the grammar, an arity check will do just fine here
249      if (node.Symbol.MaximumArity > node.SubtreeCount) {
250        switch (lastOp) {
251          case (EditOp.CutNode): {
252              // when cutting a node from the tree, it's children become children of it's parent
253              var parent = tempNode.Parent;
254              // arity checks to see if parent can accept node's children (we assume the grammar is already ok with that)
255              // (otherise, the 'cut' part of the operation will just not do anything)
256              if (parent.Symbol.MaximumArity >= tempNode.SubtreeCount + parent.SubtreeCount - 1) {
257                // -1 because tempNode will be removed
258                parent.RemoveSubtree(parent.IndexOfSubtree(tempNode));
259                for (int i = tempNode.SubtreeCount - 1; i >= 0; --i) {
260                  var child = tempNode.GetSubtree(i);
261                  tempNode.RemoveSubtree(i);
262                  parent.AddSubtree(child);
263                }
264                lastOp = EditOp.CopyNode;
265                currSelected = null;
266              }
267              break;
268            }
269          case (EditOp.CutSubtree): {
270              // cut subtree
271              var parent = tempNode.Parent;
272              parent.RemoveSubtree(parent.IndexOfSubtree(tempNode));
273              lastOp = EditOp.CopySubtree; // do this so the next paste will actually perform a copy   
274              currSelected = null;
275              break;
276            }
277          case (EditOp.CopyNode): {
278              // copy node
279              var clone = (SymbolicExpressionTreeNode)tempNode.Clone(); // should never be null
280              clone.Parent = tempNode.Parent;
281              tempNode = clone;
282              for (int i = tempNode.SubtreeCount - 1; i >= 0; --i) tempNode.RemoveSubtree(i);
283              break;
284            }
285          case (EditOp.CopySubtree): {
286              // copy subtree
287              var clone = (SymbolicExpressionTreeNode)tempNode.Clone();
288              clone.Parent = tempNode.Parent;
289              tempNode = clone;
290              break;
291            }
292        }
293        node.AddSubtree(tempNode);
294        Tree = Tree; // hack in order to trigger the reinitialization of the dictionaries after new nodes appeared in the graph
295        OnSymbolicExpressionTreeChanged(sender, e);
296      }
297    }
298
299    private bool IsValid(ISymbolicExpressionTree tree) {
300      if (tree.IterateNodesPostfix().Any(node => node.SubtreeCount < node.Symbol.MinimumArity || node.SubtreeCount > node.Symbol.MaximumArity)) {
301        treeState = TreeState.Invalid;
302        return false;
303      }
304      treeState = TreeState.Valid;
305      return true;
306    }
307  }
308}
Note: See TracBrowser for help on using the repository browser.