1 | // ============================================================== // |
---|
2 | // // |
---|
3 | // File : NT_branchAnalysis.cxx // |
---|
4 | // Purpose : // |
---|
5 | // // |
---|
6 | // Coded by Ralf Westram (coder@reallysoft.de) in August 2012 // |
---|
7 | // Institute of Microbiology (Technical University Munich) // |
---|
8 | // http://www.arb-home.de/ // |
---|
9 | // // |
---|
10 | // ============================================================== // |
---|
11 | |
---|
12 | #include "NT_local.h" |
---|
13 | #include <TreeCallbacks.hxx> |
---|
14 | #include <aw_awar.hxx> |
---|
15 | #include <awt_canvas.hxx> |
---|
16 | #include <aw_msg.hxx> |
---|
17 | #include <aw_root.hxx> |
---|
18 | #include <awt_config_manager.hxx> |
---|
19 | |
---|
20 | #define AWAR_BRANCH_ANALYSIS "branch_analysis" |
---|
21 | #define AWAR_BRANCH_ANALYSIS_TMP "tmp/" AWAR_BRANCH_ANALYSIS |
---|
22 | |
---|
23 | #define AWAR_BA_MIN_REL_DIFF AWAR_BRANCH_ANALYSIS "/min_rel_diff" |
---|
24 | #define AWAR_BA_MIN_ABS_DIFF AWAR_BRANCH_ANALYSIS "/min_abs_diff" |
---|
25 | #define AWAR_BA_MIN_DEPTH AWAR_BRANCH_ANALYSIS "/min_depth" |
---|
26 | #define AWAR_BA_MIN_ROOTDIST AWAR_BRANCH_ANALYSIS "/min_rootdist" |
---|
27 | #define AWAR_BA_DEGENERATION AWAR_BRANCH_ANALYSIS "/degeneration" |
---|
28 | |
---|
29 | #define AWAR_BA_AUTOMARK_FORMAT AWAR_BRANCH_ANALYSIS_TMP "/auto_%s" |
---|
30 | |
---|
31 | // AISC_MKPT_PROMOTE:class TREE_canvas; |
---|
32 | |
---|
33 | class BranchWindow : virtual Noncopyable { |
---|
34 | TREE_canvas *ntw; |
---|
35 | char *suffix; |
---|
36 | AW_awar *awar_info; |
---|
37 | AW_window_simple *aws; |
---|
38 | |
---|
39 | static char *get_suffix(TREE_canvas *ntw) { |
---|
40 | // suffix depends on canvas |
---|
41 | return GBS_global_string_copy("_%i", ntw->get_index()); |
---|
42 | } |
---|
43 | |
---|
44 | const char *local_awar_name (const char *prefix, const char *name) { return GBS_global_string("%s%s/%s", prefix, suffix, name); } |
---|
45 | |
---|
46 | void create_awars(AW_root *aw_root); |
---|
47 | void create_window(AW_root *aw_root); |
---|
48 | |
---|
49 | const char *automark_awarname() const { return GBS_global_string(AWAR_BA_AUTOMARK_FORMAT, suffix); } |
---|
50 | |
---|
51 | public: |
---|
52 | BranchWindow(AW_root *aw_root, TREE_canvas *ntw_) |
---|
53 | : ntw(ntw_), |
---|
54 | suffix(get_suffix(ntw)) |
---|
55 | { |
---|
56 | create_awars(aw_root); |
---|
57 | create_window(aw_root); |
---|
58 | } |
---|
59 | |
---|
60 | ~BranchWindow() { |
---|
61 | free(suffix); |
---|
62 | } |
---|
63 | |
---|
64 | AW_window *get_window() const { return aws; } |
---|
65 | |
---|
66 | private: |
---|
67 | AW_root *get_awroot() const { return get_window()->get_root(); } |
---|
68 | TREE_canvas *get_canvas() const { return ntw; } |
---|
69 | AP_tree *get_tree() const { return AWT_TREE(ntw)->get_root_node(); } |
---|
70 | GBDATA *get_gbmain() const { return get_canvas()->gb_main; } |
---|
71 | AW_awar *awar(const char *name) { return get_awroot()->awar(name); } |
---|
72 | |
---|
73 | void postmark_action() const { |
---|
74 | AWT_auto_refresh allowed_on(get_canvas()); |
---|
75 | get_canvas()->request_structure_update(); |
---|
76 | } |
---|
77 | |
---|
78 | bool have_tree() { |
---|
79 | if (get_tree()) return true; |
---|
80 | set_info("No tree selected"); |
---|
81 | return false; |
---|
82 | } |
---|
83 | |
---|
84 | static void adapt_intawar_max(AW_awar *awar, int val, int extra) { // @@@ move to AW_awar? |
---|
85 | // adapt maximum defined for int-awar (according to encountered maximum 'val' + a bit 'extra' range) |
---|
86 | nt_assert(awar->get_type() == AW_INT); |
---|
87 | |
---|
88 | int max = awar->get_max(); |
---|
89 | if (max<val || ((val*2) < max && awar->get_min()<val)) { |
---|
90 | awar->set_max(val+extra); |
---|
91 | } |
---|
92 | } |
---|
93 | static void adapt_floatawar_max(AW_awar *awar, double val, double extra) { // @@@ move to AW_awar? |
---|
94 | // adapt maximum defined for float-awar (according to encountered maximum 'val' + a bit 'extra' range) |
---|
95 | nt_assert(awar->get_type() == AW_FLOAT); |
---|
96 | |
---|
97 | double max = awar->get_max(); |
---|
98 | if (max<val || ((val*2) < max && awar->get_min()<val)) { |
---|
99 | awar->set_max(val+extra); |
---|
100 | } |
---|
101 | } |
---|
102 | |
---|
103 | public: |
---|
104 | void set_info(const char *msg) const { awar_info->write_string(msg); } |
---|
105 | void unmark_all() const { NT_mark_all_cb(NULp, get_canvas(), 0); } |
---|
106 | |
---|
107 | AW_awar *automark_awar() const { return get_awroot()->awar(automark_awarname()); } |
---|
108 | bool has_automark_set() const { return automark_awar()->read_int(); } |
---|
109 | |
---|
110 | void markDegeneratedBranches() { |
---|
111 | if (have_tree()) { |
---|
112 | GB_transaction ta(get_gbmain()); |
---|
113 | |
---|
114 | AW_awar *awar_degen = awar(AWAR_BA_DEGENERATION); |
---|
115 | double degeneration_factor = awar_degen->read_float(); |
---|
116 | |
---|
117 | unmark_all(); |
---|
118 | |
---|
119 | double found_max_degeneration = get_tree()->mark_degenerated_branches(degeneration_factor); |
---|
120 | set_info(GBS_global_string("Maximum degeneration = %.2f", found_max_degeneration)); |
---|
121 | adapt_floatawar_max(awar_degen, found_max_degeneration, 1.0); |
---|
122 | |
---|
123 | postmark_action(); |
---|
124 | } |
---|
125 | } |
---|
126 | void markDeepLeafs() { |
---|
127 | if (have_tree()) { |
---|
128 | GB_transaction ta(get_gbmain()); |
---|
129 | |
---|
130 | AW_awar *awar_min_depth = awar(AWAR_BA_MIN_DEPTH); |
---|
131 | AW_awar *awar_min_rootdist = awar(AWAR_BA_MIN_ROOTDIST); |
---|
132 | int min_depth = awar_min_depth->read_int(); |
---|
133 | double min_rootdist = awar_min_rootdist->read_float(); |
---|
134 | int found_max_depth; |
---|
135 | double found_max_rootdist; |
---|
136 | |
---|
137 | unmark_all(); |
---|
138 | set_info(get_tree()->mark_deep_leafs(min_depth, min_rootdist, found_max_depth, found_max_rootdist)); |
---|
139 | adapt_intawar_max(awar_min_depth, found_max_depth, 1); |
---|
140 | adapt_floatawar_max(awar_min_rootdist, found_max_rootdist, 0.1); |
---|
141 | postmark_action(); |
---|
142 | } |
---|
143 | } |
---|
144 | void markLongBranches() { |
---|
145 | if (have_tree()) { |
---|
146 | GB_transaction ta(get_gbmain()); |
---|
147 | |
---|
148 | AW_awar *awar_abs_diff = awar(AWAR_BA_MIN_ABS_DIFF); |
---|
149 | double min_rel_diff = awar(AWAR_BA_MIN_REL_DIFF)->read_float()/100.0; |
---|
150 | double min_abs_diff = awar_abs_diff->read_float(); |
---|
151 | double found_max_abs_diff; |
---|
152 | |
---|
153 | unmark_all(); |
---|
154 | set_info(get_tree()->mark_long_branches(min_rel_diff, min_abs_diff, found_max_abs_diff)); |
---|
155 | adapt_floatawar_max(awar_abs_diff, found_max_abs_diff, 0.1); |
---|
156 | postmark_action(); |
---|
157 | } |
---|
158 | } |
---|
159 | |
---|
160 | void analyseDistances() { |
---|
161 | if (have_tree()) { |
---|
162 | GB_transaction ta(get_gbmain()); |
---|
163 | set_info(get_tree()->analyse_distances()); |
---|
164 | } |
---|
165 | } |
---|
166 | }; |
---|
167 | |
---|
168 | static BranchWindow *existingBranchWindow[MAX_NT_WINDOWS] = { MAX_NT_WINDOWS_NULLINIT }; |
---|
169 | |
---|
170 | // -------------------------------------------------------------------------------- |
---|
171 | |
---|
172 | static void mark_long_branches_cb (AW_window*, BranchWindow *bw) { bw->markLongBranches(); } |
---|
173 | static void mark_deep_leafs_cb (AW_window*, BranchWindow *bw) { bw->markDeepLeafs(); } |
---|
174 | static void mark_degenerated_branches_cb(AW_window*, BranchWindow *bw) { bw->markDegeneratedBranches(); } |
---|
175 | static void unmark_branches_cb (AW_window*, BranchWindow *bw) { bw->unmark_all(); } |
---|
176 | static void distance_analysis_cb (AW_window*, BranchWindow *bw) { bw->analyseDistances(); } |
---|
177 | |
---|
178 | static void tree_changed_cb (AW_root*, BranchWindow *bw) { bw->set_info("<tree has changed>"); } |
---|
179 | |
---|
180 | static BranchWindow *findAutomarkingBranchWindow() { |
---|
181 | for (int w = 0; w<MAX_NT_WINDOWS; ++w) { |
---|
182 | BranchWindow *bw = existingBranchWindow[w]; |
---|
183 | if (bw && bw->has_automark_set()) { |
---|
184 | return bw; |
---|
185 | } |
---|
186 | } |
---|
187 | return NULp; |
---|
188 | } |
---|
189 | |
---|
190 | static void mark_long_branches_automark_cb() { |
---|
191 | BranchWindow *bw = findAutomarkingBranchWindow(); |
---|
192 | if (bw) bw->markLongBranches(); |
---|
193 | } |
---|
194 | static void mark_deep_leafs_automark_cb() { |
---|
195 | BranchWindow *bw = findAutomarkingBranchWindow(); |
---|
196 | if (bw) bw->markDeepLeafs(); |
---|
197 | } |
---|
198 | static void mark_degenerated_branches_automark_cb() { |
---|
199 | BranchWindow *bw = findAutomarkingBranchWindow(); |
---|
200 | if (bw) bw->markDegeneratedBranches(); |
---|
201 | } |
---|
202 | |
---|
203 | static void automark_changed_cb(AW_root *, BranchWindow *bw) { |
---|
204 | static bool avoid_recursion = false; |
---|
205 | if (!avoid_recursion) { |
---|
206 | AW_awar *awar_automark = bw->automark_awar(); |
---|
207 | if (awar_automark->read_int()) { // just activated |
---|
208 | LocallyModify<bool> avoid(avoid_recursion, true); |
---|
209 | |
---|
210 | awar_automark->write_int(0); |
---|
211 | BranchWindow *prev_active = findAutomarkingBranchWindow(); |
---|
212 | if (prev_active) prev_active->automark_awar()->write_int(0); |
---|
213 | awar_automark->write_int(1); |
---|
214 | } |
---|
215 | } |
---|
216 | } |
---|
217 | |
---|
218 | void BranchWindow::create_awars(AW_root *aw_root) { |
---|
219 | awar_info = aw_root->awar_string(local_awar_name(AWAR_BRANCH_ANALYSIS_TMP, "info"), "<No analysis performed yet>"); |
---|
220 | ntw->get_awar_tree()->add_callback(makeRootCallback(tree_changed_cb, this)); |
---|
221 | |
---|
222 | aw_root->awar_float(AWAR_BA_MIN_REL_DIFF, 75)->set_minmax(0, 100)->add_callback(makeRootCallback(mark_long_branches_automark_cb)); |
---|
223 | aw_root->awar_float(AWAR_BA_MIN_ABS_DIFF, 0.01)->set_minmax(0, 20)->add_callback(makeRootCallback(mark_long_branches_automark_cb)); |
---|
224 | |
---|
225 | aw_root->awar_int(AWAR_BA_MIN_DEPTH, 0)->set_minmax(0, 50)->add_callback(makeRootCallback(mark_deep_leafs_automark_cb)); |
---|
226 | aw_root->awar_float(AWAR_BA_MIN_ROOTDIST, 0.9)->set_minmax(0, 20)->add_callback(makeRootCallback(mark_deep_leafs_automark_cb)); |
---|
227 | |
---|
228 | aw_root->awar_float(AWAR_BA_DEGENERATION, 30)->set_minmax(0, 100)->add_callback(makeRootCallback(mark_degenerated_branches_automark_cb)); |
---|
229 | } |
---|
230 | |
---|
231 | static AWT_config_mapping_def branch_analysis_config_mapping[] = { |
---|
232 | { AWAR_BA_MIN_REL_DIFF, "minreldiff" }, |
---|
233 | { AWAR_BA_MIN_ABS_DIFF, "minabsdiff" }, |
---|
234 | { AWAR_BA_MIN_DEPTH, "mindepth" }, |
---|
235 | { AWAR_BA_MIN_ROOTDIST, "minrootdist" }, |
---|
236 | { AWAR_BA_DEGENERATION, "degeneration" }, |
---|
237 | |
---|
238 | { NULp, NULp } |
---|
239 | }; |
---|
240 | |
---|
241 | void BranchWindow::create_window(AW_root *aw_root) { |
---|
242 | aws = new AW_window_simple; |
---|
243 | |
---|
244 | aws->init(aw_root, GBS_global_string("BRANCH_ANALYSIS_%s", suffix), "Branch analysis"); |
---|
245 | aws->load_xfig("ad_branch.fig"); |
---|
246 | |
---|
247 | aws->auto_space(5, 5); |
---|
248 | |
---|
249 | aws->at("close"); |
---|
250 | aws->callback(AW_POPDOWN); |
---|
251 | aws->create_button("CLOSE", "CLOSE", "C"); |
---|
252 | |
---|
253 | aws->at("help"); |
---|
254 | aws->callback(makeHelpCallback("branch_analysis.hlp")); |
---|
255 | aws->create_button("HELP", "HELP", "H"); |
---|
256 | |
---|
257 | AW_awar *awar_automark = aw_root->awar_int(automark_awarname(), 0); |
---|
258 | aws->at("auto"); |
---|
259 | aws->label("Auto mark?"); |
---|
260 | aws->create_toggle(awar_automark->awar_name); |
---|
261 | awar_automark->add_callback(makeRootCallback(automark_changed_cb, this)); |
---|
262 | |
---|
263 | aws->at("sel"); |
---|
264 | aws->create_button(NULp, ntw->get_awar_tree()->awar_name, NULp, "+"); |
---|
265 | |
---|
266 | aws->at("info"); |
---|
267 | aws->create_text_field(awar_info->awar_name); |
---|
268 | |
---|
269 | aws->button_length(28); |
---|
270 | |
---|
271 | aws->at("dist_analyse"); |
---|
272 | aws->callback(makeWindowCallback(distance_analysis_cb, this)); |
---|
273 | aws->create_button("ANALYSE", "Analyse distances in tree"); |
---|
274 | |
---|
275 | aws->at("unmark"); |
---|
276 | aws->callback(makeWindowCallback(unmark_branches_cb, this)); |
---|
277 | aws->create_button("UNMARK", "Unmark all species"); |
---|
278 | |
---|
279 | const int FIELDWIDTH = 10; |
---|
280 | const int SCALERWIDTH = 250; |
---|
281 | |
---|
282 | aws->at("mark_long"); |
---|
283 | aws->callback(makeWindowCallback(mark_long_branches_cb, this)); |
---|
284 | aws->create_button("MARK_LONG", "Mark long branches"); |
---|
285 | |
---|
286 | aws->at("min_rel"); aws->create_input_field_with_scaler(AWAR_BA_MIN_REL_DIFF, FIELDWIDTH, SCALERWIDTH, AW_SCALER_LINEAR); |
---|
287 | aws->at("min_abs"); aws->create_input_field_with_scaler(AWAR_BA_MIN_ABS_DIFF, FIELDWIDTH, SCALERWIDTH, AW_SCALER_EXP_LOWER); |
---|
288 | |
---|
289 | |
---|
290 | aws->at("mark_deep"); |
---|
291 | aws->callback(makeWindowCallback(mark_deep_leafs_cb, this)); |
---|
292 | aws->create_button("MARK_DEEP", "Mark deep leafs"); |
---|
293 | |
---|
294 | aws->at("tree_depth"); aws->create_input_field_with_scaler(AWAR_BA_MIN_DEPTH, FIELDWIDTH, SCALERWIDTH, AW_SCALER_EXP_LOWER); |
---|
295 | aws->at("branch_depth"); aws->create_input_field_with_scaler(AWAR_BA_MIN_ROOTDIST, FIELDWIDTH, SCALERWIDTH, AW_SCALER_EXP_LOWER); |
---|
296 | |
---|
297 | aws->at("mark_degen"); |
---|
298 | aws->callback(makeWindowCallback(mark_degenerated_branches_cb, this)); |
---|
299 | aws->create_button("MARK_DEGENERATED", "Mark degenerated branches"); |
---|
300 | |
---|
301 | aws->at("degen"); aws->create_input_field_with_scaler(AWAR_BA_DEGENERATION, FIELDWIDTH, SCALERWIDTH, AW_SCALER_EXP_LOWER); |
---|
302 | |
---|
303 | aws->at("config"); |
---|
304 | AWT_insert_config_manager(aws, AW_ROOT_DEFAULT, "branch_analysis", branch_analysis_config_mapping); |
---|
305 | } |
---|
306 | |
---|
307 | AW_window *NT_create_branch_analysis_window(AW_root *aw_root, TREE_canvas *ntw) { |
---|
308 | int ntw_id = ntw->get_index(); |
---|
309 | if (!existingBranchWindow[ntw_id]) { |
---|
310 | existingBranchWindow[ntw_id] = new BranchWindow(aw_root, ntw); |
---|
311 | } |
---|
312 | nt_assert(existingBranchWindow[ntw_id]); |
---|
313 | return existingBranchWindow[ntw_id]->get_window(); |
---|
314 | } |
---|
315 | |
---|