source: trunk/sources/HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views/3.4/SymbolicExpressionGrammarAllowedChildSymbolsControl.cs @ 10520

Last change on this file since 10520 was 10520, checked in by bburlacu, 5 years ago

#2076: Got rid of layout adapters. Extracted the previous drawing code and made it into another layout engine called the BoxesLayoutEngine (because it divides the areas necessary for each subtree into boxes and recursively applies the layout). Simplified usage of layout engine so that most of the things are handled internally, and the user just has to provide some lambdas telling the engine how to navigate the original tree. Added context option in the SymbolicExpressionTreeChart to choose which layout engine to use for tree drawing. Moved the SymbolicExpressionTreeLatexFormatter to the HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views assembly because it depends on the layout engine.

File size: 16.1 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2013 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.Collections.Generic;
24using System.Drawing;
25using System.Linq;
26using System.Windows.Forms;
27using HeuristicLab.Collections;
28using HeuristicLab.Common;
29using HeuristicLab.PluginInfrastructure;
30
31using VisualSymbolicExpressionTreeNode = HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views.VisualTreeNode<HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.ISymbolicExpressionTreeNode>;
32
33namespace HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views {
34  public sealed partial class SymbolicExpressionGrammarAllowedChildSymbolsControl : UserControl {
35    private ObservableList<ISymbolicExpressionTreeNode> selectedSymbolicExpressionTreeNodes;
36
37    public SymbolicExpressionGrammarAllowedChildSymbolsControl() {
38      InitializeComponent();
39      selectedSymbolicExpressionTreeNodes = new ObservableList<ISymbolicExpressionTreeNode>();
40    }
41
42    private ISymbolicExpressionGrammar grammar;
43    public ISymbolicExpressionGrammar Grammar {
44      get { return grammar; }
45      set {
46        if (grammar != value) {
47          if (grammar != null) DeregisterGrammarEvents();
48          grammar = value;
49          if (grammar != null) RegisterGrammarEvents();
50          OnGrammarChanged();
51        }
52      }
53    }
54
55    private ISymbol symbol;
56    public ISymbol Symbol {
57      get { return symbol; }
58      set {
59        if (symbol != value) {
60          if (value != null && grammar == null) throw new InvalidOperationException("grammar is null");
61          if (value != null && !grammar.ContainsSymbol(value)) throw new ArgumentException("grammar does not contain symbol.");
62          symbol = value;
63          OnSymbolChanged();
64        }
65      }
66    }
67
68    private void RegisterGrammarEvents() {
69      grammar.Changed += new EventHandler(Grammar_Changed);
70      grammar.ReadOnlyChanged += new EventHandler(Grammar_ReadOnlyChanged);
71    }
72    private void DeregisterGrammarEvents() {
73      grammar.Changed -= new EventHandler(Grammar_Changed);
74      grammar.ReadOnlyChanged -= new EventHandler(Grammar_ReadOnlyChanged);
75    }
76
77    private void Grammar_Changed(object sender, EventArgs e) {
78      if (Grammar == null) return;
79      if (Symbol == null) return;
80      if (Symbol != null && !Grammar.ContainsSymbol(Symbol)) Symbol = null;
81      else BuildAllowedChildSymbolsTree();
82    }
83
84    private void Grammar_ReadOnlyChanged(object sender, EventArgs e) {
85      if (InvokeRequired) Invoke((MethodInvoker)BuildAllowedChildSymbolsTree);
86      else BuildAllowedChildSymbolsTree();
87    }
88
89    private void OnGrammarChanged() {
90      if (Grammar == null) {
91        symbolicExpressionTreeChart.Tree = null;
92        Symbol = null;
93      }
94    }
95    private void OnSymbolChanged() {
96      if (Symbol == null) symbolicExpressionTreeChart.Tree = null;
97      else BuildAllowedChildSymbolsTree();
98    }
99
100    private void BuildAllowedChildSymbolsTree() {
101      if (Symbol == null) {
102        symbolicExpressionTreeChart.Tree = null;
103        return;
104      }
105
106      var tree = new SymbolicExpressionTree(new SymbolicExpressionTreeNode(Symbol));
107      symbolicExpressionTreeChart.SuspendRepaint = true;
108      if (Grammar.GetMaximumSubtreeCount(Symbol) > 0) {
109        for (int i = 0; i < Grammar.GetMaximumSubtreeCount(Symbol); i++) {
110          var node = new DummySymbol("Subtree " + i).CreateTreeNode();
111          var groupSymbols = grammar.GetAllowedChildSymbols(Symbol, i).OfType<GroupSymbol>().ToList();
112          foreach (var childSymbol in Grammar.GetAllowedChildSymbols(Symbol, i)) {
113            if (!groupSymbols.Any(g => g != childSymbol && g.Flatten().Contains(childSymbol)))
114              node.AddSubtree(new SymbolicExpressionTreeNode(childSymbol));
115          }
116          tree.Root.AddSubtree(node);
117        }
118      }
119      symbolicExpressionTreeChart.Tree = tree;
120
121      foreach (var subtreeNode in tree.Root.Subtrees) {
122        foreach (var allowedChildNode in subtreeNode.Subtrees) {
123          var visualLine = symbolicExpressionTreeChart.GetVisualSymbolicExpressionTreeNodeConnection(subtreeNode, allowedChildNode);
124          visualLine.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
125        }
126      }
127
128      for (int i = Grammar.GetMinimumSubtreeCount(symbol); i < Grammar.GetMaximumSubtreeCount(symbol); i++) {
129        var subtreeNode = tree.Root.GetSubtree(i);
130        var visualTreeNode = symbolicExpressionTreeChart.GetVisualSymbolicExpressionTreeNode(subtreeNode);
131        visualTreeNode.TextColor = Color.Gray;
132        visualTreeNode.LineColor = Color.LightGray;
133
134        var visualLine = symbolicExpressionTreeChart.GetVisualSymbolicExpressionTreeNodeConnection(tree.Root, subtreeNode);
135        visualLine.LineColor = Color.LightGray;
136
137        foreach (var allowedChildNode in subtreeNode.Subtrees) {
138          visualTreeNode = symbolicExpressionTreeChart.GetVisualSymbolicExpressionTreeNode(allowedChildNode);
139          visualTreeNode.TextColor = Color.Gray;
140          visualTreeNode.LineColor = Color.LightGray;
141
142          visualLine = symbolicExpressionTreeChart.GetVisualSymbolicExpressionTreeNodeConnection(subtreeNode, allowedChildNode);
143          visualLine.LineColor = Color.LightGray;
144        }
145      }
146
147      symbolicExpressionTreeChart.SuspendRepaint = false;
148      UpdateSelectedSymbolicExpressionTreeNodes();
149    }
150
151    private void UpdateSelectedSymbolicExpressionTreeNodes() {
152      foreach (var node in symbolicExpressionTreeChart.Tree.IterateNodesPrefix()) {
153        var visualNode = symbolicExpressionTreeChart.GetVisualSymbolicExpressionTreeNode(node);
154        if (!selectedSymbolicExpressionTreeNodes.Contains(node)) visualNode.FillColor = Color.White;
155        else visualNode.FillColor = Color.LightSteelBlue;
156      }
157      symbolicExpressionTreeChart.Repaint();
158    }
159
160    private void symbolicExpressionTreeChart_SymbolicExpressionTreeNodeClicked(object sender, MouseEventArgs e) {
161      if (Grammar.ReadOnly) return;
162      if ((Control.ModifierKeys & Keys.Control) == 0)
163        selectedSymbolicExpressionTreeNodes.Clear();
164
165      VisualSymbolicExpressionTreeNode clickedNode = (VisualSymbolicExpressionTreeNode)sender;
166      var selectedNode = clickedNode.Content;
167      if (selectedNode.SubtreeCount == 0) {
168        if (!selectedSymbolicExpressionTreeNodes.Contains(selectedNode))
169          selectedSymbolicExpressionTreeNodes.Add(selectedNode);
170        else
171          selectedSymbolicExpressionTreeNodes.Remove(selectedNode);
172      }
173
174      UpdateSelectedSymbolicExpressionTreeNodes();
175    }
176
177    private void symbolicExpressionTreeChart_KeyDown(object sender, KeyEventArgs e) {
178      if (Grammar.ReadOnly) return;
179      if (e.KeyCode == Keys.Delete) {
180        var root = symbolicExpressionTreeChart.Tree.Root;
181        Grammar.StartGrammarManipulation();
182        foreach (var node in selectedSymbolicExpressionTreeNodes) {
183          int argIndex = root.IndexOfSubtree(node.Parent);
184          Grammar.RemoveAllowedChildSymbol(root.Symbol, node.Symbol, argIndex);
185        }
186
187        selectedSymbolicExpressionTreeNodes.Clear();
188        Grammar.FinishedGrammarManipulation();
189      }
190    }
191
192    #region drag & drop operations
193    private bool validDragOperation;
194    private void symbolicExpressionTreeChart_DragEnter(object sender, DragEventArgs e) {
195      validDragOperation = false;
196      if (Grammar.ReadOnly) return;
197
198      var data = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
199      var symbol = data as ISymbol;
200      var symbols = data as IEnumerable<ISymbol>;
201      if (symbol != null && !(symbol is IReadOnlySymbol) && Grammar.ContainsSymbol(symbol)) validDragOperation = true;
202      else if (symbols != null && symbols.All(s => !(symbol is IReadOnlySymbol) && Grammar.ContainsSymbol(s))) validDragOperation = true;
203    }
204
205    private void symbolicExpressionTreeChart_DragOver(object sender, DragEventArgs e) {
206      e.Effect = DragDropEffects.None;
207      if (validDragOperation) {
208        Point coordinates = symbolicExpressionTreeChart.PointToClient(new Point(e.X, e.Y));
209        var visualNode = symbolicExpressionTreeChart.FindVisualSymbolicExpressionTreeNodeAt(coordinates.X, coordinates.Y);
210        if (visualNode != null) {
211          var node = visualNode.Content;
212          var root = symbolicExpressionTreeChart.Tree.Root;
213          if (node == root || node.Parent == root) e.Effect = DragDropEffects.Copy;
214        }
215      }
216    }
217
218    private void symbolicExpressionTreeChart_DragDrop(object sender, DragEventArgs e) {
219      Point coordinates = symbolicExpressionTreeChart.PointToClient(new Point(e.X, e.Y));
220      var node = symbolicExpressionTreeChart.FindVisualSymbolicExpressionTreeNodeAt(coordinates.X, coordinates.Y);
221      var root = symbolicExpressionTreeChart.Tree.Root;
222
223      var data = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
224      var symbol = data as ISymbol;
225      var symbols = data as IEnumerable<ISymbol>;
226
227      if (node.Content == root) {
228        if (symbol != null)
229          Grammar.AddAllowedChildSymbol(root.Symbol, symbol);
230        else if (symbols != null)
231          foreach (var s in symbols) Grammar.AddAllowedChildSymbol(root.Symbol, s);
232      } else {
233        int argumentIndex = root.IndexOfSubtree(node.Content);
234        if (symbol != null)
235          Grammar.AddAllowedChildSymbol(root.Symbol, symbol, argumentIndex);
236        else if (symbols != null)
237          foreach (var s in symbols) Grammar.AddAllowedChildSymbol(root.Symbol, s, argumentIndex);
238      }
239      BuildAllowedChildSymbolsTree();
240    }
241    #endregion
242
243    #region draw and handle root node with buttons to manipulate the subtree count
244    private RectangleF increaseMinimumSubtreeCountRectangle;
245    private RectangleF decreaseMinimumSubtreeCountRectangle;
246    private RectangleF increaseMaximumSubtreeCountRectangle;
247    private RectangleF decreaseMaximumSubtreeCountRectangle;
248    private void allowedChildSymbolsControl_Paint(object sender, PaintEventArgs e) {
249      increaseMinimumSubtreeCountRectangle = RectangleF.Empty;
250      decreaseMinimumSubtreeCountRectangle = RectangleF.Empty;
251      increaseMaximumSubtreeCountRectangle = RectangleF.Empty;
252      decreaseMaximumSubtreeCountRectangle = RectangleF.Empty;
253
254      if (Grammar == null) return;
255      if (symbolicExpressionTreeChart.Tree == null) return;
256
257      var rootNode = symbolicExpressionTreeChart.Tree.Root;
258      var visualRootNode = symbolicExpressionTreeChart.GetVisualSymbolicExpressionTreeNode(rootNode);
259      var graphics = e.Graphics;
260
261      if (rootNode.Symbol is IReadOnlySymbol) return;
262      if (rootNode.Symbol.MinimumArity == rootNode.Symbol.MaximumArity) return;
263
264      using (Pen pen = new Pen(Color.Black)) {
265        using (Font font = new Font("Times New Roman", 8)) {
266          var stringFormat = new StringFormat();
267          stringFormat.Alignment = StringAlignment.Center;
268          stringFormat.LineAlignment = StringAlignment.Center;
269          int spacing = 5;
270          int size = (visualRootNode.Height - spacing * 3) / 2;
271
272          increaseMinimumSubtreeCountRectangle = new RectangleF(visualRootNode.X - spacing - size, visualRootNode.Y + spacing, size, size);
273          decreaseMinimumSubtreeCountRectangle = new RectangleF(visualRootNode.X - spacing - size, visualRootNode.Y + size + 2 * spacing, size, size);
274          increaseMaximumSubtreeCountRectangle = new RectangleF(visualRootNode.X + visualRootNode.Width + spacing, visualRootNode.Y + spacing, size, size);
275          decreaseMaximumSubtreeCountRectangle = new RectangleF(visualRootNode.X + visualRootNode.Width + spacing, visualRootNode.Y + size + 2 * spacing, size, size);
276
277          pen.Color = Grammar.ReadOnly || Grammar.GetMaximumSubtreeCount(rootNode.Symbol) == Grammar.GetMinimumSubtreeCount(rootNode.Symbol) ? Color.LightGray : Color.Black;
278          graphics.DrawString("+", font, pen.Brush, increaseMinimumSubtreeCountRectangle, stringFormat);
279          graphics.DrawRectangle(pen, Rectangle.Round(increaseMinimumSubtreeCountRectangle));
280          if (pen.Color == Color.LightGray) increaseMinimumSubtreeCountRectangle = RectangleF.Empty;
281
282          pen.Color = Grammar.ReadOnly || Grammar.GetMinimumSubtreeCount(rootNode.Symbol) == rootNode.Symbol.MinimumArity ? Color.LightGray : Color.Black;
283          graphics.DrawString("-", font, pen.Brush, decreaseMinimumSubtreeCountRectangle, stringFormat);
284          graphics.DrawRectangle(pen, Rectangle.Round(decreaseMinimumSubtreeCountRectangle));
285          if (pen.Color == Color.LightGray) decreaseMinimumSubtreeCountRectangle = RectangleF.Empty;
286
287          pen.Color = Grammar.ReadOnly || Grammar.GetMaximumSubtreeCount(rootNode.Symbol) == rootNode.Symbol.MaximumArity ? Color.LightGray : Color.Black;
288          graphics.DrawRectangle(pen, Rectangle.Round(increaseMaximumSubtreeCountRectangle));
289          graphics.DrawString("+", font, pen.Brush, increaseMaximumSubtreeCountRectangle, stringFormat);
290          if (pen.Color == Color.LightGray) increaseMaximumSubtreeCountRectangle = RectangleF.Empty;
291
292          pen.Color = Grammar.ReadOnly || Grammar.GetMaximumSubtreeCount(rootNode.Symbol) == Grammar.GetMinimumSubtreeCount(rootNode.Symbol) ? Color.LightGray : Color.Black;
293          graphics.DrawRectangle(pen, Rectangle.Round(decreaseMaximumSubtreeCountRectangle));
294          graphics.DrawString("-", font, pen.Brush, decreaseMaximumSubtreeCountRectangle, stringFormat);
295          if (pen.Color == Color.LightGray) decreaseMaximumSubtreeCountRectangle = RectangleF.Empty;
296        }
297      }
298    }
299
300    private void allowedChildSymbolsControl_MouseDown(object sender, MouseEventArgs e) {
301      if (Grammar == null || Grammar.ReadOnly) return;
302      if (symbolicExpressionTreeChart.Tree == null) return;
303
304      var pointF = new PointF(e.X, e.Y);
305      var rootSymbol = symbolicExpressionTreeChart.Tree.Root.Symbol;
306      int minimumSubtreeCount = Grammar.GetMinimumSubtreeCount(rootSymbol);
307      int maximumSubtreecount = Grammar.GetMaximumSubtreeCount(rootSymbol);
308
309      bool changed = true;
310      if (increaseMinimumSubtreeCountRectangle.Contains(pointF))
311        Grammar.SetSubtreeCount(rootSymbol, minimumSubtreeCount + 1, maximumSubtreecount);
312      else if (decreaseMinimumSubtreeCountRectangle.Contains(pointF))
313        Grammar.SetSubtreeCount(rootSymbol, minimumSubtreeCount - 1, maximumSubtreecount);
314      else if (increaseMaximumSubtreeCountRectangle.Contains(pointF))
315        Grammar.SetSubtreeCount(rootSymbol, minimumSubtreeCount, maximumSubtreecount + 1);
316      else if (decreaseMaximumSubtreeCountRectangle.Contains(pointF))
317        Grammar.SetSubtreeCount(rootSymbol, minimumSubtreeCount, maximumSubtreecount - 1);
318      else
319        changed = false;
320
321      if (changed) BuildAllowedChildSymbolsTree();
322    }
323    #endregion
324
325  }
326
327  [NonDiscoverableType]
328  internal class DummySymbol : Symbol {
329    private const int minimumArity = 1;
330    private const int maximumArity = byte.MaxValue;
331
332    public override int MinimumArity {
333      get { return minimumArity; }
334    }
335    public override int MaximumArity {
336      get { return maximumArity; }
337    }
338
339    public DummySymbol(DummySymbol original, Cloner cloner) : base(original, cloner) { }
340    public DummySymbol(string name) : base(name, "DummySymbol for views") { }
341    public override IDeepCloneable Clone(Cloner cloner) { return new DummySymbol(this, cloner); }
342  }
343}
Note: See TracBrowser for help on using the repository browser.