source: src/main/java/weka/gui/treevisualizer/PlaceNode2.java @ 27

Last change on this file since 27 was 4, checked in by gnappo, 14 years ago

Import di weka.

File size: 17.6 KB
Line 
1/*
2 *    This program is free software; you can redistribute it and/or modify
3 *    it under the terms of the GNU General Public License as published by
4 *    the Free Software Foundation; either version 2 of the License, or
5 *    (at your option) any later version.
6 *
7 *    This program is distributed in the hope that it will be useful,
8 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 *    GNU General Public License for more details.
11 *
12 *    You should have received a copy of the GNU General Public License
13 *    along with this program; if not, write to the Free Software
14 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17/*
18 *    PlaceNode2.java
19 *    Copyright (C) 1999 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.gui.treevisualizer;
24
25import java.util.*;
26
27/**
28 * This class will place the Nodes of a tree. <p>
29 *
30 * It will place these nodes so that they fall at evenly below their parent.
31 * It will then go through and look for places where nodes fall on the wrong
32 * side of other nodes
33 * when it finds one it will trace back up the tree to find the first common
34 * sibling group these two nodes have
35 * And it will adjust the spacing between these two siblings so that the two
36 * nodes no longer overlap.
37 * This is nasty to calculate with , and takes a while with the current
38 * algorithm I am using to do this.<p>
39 *
40 *
41 * @author Malcolm Ware (mfw4@cs.waikato.ac.nz)
42 * @version $Revision: 1.4 $
43 */
44public class PlaceNode2 implements NodePlace {
45  /** The space each row will take up. */
46  private double m_yRatio;
47
48  /** An array that lists the groups and information about them. */
49  private Group[] m_groups;
50
51  /** An array that lists the levels and information about them. */
52  private Level[] m_levels;
53
54  /** The Number of groups the tree has */
55  private int m_groupNum;
56
57  /** The number of levels the group tree has */
58  private int m_levelNum;
59 
60  /**
61   * The Funtion to call to have the nodes arranged.
62   *
63   * @param r The top node of the tree to arrange.
64   */
65  public void place(Node r) {
66    //note i might write count groups into the node class as well as
67    //it may be useful too;
68   
69    m_groupNum = Node.getGCount(r,0); //i could swap over to the node class
70    //group count,but this works os i'm not gonna
71    m_groups = new Group[m_groupNum];
72       
73    for (int noa = 0;noa < m_groupNum;noa++) {
74      m_groups[noa] = new Group();
75      m_groups[noa].m_gap = 3;
76      m_groups[noa].m_start = -1;
77    }
78   
79    groupBuild(r);
80    m_levelNum = Node.getHeight(r,0);
81    m_yRatio = 1 / (double)(m_levelNum + 1);
82   
83    m_levels = new Level[m_levelNum];
84   
85    for (int noa = 0;noa < m_levelNum;noa++) {
86      m_levels[noa] = new Level();
87    }
88    r.setTop(m_yRatio);
89    yPlacer();
90    r.setCenter(0);
91    xPlacer(0);
92
93
94    //ok now i just have to untangle then scale down
95    //note instead of starting with coords between 1 and 0 i will
96    //use ints then scale them down
97    //i will scale them down either by all relative to the largest
98    //line or by each line individually
99
100    untangle2();
101   
102    scaleByMax();
103    //scaleByInd();
104  }
105
106
107  /*
108  private void thinner()
109  {
110    //what this function does is it retains the symmetry of the
111     // parent node about the children but the children are no longer evenly
112      //spaced this stops children from being pushed too far to the sides
113      //,note this algorithm may need the method altered as it may
114     // require heavy optimisation to go at any decent speed   
115 
116    Node r,s;
117    Edge e;
118    double parent_x;
119    for (int noa = group_num - 1;noa >= 0;noa--)
120      {
121        Vector shifts = new Vector(20,10);
122        shifts.addElement(0);
123        int g_num = 0;//this is the offset from groups.m_start to get the right 1
124        r = groups[noa].m_p;
125        parent_x = r.getCenter();
126        for (int nob = 1;(e = r.getChild(nob)) != null;nob++)
127          {
128            double margin;
129            s = e.getTarget();
130            margin = s_getCenter - r.getChild(nob - 1).getTarget().getCenter-1
131                     - shift.elementAt(nob-1);
132            if (margin > 0)
133              {
134                margin = check_down(s,g_num,margin);
135                if (margin > 0)
136                  {
137                    shift.addElement(-margin);
138                  }
139                else
140                  {
141                    shift.addElement(0);
142                  }
143              }
144            else
145              {
146                shift.addElement(0);
147              }
148            if (s.getChild(0) != null)
149              {
150                g_num++;
151              }
152          }
153      }
154  }
155
156
157  private double check_down(Node r,int gn,double m)
158  {
159    //note i need to know where the children of the
160    //other changers are to properly overlap check
161    //to do this i think the best way is to go up the other group
162    //parents line and see if it goes through the current group
163    //this means to save time i need to know the level that is being
164    //worked with along with the group
165   
166    Edge e;
167    for (int noa = 0;(e = r.getChild(noa)) != null;noa++)
168      {
169       
170      }
171  }
172*/
173
174
175  /**
176   * This will set initial places for the x coord of the nodes.
177   * @param start The `number for the first group to start on (I think).
178   */
179  private void xPlacer(int start) {
180    //this can be one of a few x_placers (the first)
181    //it will work by placing 1 space inbetween each node
182    //ie the first at 0 the second at 1 and so on
183    //then it will add to this value the place of the parent
184    //node - half of the size
185    //i will break this up into several functions
186    //first the gap setter;
187    //then the shifter
188    //it will require a vector shift function added to the node class
189    //i will write an additional shifter for the untangler
190    //for its particular situation
191
192    Node r;
193    Edge e;
194    if (m_groupNum > 0) {
195      m_groups[0].m_p.setCenter(0);
196      for (int noa = start;noa < m_groupNum;noa++) {
197        int nob,alter =0;
198        double c = m_groups[noa].m_gap;
199        r = m_groups[noa].m_p;
200        for (nob = 0;(e = r.getChild(nob)) != null;nob++) {
201          if (e.getTarget().getParent(0) == e) {
202            e.getTarget().setCenter(nob * c);
203          }
204          else {
205            alter++;
206          }
207        }
208        m_groups[noa].m_size = (nob - 1 - alter) * c;
209        xShift(noa);
210      }
211    }
212  }
213
214  /**
215   * This will shift a group of nodes to be aligned under their parent.
216   * @param n The group number to shift
217   */
218  private void xShift(int n) {
219    Edge e;
220    Node r = m_groups[n].m_p;
221    double h = m_groups[n].m_size / 2;
222    double c = m_groups[n].m_p.getCenter();
223    double m = c - h;
224    m_groups[n].m_left = m;
225    m_groups[n].m_right = c + h;
226   
227    for (int noa = 0;(e = r.getChild(noa)) != null;noa++) {
228      if (e.getTarget().getParent(0) == e) {
229        e.getTarget().adjustCenter(m);
230      }
231    }
232  }
233
234  /**
235   * This scales all the x values to be between 0 and 1.
236   */
237  private void scaleByMax() {
238    //ammendment to what i may have commented before
239    //this takes the lowest x and highest x  and uses that as the scaling
240    //factor
241    double l_x = 5000,h_x = -5000;
242    for (int noa = 0;noa < m_groupNum;noa++) {
243      if (l_x > m_groups[noa].m_left) {
244        l_x = m_groups[noa].m_left;
245      }
246
247      if (h_x < m_groups[noa].m_right) {
248        h_x = m_groups[noa].m_right;
249      }
250    }
251   
252    Edge e;
253    Node r,s;
254    double m_scale = h_x - l_x + 1;
255    if (m_groupNum > 0) {
256      r = m_groups[0].m_p;
257      r.setCenter((r.getCenter() - l_x) / m_scale);
258      //System.out.println("from scaler " + l_x + " " + m_scale);
259      for (int noa = 0; noa < m_groupNum;noa++) {
260        r = m_groups[noa].m_p;
261        for (int nob = 0;(e = r.getChild(nob)) != null;nob++) {
262          s = e.getTarget();
263          if (s.getParent(0) == e) {
264            s.setCenter((s.getCenter() - l_x) / m_scale);
265          }
266        }
267      }
268    }
269  }
270 
271  /**
272   * This scales the x values to between 0 and 1 for each individual line
273   * rather than doing them all at once.
274   */
275  private void scaleByInd() {
276    //ammendment to what i may have commented before
277    //this takes the lowest x and highest x  on each line and uses that for
278    //the line in question
279    double l_x,h_x;
280
281    Edge e;
282    Node r,s;
283    r = m_groups[0].m_p;
284    r.setCenter(.5);
285    double m_scale;
286    for (int noa = 0;noa < m_levelNum;noa++) {
287      l_x = m_groups[m_levels[noa].m_start].m_left;
288      h_x = m_groups[m_levels[noa].m_end].m_right;
289      m_scale = h_x - l_x + 1;
290      for (int nob = m_levels[noa].m_start; nob <= m_levels[noa].m_end;nob++) {
291        r = m_groups[nob].m_p;
292        for (int noc = 0;(e = r.getChild(noc)) != null;noc++) {
293          s = e.getTarget();
294          if (s.getParent(0) == e) {
295            s.setCenter((s.getCenter() - l_x) / m_scale);
296          }
297        }
298      }
299    }
300  }
301 
302  /**
303   * This untangles the nodes so that they will will fall on the correct
304   * side of the other nodes along their row.
305   */
306  private void untangle2() {
307    Ease a;
308    Edge e;
309    Node r,nf = null,ns = null,mark;
310    int l = 0,times = 0;
311    int f,s,tf = 0,ts = 0,pf,ps;
312    while ((a = overlap(l)) != null) {
313      times++;
314      //System.out.println("from untang 2 " + group_num);
315      f = a.m_place;
316      s = a.m_place + 1;
317      while (f != s) {
318        a.m_lev--;
319        tf = f;
320        ts = s;
321        f = m_groups[f].m_pg;
322        s = m_groups[s].m_pg;
323      }
324      l = a.m_lev;
325      pf = 0;
326      ps = 0;
327      r = m_groups[f].m_p;
328      mark = m_groups[tf].m_p;
329      nf = null;
330      ns = null;
331      for (int noa = 0; nf != mark;noa++) {
332        pf++;
333        nf = r.getChild(noa).getTarget();
334      }
335      mark = m_groups[ts].m_p;
336      for (int noa = pf; ns != mark;noa++) {
337        ps++; //the number of gaps between the two nodes
338        ns = r.getChild(noa).getTarget();
339      }
340      //m_groups[f].gap =
341      //              Math.ceil((a.amount / (double)ps) + m_groups[f].gap);
342      //note for this method i do not need the group gap ,but i will leave
343      //it for the other methods;
344      Vector o_pos = new Vector(20,10);
345      for (int noa = 0;(e = r.getChild(noa)) != null;noa++) {
346        if (e.getTarget().getParent(0) == e) {
347          Double tem = new Double(e.getTarget().getCenter());
348          o_pos.addElement(tem);
349        }
350      }
351
352      pf--;
353      double inc = a.m_amount / (double)ps;
354      for (int noa = 0;(e = r.getChild(noa)) != null;noa++) {
355        ns = e.getTarget();
356        if (ns.getParent(0) == e) {
357          if (noa > pf + ps) {
358            ns.adjustCenter(a.m_amount);
359          }
360          else if (noa > pf) {
361            ns.adjustCenter(inc * (double)(noa - pf));
362          }
363        }
364      }
365
366      nf = r.getChild(0).getTarget();
367      inc = ns.getCenter() - nf.getCenter();
368      m_groups[f].m_size = inc;
369      m_groups[f].m_left = r.getCenter() - inc / 2; 
370      m_groups[f].m_right = m_groups[f].m_left + inc;
371      inc = m_groups[f].m_left - nf.getCenter();
372
373      double shift;
374      int g_num = 0;
375      for (int noa = 0;(e = r.getChild(noa)) != null;noa++) {
376        ns = e.getTarget();
377        if (ns.getParent(0) == e) {
378          ns.adjustCenter(inc);
379          shift = ns.getCenter() - 
380            ((Double)o_pos.elementAt(noa)).doubleValue();
381          if (ns.getChild(0) != null) {
382            moveSubtree(m_groups[f].m_start + g_num,shift);
383            g_num++;
384          }
385        }
386        //ns.adjustCenter(-shift);
387      }
388      //zero_offset(r);
389     
390      //x_placer(f);
391    }
392  }
393
394
395  /**
396   * This will recursively shift a sub there to be centered about
397   * a particular value.
398   * @param n The first group in the sub tree.
399   * @param o The point to start shifting the subtree.
400   */
401  private void moveSubtree(int n,double o) {
402    Edge e;
403    Node r = m_groups[n].m_p;
404    for (int noa = 0;(e = r.getChild(noa)) != null;noa++) {
405      if (e.getTarget().getParent(0) == e) {
406        e.getTarget().adjustCenter(o);
407      }
408    }
409    m_groups[n].m_left += o;
410    m_groups[n].m_right += o;
411    if (m_groups[n].m_start != -1) {
412      for (int noa = m_groups[n].m_start;noa <= m_groups[n].m_end;noa++) {
413        moveSubtree(noa,o);
414      }
415    }
416  }
417
418
419  /**
420   * This will untangle the nodes in the tree so that they fall on the
421   * correct side of each other.
422   */
423  private void untangle() {
424    Ease a;
425    Edge e;
426    Node r,nf = null,ns = null,mark;
427    int l = 0,times = 0;
428    int f,s,tf = 0,ts = 0,pf,ps;
429    while ((a = overlap(l)) != null) {
430      times++;
431      //System.out.println(group_num);
432      f = a.m_place;
433      s = a.m_place + 1;
434      while (f != s) {
435        a.m_lev--;
436        tf = f;
437        ts = s;
438        f = m_groups[f].m_pg;
439        s = m_groups[s].m_pg;
440      }
441      l = a.m_lev;
442      pf = 0;
443      ps = 0;
444      r = m_groups[f].m_p;
445      mark = m_groups[tf].m_p;
446      nf = null;
447      ns = null;
448      for (int noa = 0; nf != mark;noa++) {
449        pf++;
450        nf = r.getChild(noa).getTarget();
451      }
452      mark = m_groups[ts].m_p;
453      for (int noa = pf; ns != mark;noa++) {
454        ps++; //the number of gaps between the two nodes
455        ns = r.getChild(noa).getTarget();
456      }
457      m_groups[f].m_gap =
458        Math.ceil((a.m_amount / (double)ps) + m_groups[f].m_gap);
459     
460      xPlacer(f);
461    }
462  }
463 
464  /**
465   * This will find an overlap and then return information about that overlap
466   * @param l The level to start on.
467   * @return null if there was no overlap , otherwise an object containing
468   * the group number that overlaps (only need one) how much they overlap by,
469   * and the level they overlap on.
470   */
471  private Ease overlap(int l) {
472    Ease a = new Ease();
473    for (int noa = l;noa < m_levelNum;noa++) {
474      for (int nob = m_levels[noa].m_start;nob < m_levels[noa].m_end;nob++) {
475        a.m_amount = m_groups[nob].m_right - m_groups[nob+1].m_left + 2;
476        //System.out.println(m_groups[nob].m_right + " + " +
477        //             m_groups[nob+1].m_left + " = " + a.amount);
478        if (a.m_amount >= 0) {
479          a.m_amount++;
480          a.m_lev = noa;
481          a.m_place = nob;
482          return a;
483        }
484      }
485    }
486    return null;
487  }
488 
489  /* private int count_m_groups(Node r,int l)
490  {
491    Edge e;
492    if (r.getChild(0) != null)
493      {
494        l++;
495      }
496    for (int noa = 0;(e = r.getChild(noa)) != null;noa++)
497      {
498        l = count_groups(e.getTarget(),l);
499      }
500
501    return l;
502  }
503  */
504
505  /**
506   * This function sets up the height of each node, and also fills the
507   * levels array with information about what the start and end groups on that
508   * level are.
509   */
510  private void yPlacer() {
511    //note this places the y height and sets up the levels array
512    double changer = m_yRatio;
513    int lev_place = 0;
514    if (m_groupNum > 0) {
515      m_groups[0].m_p.setTop(m_yRatio);
516      m_levels[0].m_start = 0;
517     
518      for (int noa = 0;noa < m_groupNum;noa++) {
519        if (m_groups[noa].m_p.getTop() != changer) {
520          m_levels[lev_place].m_end = noa - 1;
521          lev_place++;
522          m_levels[lev_place].m_start = noa;
523          changer = m_groups[noa].m_p.getTop();
524        }
525        nodeY(m_groups[noa].m_p);
526      }
527      m_levels[lev_place].m_end = m_groupNum - 1;
528    }
529  }
530
531  /**
532   * This will set all of the children node of a particular node to their
533   * height.
534   * @param r The parent node of the children to set their height.
535   */
536  private void nodeY(Node r) {
537    Edge e;
538    double h = r.getTop() + m_yRatio;
539    for (int noa = 0;(e = r.getChild(noa)) != null;noa++) {
540      if (e.getTarget().getParent(0) == e) {
541        e.getTarget().setTop(h);
542        if (!e.getTarget().getVisible()) {
543          //System.out.println("oh bugger");
544        }
545      }
546    }
547  }
548 
549  /**
550   * This starts to create the information about the sibling groups.
551   * As more groups are created the for loop in this will check those groups
552   * for lower groups.
553   * @param r The top node.
554   */
555  private void groupBuild(Node r) {
556    if (m_groupNum > 0) {
557      m_groupNum = 0;
558      m_groups[0].m_p = r;
559      m_groupNum++;
560      //note i need to count up the num of groups first
561      //woe is me
562      for (int noa = 0;noa < m_groupNum ;noa++) {
563        groupFind(m_groups[noa].m_p,noa);
564      }
565    }
566  }
567 
568  /**
569   * This is called to build the rest of the grouping information.
570   * @param r The parent of the group.
571   * @param pg The number for the parents group.
572   */
573  private void groupFind(Node r,int pg) {
574    Edge e;
575    boolean first = true;
576    for (int noa = 0;(e = r.getChild(noa)) != null;noa++) {
577      if (e.getTarget().getParent(0) == e) {
578        if (e.getTarget().getChild(0) != null && e.getTarget().getCVisible()) {
579          if (first) {
580            m_groups[pg].m_start = m_groupNum;
581            first = false;
582          }
583          m_groups[pg].m_end = m_groupNum;
584          m_groups[m_groupNum].m_p = e.getTarget();
585          m_groups[m_groupNum].m_pg = pg;
586          m_groups[m_groupNum].m_id = m_groupNum; //just in case I ever need
587          //this info
588          m_groupNum++;
589        }
590      }
591    }
592  }
593 
594
595  //note these three classes are only to help organise the data and are
596  //inter related between each other and this placer class
597  //so don't mess with them or try to use them somewhere else
598  //(because that would be a mistake and I would pity you)
599 
600
601  /**
602   * Inner class for containing the level data.
603   */
604  private class Level {
605    /** The number for the group on the left of this level. */
606    public int m_start;
607    /** The number for the group on the right of this level. */
608    public int m_end;
609
610    /** These two params would appear to not be used. */
611    public int m_left;
612    public int m_right;
613  }
614
615  /**
616   * Inner class for containing the grouping data.
617   */
618  private class Group {
619    /** The parent node of this group. */
620    public Node m_p;
621
622    /** The group number for the parent of this group. */
623    public int m_pg;
624
625    /** The gap size for the distance between the nodes in this group. */
626    public double m_gap;
627
628    /** The leftmost position of this group. */
629    public double m_left;
630
631    /** The rightmost position of this group. */
632    public double m_right;
633
634    /** The size of this group. */
635    public double m_size;
636
637    /** The start node of this group. */
638    public int m_start;
639
640    /** The end node of this group. */
641    public int m_end;
642
643    /** The group number for this group. (may not be used!?). */
644    public int m_id;
645  }
646 
647  /**
648   * An inner class used to report information about any tangles found.
649   */
650  private class Ease {
651    /** The number of the group on the left of the tangle. */
652    public int m_place;
653    /** The distance they were tangled. */
654    public double m_amount;
655    /** The level on which they were tangled. */
656    public int m_lev;
657  }
658}
659
660
661
662
663
664
665
666
667
668
669
670
671
672
Note: See TracBrowser for help on using the repository browser.