source: trunk/SL/TREEDISP/irstree_display.cxx

Last change on this file was 19271, checked in by westram, 2 years ago
  • fix #824
    • limit display recursion depth (to approx. MAX_TREEDISP_RECURSION_DEPTH)
    • when reached:
      • truncate branch
      • display warning in tree (like a leaf).
    • applied to all 3 tree display modes (dendro, irs + radial)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.0 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : irstree_display.cxx                               //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "TreeDisplay.hxx"
12#include <AP_TreeColors.hxx>
13
14#include <nds.h>
15
16using namespace AW;
17
18// *********************** paint sub tree ************************
19
20const int TIP_BOX_SIZE = 3;
21
22struct IRS_data {
23    bool   draw_separator;
24    AW_pos y;
25    AW_pos min_y;    // ypos of folding line
26    AW_pos max_y;
27    AW_pos step_y;
28    AW_pos halfstep_y;
29    AW_pos onePixel;
30    AW_pos fold_x1, fold_x2;
31
32    int    group_closed;
33    double x_scale;
34
35    Vector adjust_text;
36    AW_pos tree_depth;
37
38    AW_pos gap; // between group frame and (box or text)
39    AW_pos openGroupExtra; // extra y-size of unfolded groups
40
41    AW_bitset sep_filter;
42
43    bool is_size_device;
44
45    void draw_top_separator_once(AW_device *device) {
46        if (draw_separator) {
47            if (!is_size_device) {
48                device->set_line_attributes(AWT_GC_IRS_GROUP_BOX, 4, AW_SOLID);
49                device->line(AWT_GC_IRS_GROUP_BOX, fold_x1, min_y, fold_x2, min_y, sep_filter);
50            }
51            draw_separator = false;
52        }
53    }
54
55
56};
57static IRS_data IRS;
58
59inline AW_pos Y_correction_for_IRS() {
60    if (IRS.is_size_device) {
61        // hack to fix calculated cursor position:
62        // - IRS tree reports different cursor positions in AW_SIZE and normal draw modes.
63        // - the main reason for the difference is the number of open groups clipped away
64        //   above the separator line.
65        // - There is still some unhandled difference mostlikely caused by the number of
66        //   open groups on the screen, but in most cases the cursor position is inside view now.
67
68        double correctionPerGroup = IRS.openGroupExtra; // depends on size of marked-species-font
69        double cursorCorrection   = -IRS.group_closed * correctionPerGroup;
70        return cursorCorrection;
71    }
72    return 0.0;
73}
74
75AW_pos AWT_graphic_tree::paint_irs_sub_tree(AP_tree *node, AW_pos x_offset, const NDS_Labeler& labeler) {
76    static int recursion_depth = 0;
77
78    if (!IRS.is_size_device) {
79        // check clipping rectangle
80        if (IRS.y > IRS.max_y) {
81            return IRS.max_y;
82        }
83        AW_pos height_of_subtree = IRS.step_y*node->gr.view_sum;
84        if (IRS.y + height_of_subtree < IRS.min_y) {
85            IRS.y += height_of_subtree;
86            return IRS.min_y;
87        }
88    }
89
90    bool limit_recursion_depth = recursion_depth>=MAX_TREEDISP_RECURSION_DEPTH;
91    if (node->is_leaf() || limit_recursion_depth) {
92        IRS.y+=IRS.step_y;
93        IRS.draw_top_separator_once(disp_device);
94
95        Position leaf(x_offset, IRS.y);
96
97        if (node->hasName(species_name)) {
98            Position cursor(leaf);
99            cursor.movey(Y_correction_for_IRS());
100            selSpec = PaintedNode(cursor, node);
101        }
102
103        AW_click_cd cd(disp_device, (AW_CL)node, CL_NODE);
104
105        int gc;
106        const char *specinfo;
107        if (limit_recursion_depth) {
108            gc       = AWT_GC_ONLY_ZOMBIES;
109            specinfo = TREEDISP_TRUNCATION_MESSAGE; // @@@ this also appears ABOVE separator-line!
110        }
111        else {
112            gc = node->gr.gc;
113            if (node->gb_node && GB_read_flag(node->gb_node)) {
114                set_line_attributes_for(node);
115                filled_box(gc, leaf, NT_BOX_WIDTH);
116            }
117            specinfo = labeler.speciesLabel(gb_main, node->gb_node, node, tree_static->get_tree_name());
118        }
119
120        Position    textpos  = leaf+IRS.adjust_text;
121        disp_device->text(gc, specinfo, textpos);
122
123        return IRS.y;
124    }
125
126    AW_pos frame_width = NAN;
127    AW_pos group_y1    = NAN;
128
129    bool      is_group    = node->is_clade();
130    bool      is_selected = is_group && selected_group.at_node(node);
131    const int group_gc    = is_selected ? int(AWT_GC_CURSOR) : node->gr.gc;
132
133    if (is_selected) {
134        Position group(x_offset, IRS.y);
135        group.movey(Y_correction_for_IRS());
136        selGroup = PaintedNode(group, node);
137    }
138
139    if (is_group) {
140        frame_width = node->gr.max_tree_depth * IRS.x_scale;
141
142        if (node->is_folded_group()) {
143            AW_pos y_center;
144
145            AW_pos frame_height = node->gr.view_sum * IRS.step_y;
146            AW_pos frame_y1     = IRS.y+IRS.halfstep_y+IRS.gap;
147            AW_pos frame_y2     = frame_y1 + frame_height;
148
149            if (frame_y2 >= IRS.min_y) {
150                if (frame_y1 < IRS.min_y) { // shift folded groups into the folding area (disappears when completely inside)
151                    frame_y1  = IRS.min_y;
152                    IRS.min_y += IRS.halfstep_y+IRS.gap;
153                }
154
155                AW_pos    visible_frame_height = frame_y2-frame_y1;
156                Rectangle frame(Position(x_offset, frame_y1), Vector(frame_width, visible_frame_height));
157
158                // draw group frame (unclosed on right hand):
159                AW_click_cd cd(disp_device, (AW_CL)node, CL_NODE);
160
161                {
162                    const int gc = is_selected ? AWT_GC_CURSOR : AWT_GC_IRS_GROUP_BOX;
163                    disp_device->set_line_attributes(gc, 1, AW_SOLID);
164                    disp_device->line(gc, frame.upper_edge());
165                    disp_device->line(gc, frame.left_edge());
166                    disp_device->line(gc, frame.lower_edge());
167                }
168
169                const int gc = node->gr.gc;
170                set_line_attributes_for(node);
171                filled_box(group_gc, frame.upper_left_corner(), TIP_BOX_SIZE);
172
173                Vector    frame2box(IRS.gap, IRS.gap);
174                Rectangle gbox(frame.upper_left_corner()+frame2box, Vector(frame.width()*.5, frame.height()-2*IRS.gap));
175
176                disp_device->set_grey_level(gc, group_greylevel);
177                disp_device->box(gc, AW::FillStyle::SHADED_WITH_BORDER, gbox);
178
179                Position box_rcenter = gbox.right_edge().centroid();
180
181                const GroupInfo& info = get_group_info(node, group_info_pos == GIP_SEPARATED ? GI_SEPARATED : GI_COMBINED, group_info_pos == GIP_OVERLAYED, labeler);
182                if (info.name) { //  a node name should be displayed
183                    disp_device->text(group_gc, SizedCstr(info.name, info.name_len), box_rcenter+IRS.adjust_text);
184                }
185                if (info.count) {
186                    Position box_lcenter = gbox.left_edge().centroid();
187                    disp_device->text(group_gc, SizedCstr(info.count, info.count_len), box_lcenter+IRS.adjust_text);
188                }
189
190                IRS.draw_top_separator_once(disp_device);
191
192                IRS.y    += frame_height + 2*IRS.gap;
193                y_center  = box_rcenter.ypos();
194            }
195            else {
196                IRS.y    += frame_height + 2*IRS.gap;
197                y_center  = IRS.min_y;
198
199                if (IRS.y > IRS.min_y) {
200                    IRS.y = IRS.min_y;
201                }
202            }
203            return y_center;
204        }
205
206        // -----------------------------------
207        //      otherwise: unfolded group
208
209        group_y1 = IRS.y;
210        if (group_y1 >= IRS.min_y) {
211            IRS.draw_top_separator_once(disp_device);
212            group_y1 = IRS.y + IRS.halfstep_y+IRS.gap;
213        }
214        else {
215            group_y1   = IRS.min_y;
216            IRS.min_y += IRS.openGroupExtra;
217        }
218        IRS.y += IRS.openGroupExtra;
219
220        const int   gc = is_selected ? AWT_GC_CURSOR : AWT_GC_IRS_GROUP_BOX;
221        AW_click_cd cd(disp_device, (AW_CL)node, CL_NODE);
222        disp_device->set_line_attributes(gc, 1, AW_DOTTED);
223        disp_device->line(gc, x_offset-IRS.onePixel, group_y1, x_offset+frame_width, group_y1); // opened-group-frame
224
225        const GroupInfo& info = get_group_info(node, GI_COMBINED, false, labeler);
226
227        td_assert(info.name); // if fails -> maybe skip whole headerline
228        disp_device->text(group_gc,
229                          SizedCstr(info.name, info.name_len),
230                          x_offset-IRS.onePixel + IRS.gap,
231                          group_y1 + 2*IRS.adjust_text.y() + IRS.gap);
232    }
233
234    ++recursion_depth;
235
236    // draw subtrees
237    AW_pos left_x = x_offset + IRS.x_scale * node->leftlen;
238    AW_pos left_y = paint_irs_sub_tree(node->get_leftson(), left_x, labeler);
239
240    AW_pos right_x = x_offset + IRS.x_scale * node->rightlen;
241    AW_pos right_y = paint_irs_sub_tree(node->get_rightson(), right_x, labeler);
242
243    if (is_group) IRS.group_closed++; // @@@ only done for unfolded groups. bug?
244
245    --recursion_depth;
246
247    // draw structure
248    if (left_y > IRS.min_y) {
249        if (left_y < IRS.max_y) { // clip y on top border
250            AP_tree     *son = node->get_leftson();
251            AW_click_cd  cd(disp_device, (AW_CL)son, CL_NODE);
252            Position     left(left_x, left_y);
253
254            set_line_attributes_for(son);
255            draw_branch_line(son->gr.gc, Position(x_offset, left_y), left, line_filter);
256
257            if (bconf.shall_show_remark_for(son)) {
258                if (!son->is_son_of_root()) { // bootstrap of root-edge is displayed below (at right son)
259                    bconf.display_node_remark(disp_device, son, left, IRS.x_scale * node->leftlen, scaled_branch_distance, D_NORTH_WEST);
260                }
261            }
262        }
263    }
264    else {
265        left_y = IRS.min_y;
266    }
267
268    AW_pos y_center = (left_y + right_y)*0.5;
269
270    if (right_y > IRS.min_y && right_y < IRS.max_y) { // visible right branch in lower part of display
271        AP_tree     *son = node->get_rightson();
272        AW_click_cd  cd(disp_device, (AW_CL)son, CL_NODE);
273        Position     right(right_x, right_y);
274
275        set_line_attributes_for(son);
276        draw_branch_line(son->gr.gc, Position(x_offset, right_y), right, line_filter);
277
278        if (bconf.shall_show_remark_for(son)) {
279            if (son->is_son_of_root()) {
280                AW_click_cd cdr(disp_device, 0, CL_ROOTNODE);
281                bconf.display_node_remark(disp_device, son, Position(x_offset, y_center), IRS.x_scale * (node->rightlen+node->leftlen), scaled_branch_distance, D_EAST);
282            }
283            else {
284                bconf.display_node_remark(disp_device, son, right, IRS.x_scale * node->rightlen, scaled_branch_distance, D_SOUTH_WEST);
285            }
286        }
287    }
288
289    AW_click_cd cd(disp_device, (AW_CL)node, CL_NODE);
290    set_line_attributes_for(node->get_leftson());
291    disp_device->line(node->get_leftson()->gr.gc, x_offset, y_center, x_offset, left_y);
292
293    set_line_attributes_for(node->get_rightson());
294    disp_device->line(node->get_rightson()->gr.gc, x_offset, y_center, x_offset, right_y);
295
296    if (node->is_normal_group()) { // close unfolded group brackets and draw tipbox
297        IRS.+= IRS.halfstep_y+IRS.gap;
298
299        {
300            const int gc = is_selected ? AWT_GC_CURSOR : AWT_GC_IRS_GROUP_BOX;
301            disp_device->set_line_attributes(gc, 1, AW_DOTTED);
302            disp_device->line(gc, x_offset-IRS.onePixel, IRS.y, x_offset+frame_width, IRS.y); // opened-group-frame
303            disp_device->line(gc, x_offset-IRS.onePixel, group_y1, x_offset-IRS.onePixel,  IRS.y); // opened-group-frame
304        }
305
306        const int gc = group_gc;
307        set_line_attributes_for(node);
308        filled_box(gc, Position(x_offset-IRS.onePixel, group_y1), TIP_BOX_SIZE);
309    }
310    return y_center;
311}
312
313void AWT_graphic_tree::show_irs_tree(AP_tree *at, double height, const NDS_Labeler& labeler) {
314
315    IRS.draw_separator = true;
316    IRS.y              = 0;
317    IRS.step_y         = height;
318    IRS.halfstep_y     = IRS.step_y*0.5;
319    IRS.x_scale        = 200.0;      // @@@ should not have any effect, since display gets x-scaled. But if it's to low (e.g. 1.0) scaling on zoom-reset does not work
320
321    const AW_font_limits& limits = disp_device->get_font_limits(AWT_GC_ALL_MARKED, 0);
322
323    IRS.adjust_text    = disp_device->rtransform(Vector(NT_BOX_WIDTH, limits.ascent*0.5));
324    IRS.onePixel       = disp_device->rtransform_size(1.0);
325    IRS.gap            = 3*IRS.onePixel;
326    IRS.group_closed   = 0;
327    IRS.tree_depth     = at->gr.max_tree_depth;
328    IRS.openGroupExtra = IRS.step_y+IRS.gap;
329    IRS.sep_filter     = AW_SCREEN|AW_PRINTER_CLIP;
330
331    IRS.is_size_device = disp_device->type() == AW_DEVICE_SIZE;
332
333    Position  corner = disp_device->rtransform(Origin);   // real world coordinates of left/upper screen corner
334    Rectangle rclip  = disp_device->get_rtransformed_cliprect();
335
336    // the following values currently contain nonsense for size device @@@
337    IRS.min_y   = corner.ypos();
338    IRS.max_y   = rclip.bottom();
339    IRS.fold_x1 = rclip.left();
340    IRS.fold_x2 = rclip.right();
341
342    list_tree_ruler_y           = paint_irs_sub_tree(at, 0, labeler);
343    irs_tree_ruler_scale_factor = IRS.x_scale;
344
345    disp_device->invisible(corner); // @@@ remove when size-dev works
346}
Note: See TracBrowser for help on using the repository browser.