ARB
irstree_display.cxx
Go to the documentation of this file.
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 
16 using namespace AW;
17 
18 // *********************** paint sub tree ************************
19 
20 const int TIP_BOX_SIZE = 3;
21 
22 struct IRS_data {
25  AW_pos min_y; // ypos of folding line
30  AW_pos fold_x1, fold_x2;
31 
33  double x_scale;
34 
37 
38  AW_pos gap; // between group frame and (box or text)
39  AW_pos openGroupExtra; // extra y-size of unfolded groups
40 
42 
44 
46  if (draw_separator) {
47  if (!is_size_device) {
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 };
57 static IRS_data IRS;
58 
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 
75 AW_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.y += 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 
313 void 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;
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 }
#define MAX_TREEDISP_RECURSION_DEPTH
Definition: TreeDisplay.hxx:35
bool draw_separator
AW_pos halfstep_y
double x_scale
long AW_bitset
Definition: aw_base.hxx:44
unsigned view_sum
Definition: AP_Tree.hxx:159
#define TREEDISP_TRUNCATION_MESSAGE
Definition: TreeDisplay.hxx:36
const AW_bitset AW_PRINTER_CLIP
Definition: aw_device.hxx:41
const AW_bitset AW_SCREEN
Definition: aw_device.hxx:34
void set_line_attributes(int gc, short width, AW_linestyle style)
Definition: AW_device.cxx:465
#define td_assert(cond)
Definition: Group.hxx:18
bool is_clade() const
Definition: TreeNode.h:480
double right() const
bool is_folded_group() const
Definition: AP_Tree.hxx:348
AP_tree_members gr
Definition: AP_Tree.hxx:214
AW_bitset sep_filter
static IRS_data IRS
AW_pos onePixel
const char * count
bool hasName(const char *Name) const
Definition: AP_Tree.hxx:374
GBT_LEN leftlen
Definition: TreeNode.h:172
AW_pos fold_x1
void draw_top_separator_once(AW_device *device)
double AW_pos
Definition: aw_base.hxx:29
AW_pos openGroupExtra
AW_pos fold_x2
bool is_size_device
bool is_son_of_root() const
Definition: TreeNode.h:255
const double & ypos() const
unsigned name_len
static int group[MAXN+1]
Definition: ClustalV.cxx:65
bool line(int gc, const AW::LineVector &Line, AW_bitset filteri=AW_ALL_DEVICES_SCALED)
Definition: aw_device.hxx:430
const double & y() const
int GB_read_flag(GBDATA *gbd)
Definition: arbdb.cxx:2796
Vector adjust_text
GBT_LEN rightlen
Definition: TreeNode.h:172
AW_pos Y_correction_for_IRS()
double bottom() const
double left() const
bool is_leaf() const
Definition: TreeNode.h:211
long AW_CL
Definition: cb.h:21
#define NT_BOX_WIDTH
Definition: TreeDisplay.hxx:80
unsigned count_len
AW_pos tree_depth
GBDATA * gb_node
Definition: TreeNode.h:173
GBDATA * gb_main
Definition: adname.cxx:32
const int TIP_BOX_SIZE
static int info[maxsites+1]
const Position Origin
uint32_t gc
Definition: AP_Tree.hxx:152
float max_tree_depth
Definition: AP_Tree.hxx:161
bool is_normal_group() const
Definition: TreeNode.h:470