1 | // =============================================================== // |
---|
2 | // // |
---|
3 | // File : SEC_paint.cxx // |
---|
4 | // Purpose : // |
---|
5 | // // |
---|
6 | // Institute of Microbiology (Technical University Munich) // |
---|
7 | // http://www.arb-home.de/ // |
---|
8 | // // |
---|
9 | // =============================================================== // |
---|
10 | |
---|
11 | #include "SEC_root.hxx" |
---|
12 | #include "SEC_graphic.hxx" |
---|
13 | #include "SEC_iter.hxx" |
---|
14 | #include "SEC_drawn_pos.hxx" |
---|
15 | #include "SEC_bonddef.hxx" |
---|
16 | #include "SEC_toggle.hxx" |
---|
17 | |
---|
18 | #include <aw_msg.hxx> |
---|
19 | #include <iupac.h> |
---|
20 | |
---|
21 | #include <ed4_extern.hxx> |
---|
22 | |
---|
23 | #include <arb_defs.h> |
---|
24 | |
---|
25 | #include <iostream> |
---|
26 | #include <sstream> |
---|
27 | |
---|
28 | using namespace std; |
---|
29 | |
---|
30 | // ------------------- |
---|
31 | // Debugging |
---|
32 | |
---|
33 | #if defined(ASSERTION_USED) |
---|
34 | |
---|
35 | inline bool valid_cb_params(AW_CL cd1, AW_CL cd2) { |
---|
36 | return cd1 == 0 || cd2 != -1; |
---|
37 | } |
---|
38 | inline bool valid_cb_params(AW_device *device) { |
---|
39 | const AW_click_cd *cd = device->get_click_cd(); |
---|
40 | return valid_cb_params(cd->get_cd1(), cd->get_cd2()); |
---|
41 | } |
---|
42 | |
---|
43 | #endif |
---|
44 | |
---|
45 | #if defined(DEBUG) |
---|
46 | // #define PAINT_REGION_INDEX // // paint region-internal index next to base |
---|
47 | |
---|
48 | static void paintDebugInfo(AW_device *device, int color, const Position& pos, const char *txt) { |
---|
49 | sec_assert(valid_cb_params(device)); |
---|
50 | device->circle(color, AW::FillStyle::SOLID, pos.xpos(), pos.ypos(), 0.06, 0.06); |
---|
51 | device->text(SEC_GC_DEFAULT, txt, pos.xpos(), pos.ypos(), 0, AW_SCREEN); |
---|
52 | } |
---|
53 | static void paintStrandDebugInfo(AW_device *device, int color, SEC_helix_strand *strand) { |
---|
54 | AW_click_cd cd(device, strand->self(), strand->rightAttachAbspos()); paintDebugInfo(device, color, strand->rightAttachPoint(), "RAP"); |
---|
55 | cd.set_cd2(strand->leftAttachAbspos()); paintDebugInfo(device, color, strand->leftAttachPoint(), "LAP"); |
---|
56 | cd.set_cd2(strand->startAttachAbspos()); paintDebugInfo(device, color, strand->get_fixpoint(), strand->isRootsideFixpoint() ? "RFP" : "FP"); |
---|
57 | } |
---|
58 | |
---|
59 | #endif // DEBUG |
---|
60 | |
---|
61 | // ------------------- |
---|
62 | // PaintData |
---|
63 | |
---|
64 | class PaintData { |
---|
65 | int gc_edit4_to_secedit[ED4_G_DRAG+1]; // GC translation table (EDIT4 -> SECEDIT) |
---|
66 | int line_property_gc[SEC_GC_LAST_DATA+1][SEC_GC_LAST_DATA+1]; // of two GCs, which is responsible for line properties |
---|
67 | |
---|
68 | public: |
---|
69 | PaintData() { |
---|
70 | int gc; |
---|
71 | |
---|
72 | // GC translation (EDIT4->SECEDIT) |
---|
73 | for (gc = 0; gc <= ED4_G_DRAG; gc++) { |
---|
74 | gc_edit4_to_secedit[gc] = -1; // invalid |
---|
75 | } |
---|
76 | for (gc = ED4_G_SBACK_0; gc <= ED4_G_SBACK_8; gc++) { // IRRELEVANT_LOOP |
---|
77 | gc_edit4_to_secedit[gc] = gc-ED4_G_SBACK_0+SEC_GC_SBACK_0; |
---|
78 | } |
---|
79 | for (gc = ED4_G_CBACK_0; gc <= ED4_G_CBACK_9; gc++) { // IRRELEVANT_LOOP |
---|
80 | gc_edit4_to_secedit[gc] = gc-ED4_G_CBACK_0+SEC_GC_CBACK_0; |
---|
81 | } |
---|
82 | |
---|
83 | // calc line property GCs |
---|
84 | for (gc = SEC_GC_FIRST_DATA; gc <= SEC_GC_LAST_DATA; gc++) { |
---|
85 | for (int gc2 = SEC_GC_FIRST_DATA; gc <= SEC_GC_LAST_DATA; gc++) { |
---|
86 | int prop_gc; |
---|
87 | if (gc == gc2) { |
---|
88 | prop_gc = gc; |
---|
89 | } |
---|
90 | else { |
---|
91 | if (gc == SEC_GC_LOOP || gc2 == SEC_GC_LOOP) { |
---|
92 | prop_gc = SEC_GC_LOOP; // use loop-properties in loop and at loop-helix-transition |
---|
93 | } |
---|
94 | else if (gc == SEC_GC_NHELIX || gc2 == SEC_GC_NHELIX) { |
---|
95 | prop_gc = SEC_GC_NHELIX; // use nhelix-properties in nhelix and at helix-nhelix-transition |
---|
96 | } |
---|
97 | else { |
---|
98 | prop_gc = SEC_GC_HELIX; // use helix-properties in helix |
---|
99 | } |
---|
100 | } |
---|
101 | line_property_gc[gc][gc2] = prop_gc; |
---|
102 | } |
---|
103 | } |
---|
104 | } |
---|
105 | |
---|
106 | int convert_BackgroundGC(int edit4_gc) const { |
---|
107 | // returns -1 if edit4_gc is invalid |
---|
108 | sec_assert(edit4_gc >= 0 && edit4_gc <= ED4_G_DRAG); |
---|
109 | return gc_edit4_to_secedit[edit4_gc]; |
---|
110 | } |
---|
111 | |
---|
112 | int get_linePropertyGC(int gc1, int gc2) { |
---|
113 | // of the GCs of two positions, it returns the GC which is |
---|
114 | // defining the properties for the background painted in-between the two positions |
---|
115 | sec_assert(gc1 >= SEC_GC_FIRST_DATA && gc1 <= SEC_GC_LAST_DATA); |
---|
116 | sec_assert(gc2 >= SEC_GC_FIRST_DATA && gc2 <= SEC_GC_LAST_DATA); |
---|
117 | return line_property_gc[gc1][gc2]; |
---|
118 | } |
---|
119 | }; |
---|
120 | |
---|
121 | static PaintData paintData; |
---|
122 | |
---|
123 | // --------------------- |
---|
124 | // Annotations |
---|
125 | |
---|
126 | void SEC_root::paintAnnotation(AW_device *device, int gc, |
---|
127 | const Position& pos, const Position& left, const Position& right, |
---|
128 | double noteDistance, const char *text, |
---|
129 | bool lineToPos, bool linesToLeftRight, bool boxText) |
---|
130 | { |
---|
131 | // draw annotation to explicit position 'pos' (annotation is drawn "above" the line left->right) |
---|
132 | // The distance between pos and note is determined by |
---|
133 | // * textsize (minimal half textsize/boxsize) and |
---|
134 | // * the given 'noteDistance' |
---|
135 | // lineToPos == true -> draw a line from text to 'pos' |
---|
136 | // linesToLeftRight == true -> draw lines from text to 'left' and 'right' |
---|
137 | // boxText == true -> draw a box around text |
---|
138 | |
---|
139 | sec_assert(valid_cb_params(device)); |
---|
140 | |
---|
141 | Vector strand(left, right); |
---|
142 | Angle pos2note(strand); |
---|
143 | pos2note.rotate270deg(); |
---|
144 | |
---|
145 | int fontgc = gc <= SEC_GC_LAST_FONT ? gc : SEC_GC_DEFAULT; |
---|
146 | double half_charSize = center_char[fontgc].length(); |
---|
147 | |
---|
148 | SizedCstr stext(text); |
---|
149 | |
---|
150 | // calculate textsize |
---|
151 | AW_pos half_width = 0.5 * device->rtransform_size(device->get_string_size(gc, stext)); |
---|
152 | AW_pos half_height = center_char[fontgc].y(); |
---|
153 | |
---|
154 | double note_distance = max(half_height, half_width) * (boxText ? 1.3 : 1.0); |
---|
155 | note_distance = max(note_distance, noteDistance); |
---|
156 | |
---|
157 | Position note_center = pos + pos2note.normal()*note_distance; |
---|
158 | |
---|
159 | if (device->get_filter() & AW_PRINTER) { |
---|
160 | boxText = false; // don't print/xfig-export boxes |
---|
161 | } |
---|
162 | |
---|
163 | if (lineToPos || linesToLeftRight) { |
---|
164 | device->set_line_attributes(gc, 1, AW_SOLID); |
---|
165 | |
---|
166 | if (lineToPos) { |
---|
167 | Vector dist = pos2note.normal()*half_charSize; |
---|
168 | device->line(gc, boxText ? note_center : note_center-dist, pos+dist); |
---|
169 | } |
---|
170 | if (linesToLeftRight) { |
---|
171 | Vector out(pos, note_center); |
---|
172 | |
---|
173 | if (out.length()*2 >= strand.length()) { // short strands -> draw simple bracket |
---|
174 | Vector toLeft(note_center, left); |
---|
175 | Vector toRight(note_center, right); |
---|
176 | |
---|
177 | device->line(gc, boxText ? note_center : note_center+toLeft*(half_width/toLeft.length()), |
---|
178 | left-toLeft*(half_charSize/toLeft.length()), AW_ALL_DEVICES_SCALED); |
---|
179 | device->line(gc, boxText ? note_center : note_center+toRight*(half_width/toRight.length()), |
---|
180 | right-toRight*(half_charSize/toRight.length()), AW_ALL_DEVICES_SCALED); |
---|
181 | } |
---|
182 | else { |
---|
183 | Vector rightIndent = out; |
---|
184 | rightIndent.rotate270deg(); |
---|
185 | |
---|
186 | Position rightOut = right+out+rightIndent; |
---|
187 | Position leftOut = left+out-rightIndent; |
---|
188 | |
---|
189 | Vector posPad = Vector(right, rightOut).set_length(half_charSize); |
---|
190 | device->line(gc, right+posPad, rightOut); |
---|
191 | posPad.rotate90deg(); |
---|
192 | device->line(gc, left+posPad, leftOut); |
---|
193 | |
---|
194 | if (boxText) { |
---|
195 | device->line(gc, leftOut, rightOut); |
---|
196 | } |
---|
197 | else { |
---|
198 | Vector rightTextPad(note_center, rightOut); |
---|
199 | rightTextPad.set_length(half_width); |
---|
200 | |
---|
201 | device->line(gc, note_center+rightTextPad, rightOut); |
---|
202 | device->line(gc, note_center-rightTextPad, leftOut); |
---|
203 | } |
---|
204 | } |
---|
205 | } |
---|
206 | } |
---|
207 | |
---|
208 | Vector center_textcorner(-half_width, half_height); // from center to lower left corner |
---|
209 | Position textcorner = note_center+center_textcorner; |
---|
210 | |
---|
211 | if (boxText) { |
---|
212 | Vector center_corner(-half_width-half_height*0.3, half_height*1.3); // box is 25% bigger than text |
---|
213 | Rectangle box(note_center+center_corner, -2*center_corner); |
---|
214 | |
---|
215 | device->clear_part(box, -1); |
---|
216 | device->box(gc, AW::FillStyle::EMPTY, box); |
---|
217 | } |
---|
218 | |
---|
219 | device->text(gc, text, textcorner); |
---|
220 | } |
---|
221 | |
---|
222 | void SEC_root::paintPosAnnotation(AW_device *device, int gc, size_t absPos, const char *text, bool lineToBase, bool boxText) { |
---|
223 | // draw a annotation next to a base (only works after paint()). |
---|
224 | // if nothing was drawn at absPos, annotate a position between previous and next drawn position. |
---|
225 | // text == NULp -> draw absPos as number |
---|
226 | // lineToBase == true -> draw a line to the base itself |
---|
227 | // boxText == true -> draw a box around text |
---|
228 | |
---|
229 | size_t abs1, abs2; |
---|
230 | const Position& pos1 = drawnPositions->drawn_before(absPos, &abs1); |
---|
231 | const Position& pos2 = drawnPositions->drawn_after (absPos, &abs2); |
---|
232 | |
---|
233 | LineVector vec12(pos1, pos2); |
---|
234 | Position mid12 = vec12.centroid(); |
---|
235 | Position pos; |
---|
236 | { |
---|
237 | const Position *posDrawn = drawnPositions->drawn_at(absPos); |
---|
238 | if (posDrawn) { // absPos was drawn |
---|
239 | pos = *posDrawn; |
---|
240 | } |
---|
241 | else { // absPos was not drawn -> use position in-between |
---|
242 | pos = mid12; |
---|
243 | } |
---|
244 | } |
---|
245 | |
---|
246 | if (!text) text = GBS_global_string("%zu", absPos); |
---|
247 | |
---|
248 | AW_click_cd cd(device, 0, absPos); |
---|
249 | paintAnnotation(device, gc, pos, pos1, pos2, vec12.length(), text, lineToBase, false, boxText); |
---|
250 | } |
---|
251 | |
---|
252 | void SEC_root::paintEcoliPositions(AW_device *device) { |
---|
253 | long abspos = db->ecoli()->rel_2_abs(0); |
---|
254 | paintPosAnnotation(device, SEC_GC_ECOLI, size_t(abspos), "1", true, true); |
---|
255 | |
---|
256 | const BI_ecoli_ref *ecoli = db->ecoli(); |
---|
257 | for (size_t ep = bio2info(100); ep < (size_t)ecoli->base_count(); ep += 100) { |
---|
258 | abspos = ecoli->rel_2_abs(ep); |
---|
259 | paintPosAnnotation(device, SEC_GC_ECOLI, size_t(abspos), GBS_global_string("%i", info2bio(ep)), true, true); |
---|
260 | } |
---|
261 | } |
---|
262 | |
---|
263 | void SEC_root::paintHelixNumbers(AW_device *device) { |
---|
264 | for (SEC_base_iterator elem(this); elem; ++elem) { |
---|
265 | if (elem->getType() == SEC_HELIX) { |
---|
266 | SEC_helix& helix = static_cast<SEC_helix&>(*elem); |
---|
267 | |
---|
268 | // paint helix number of right (3') helix strand |
---|
269 | SEC_helix_strand *strand = helix.strandToRoot()->is3end() ? helix.strandToRoot() : helix.strandToOutside(); |
---|
270 | |
---|
271 | int absPos = strand->startAttachAbspos(); |
---|
272 | const char *helixNr = helixNrAt(absPos); |
---|
273 | |
---|
274 | if (helixNr) { |
---|
275 | if (helix.standardSize() == 0) { // helix with zero length (just one position on each strand) |
---|
276 | paintPosAnnotation(device, SEC_GC_HELIX_NO, |
---|
277 | strand->startAttachAbspos(), helixNr, true, true); |
---|
278 | } |
---|
279 | else { |
---|
280 | const Position& start = strand->startAttachPoint(); |
---|
281 | const Position& end = strand->endAttachPoint(); |
---|
282 | Position helixCenter = centroid(start, end); |
---|
283 | |
---|
284 | AW_click_cd cd(device, strand->self(), absPos); |
---|
285 | paintAnnotation(device, SEC_GC_HELIX_NO, |
---|
286 | helixCenter, start, end, |
---|
287 | // displayParams.distance_between_strands*2, |
---|
288 | displayParams.distance_between_strands, |
---|
289 | helixNr, false, true, true); |
---|
290 | } |
---|
291 | } |
---|
292 | } |
---|
293 | } |
---|
294 | } |
---|
295 | |
---|
296 | |
---|
297 | #if defined(PAINT_ABSOLUTE_POSITION) |
---|
298 | void SEC_root::showSomeAbsolutePositions(AW_device *device) { |
---|
299 | if (device->get_filter() != AW_SIZE) { // ignore for size calculation (@@@) |
---|
300 | Rectangle screen = device->rtransform(Rectangle(device->get_area_size(), INCLUSIVE_OUTLINE)); |
---|
301 | Vector diag3 = screen.diagonal()/3; |
---|
302 | Rectangle showInside(screen.upper_left_corner()+diag3*1.85, diag3); |
---|
303 | |
---|
304 | AW_click_cd cd(device, 0, -1); |
---|
305 | device->box(SEC_GC_DEFAULT, AW::FillStyle::EMPTY, showInside); |
---|
306 | |
---|
307 | PosMap::const_iterator end = drawnPositions->end(); |
---|
308 | for (PosMap::const_iterator pos = drawnPositions->begin(); pos != end; ++pos) { |
---|
309 | if (showInside.contains(pos->second)) { |
---|
310 | paintPosAnnotation(device, SEC_GC_DEFAULT, pos->first, NULp, true, true); |
---|
311 | } |
---|
312 | } |
---|
313 | } |
---|
314 | } |
---|
315 | #endif // PAINT_ABSOLUTE_POSITION |
---|
316 | |
---|
317 | void SEC_root::announce_base_position(int base_pos, const Position& draw_pos) { |
---|
318 | drawnPositions->announce(base_pos, draw_pos); |
---|
319 | } |
---|
320 | void SEC_root::clear_announced_positions() { |
---|
321 | if (!drawnPositions) drawnPositions = new SEC_drawn_positions; |
---|
322 | drawnPositions->clear(); |
---|
323 | } |
---|
324 | |
---|
325 | void SEC_root::delete_announced_positions() { |
---|
326 | delete drawnPositions; |
---|
327 | drawnPositions = NULp; |
---|
328 | } |
---|
329 | |
---|
330 | |
---|
331 | // ---------------------------- |
---|
332 | // Paints CONSTRAINTS |
---|
333 | |
---|
334 | void SEC_helix_strand::paint_constraints(AW_device *device) { |
---|
335 | double minS = helix_info->minSize(); |
---|
336 | double maxS = helix_info->maxSize(); |
---|
337 | |
---|
338 | if (minS>0 || maxS>0) { |
---|
339 | const Position& startP = startAttachPoint(); |
---|
340 | const Position& endP = endAttachPoint(); |
---|
341 | |
---|
342 | bool drawMidLine = minS>0 && maxS>0; |
---|
343 | Position minP = startP + Vector(startP, endP) * (drawMidLine ? minS/maxS : 0.5); |
---|
344 | |
---|
345 | AW_click_cd cd(device, self(), startAttachAbspos()); |
---|
346 | get_root()->paintAnnotation(device, SEC_GC_DEFAULT, |
---|
347 | minP, startP, endP, |
---|
348 | get_root()->display_params().distance_between_strands*2, |
---|
349 | GBS_global_string("%.1f-%.1f", minS, maxS), |
---|
350 | drawMidLine, true, true); |
---|
351 | } |
---|
352 | } |
---|
353 | |
---|
354 | void SEC_loop::paint_constraints(AW_device *device) { |
---|
355 | int abspos = get_fixpoint_strand()->startAttachAbspos(); |
---|
356 | |
---|
357 | double minS = minSize(); |
---|
358 | double maxS = maxSize(); |
---|
359 | |
---|
360 | if (minS>0 || maxS>0) { |
---|
361 | AW_click_cd cd(device, self(), abspos); |
---|
362 | |
---|
363 | if (minS>0) device->circle(SEC_GC_DEFAULT, AW::FillStyle::EMPTY, center, Vector(minS, minS)); |
---|
364 | if (maxS>0) device->circle(SEC_GC_DEFAULT, AW::FillStyle::EMPTY, center, Vector(maxS, maxS)); |
---|
365 | |
---|
366 | device->text(SEC_GC_DEFAULT, GBS_global_string("%.1f-%.1f", minS, maxS), center+Vector(0, max(minS, maxS)/2), 0.5, AW_ALL_DEVICES_UNSCALED); |
---|
367 | } |
---|
368 | } |
---|
369 | |
---|
370 | // --------------------------- |
---|
371 | // Background colors |
---|
372 | |
---|
373 | #if defined(WARN_TODO) |
---|
374 | #warning move to SEC_db_interface |
---|
375 | #endif |
---|
376 | void SEC_root::cacheBackgroundColor() { |
---|
377 | freenull(bg_color); |
---|
378 | |
---|
379 | int start = 0; |
---|
380 | int len = db->length(); |
---|
381 | int end = len-1; |
---|
382 | |
---|
383 | ARB_alloc(bg_color, len); |
---|
384 | |
---|
385 | const char *bg_sai = displayParams.display_sai ? host().get_SAI_background(start, end) : NULp; |
---|
386 | const char *bg_search = displayParams.display_search ? host().get_search_background(start, end) : NULp; |
---|
387 | |
---|
388 | if (bg_sai) { |
---|
389 | if (bg_search) { |
---|
390 | for (int i = start; i <= end; ++i) { |
---|
391 | bg_color[i] = bg_search[i] ? bg_search[i] : bg_sai[i]; |
---|
392 | } |
---|
393 | } |
---|
394 | else memcpy(bg_color, bg_sai, len); |
---|
395 | } |
---|
396 | else { |
---|
397 | if (bg_search) memcpy(bg_color, bg_search, len); |
---|
398 | else memset(bg_color, 0, len); |
---|
399 | } |
---|
400 | } |
---|
401 | |
---|
402 | void SEC_root::paintBackgroundColor(AW_device *device, SEC_bgpaint_mode mode, const Position& p1, int color1, int gc1, const Position& p2, int color2, int gc2, int skel_gc) { |
---|
403 | // paints background colors for p2 and connection between p1 and p2. |
---|
404 | // gc1/gc2 are foreground gc used to detect size of background regions |
---|
405 | // |
---|
406 | // Also paints skeleton |
---|
407 | |
---|
408 | sec_assert(valid_cb_params(device)); |
---|
409 | |
---|
410 | color1 = paintData.convert_BackgroundGC(color1); // convert EDIT4-GCs into SECEDIT-GCs |
---|
411 | color2 = paintData.convert_BackgroundGC(color2); |
---|
412 | |
---|
413 | if (color1 >= 0 || color2 >= 0 || displayParams.show_strSkeleton) { |
---|
414 | const double& radius1 = get_char_radius(gc1); |
---|
415 | const double& radius2 = get_char_radius(gc2); |
---|
416 | |
---|
417 | if (mode & BG_PAINT_FIRST && color1 >= 0) { // paint first circle ? |
---|
418 | device->circle(color1, AW::FillStyle::SOLID, p1, Vector(radius1, radius1)); |
---|
419 | } |
---|
420 | |
---|
421 | if (mode & BG_PAINT_SECOND && color2 >= 0) { // paint second circle ? |
---|
422 | device->circle(color2, AW::FillStyle::SOLID, p2, Vector(radius1, radius1)); |
---|
423 | } |
---|
424 | |
---|
425 | if (color1 == color2 && color1 >= 0) { // colors are equal -> paint background between points |
---|
426 | device->set_line_attributes(color1, bg_linewidth[paintData.get_linePropertyGC(gc1, gc2)], AW_SOLID); |
---|
427 | device->line(color1, p1, p2); |
---|
428 | } |
---|
429 | |
---|
430 | if (displayParams.show_strSkeleton) { // paint skeleton? |
---|
431 | Position s1 = p1; |
---|
432 | Position s2 = p2; |
---|
433 | bool space = false; |
---|
434 | |
---|
435 | if (displayParams.hide_bases) { |
---|
436 | space = true; // no base chars -> enough space to paint |
---|
437 | } |
---|
438 | else { |
---|
439 | Vector v12(p1, p2); |
---|
440 | double vlen = v12.length(); |
---|
441 | |
---|
442 | // Note: LINE_THICKNESS |
---|
443 | // Lines drawn with thickness != 1 differ between motif-version and gtk-version: |
---|
444 | // in motif thicker lines are also drawn longer than specified (half thickness on each side) |
---|
445 | const double CORR = skelThickWorld; |
---|
446 | |
---|
447 | if ((radius1+radius2+CORR) < vlen) { // test if there is enough space between characters |
---|
448 | s1 = p1 + v12*((radius1+CORR/2)/vlen); // skeleton<->base attach-points |
---|
449 | s2 = p2 - v12*((radius2+CORR/2)/vlen); |
---|
450 | space = true; |
---|
451 | } |
---|
452 | } |
---|
453 | |
---|
454 | if (space) { |
---|
455 | device->set_line_attributes(skel_gc, displayParams.skeleton_thickness, AW_SOLID); |
---|
456 | #if defined(DEBUG) |
---|
457 | if (displayParams.show_debug) { s1 = p1; s2 = p2; } // in debug mode always show full skeleton |
---|
458 | #endif // DEBUG |
---|
459 | device->line(skel_gc, s1, s2); |
---|
460 | } |
---|
461 | } |
---|
462 | } |
---|
463 | } |
---|
464 | |
---|
465 | void SEC_root::paintSearchPatternStrings(AW_device *device, int clickedPos, AW_pos xPos, AW_pos yPos) { |
---|
466 | int searchColor = getBackgroundColor(clickedPos); |
---|
467 | |
---|
468 | if (searchColor >= SEC_GC_SBACK_0 && searchColor <= SEC_GC_SBACK_8) { |
---|
469 | static const char *text[SEC_GC_SBACK_8-SEC_GC_SBACK_0+1] = { |
---|
470 | "User 1", |
---|
471 | "User 2", |
---|
472 | "Probe", |
---|
473 | "Primer (local)", |
---|
474 | "Primer (region)", |
---|
475 | "Primer (global)", |
---|
476 | "Signature (local)", |
---|
477 | "Signature (region)", |
---|
478 | "Signature (global)", |
---|
479 | }; |
---|
480 | |
---|
481 | AW_click_cd cd(device, 0, clickedPos); |
---|
482 | device->text(searchColor, text[searchColor-SEC_GC_SBACK_0], xPos, yPos, 0, AW_SCREEN); |
---|
483 | } |
---|
484 | else { |
---|
485 | aw_message("Please click on a search result"); |
---|
486 | } |
---|
487 | } |
---|
488 | |
---|
489 | // --------------- |
---|
490 | // Bonds |
---|
491 | |
---|
492 | void SEC_bond_def::paint(AW_device *device, char base1, char base2, const Position& p1, const Position& p2, const Vector& toNextBase, const double& char_radius) const { |
---|
493 | if (base1 && base2) { |
---|
494 | char Bond = get_bond(base1, base2); |
---|
495 | if (Bond == ' ') { |
---|
496 | // check IUPACs |
---|
497 | const char *iupac1 = iupac::decode(base1, ali_type, 0); |
---|
498 | const char *iupac2 = iupac::decode(base2, ali_type, 0); |
---|
499 | |
---|
500 | bool useBond[SEC_BOND_PAIR_CHARS]; |
---|
501 | for (int i = 0; i<SEC_BOND_PAIR_CHARS; i++) useBond[i] = false; |
---|
502 | |
---|
503 | int maxIdx = -1; |
---|
504 | for (int i1 = 0; iupac1[i1]; ++i1) { |
---|
505 | for (int i2 = 0; iupac2[i2]; ++i2) { |
---|
506 | char b = get_bond(iupac1[i1], iupac2[i2]); |
---|
507 | |
---|
508 | if (b != ' ') { |
---|
509 | const char *found = strchr(SEC_BOND_PAIR_CHAR, b); |
---|
510 | int idx = found-SEC_BOND_PAIR_CHAR; |
---|
511 | |
---|
512 | useBond[idx] = true; |
---|
513 | if (idx>maxIdx) maxIdx = idx; |
---|
514 | } |
---|
515 | } |
---|
516 | } |
---|
517 | |
---|
518 | if (maxIdx >= 0) { |
---|
519 | for (int i = 0; i<SEC_BOND_PAIR_CHARS; i++) { |
---|
520 | if (useBond[i]) { |
---|
521 | paint(device, i == maxIdx ? SEC_GC_BONDS : SEC_GC_ERROR, SEC_BOND_PAIR_CHAR[i], p1, p2, toNextBase, char_radius); |
---|
522 | } |
---|
523 | } |
---|
524 | } |
---|
525 | } |
---|
526 | else { |
---|
527 | paint(device, SEC_GC_BONDS, Bond, p1, p2, toNextBase, char_radius); |
---|
528 | } |
---|
529 | } |
---|
530 | } |
---|
531 | |
---|
532 | void SEC_bond_def::paint(AW_device *device, int GC, char bondChar, const Position& p1, const Position& p2, const Vector& toNextBase, const double& char_radius) const { |
---|
533 | Vector v12(p1, p2); |
---|
534 | double oppoDist = v12.length(); |
---|
535 | double bondLen = oppoDist-2*char_radius; |
---|
536 | |
---|
537 | if (bondLen <= 0.0) return; // not enough space to draw bond |
---|
538 | |
---|
539 | Vector pb = v12*(char_radius/oppoDist); |
---|
540 | |
---|
541 | Position b1 = p1+pb; // start/end pos of bond |
---|
542 | Position b2 = p2-pb; |
---|
543 | |
---|
544 | Position center = centroid(b1, b2); |
---|
545 | |
---|
546 | Vector aside = toNextBase; |
---|
547 | { |
---|
548 | // limit aside-size by strand-distance |
---|
549 | double aside_len = aside.length(); |
---|
550 | double max_aside_len = min(aside_len, Vector(b1, b2).length()); |
---|
551 | if (max_aside_len<aside_len) { |
---|
552 | aside *= max_aside_len/aside_len; |
---|
553 | } |
---|
554 | } |
---|
555 | aside *= 0.22; // max. 22% towards next base position (has to be less than 25%, because 'aside' is added twice for some bondtypes) |
---|
556 | |
---|
557 | switch (bondChar) { |
---|
558 | case '-': // single line |
---|
559 | device->line(GC, b1, b2); |
---|
560 | break; |
---|
561 | |
---|
562 | case '#': // double cross |
---|
563 | case '=': // double line |
---|
564 | device->line(GC, b1+aside, b2+aside); |
---|
565 | device->line(GC, b1-aside, b2-aside); |
---|
566 | |
---|
567 | if (bondChar == '#') { |
---|
568 | Vector outside = v12*(bondLen/oppoDist/4); |
---|
569 | Position c1 = center+outside; |
---|
570 | Position c2 = center-outside; |
---|
571 | |
---|
572 | aside *= 2; |
---|
573 | |
---|
574 | device->line(GC, c1-aside, c1+aside); |
---|
575 | device->line(GC, c2-aside, c2+aside); |
---|
576 | } |
---|
577 | break; |
---|
578 | |
---|
579 | case '~': { |
---|
580 | double radius = aside.length(); |
---|
581 | { |
---|
582 | double maxRadius = bondLen/4; |
---|
583 | if (maxRadius<radius) radius = maxRadius; |
---|
584 | } |
---|
585 | |
---|
586 | Vector outside = v12*(radius/oppoDist); |
---|
587 | |
---|
588 | Position c1 = center+outside; |
---|
589 | Position c2 = center-outside; |
---|
590 | |
---|
591 | aside *= 2; |
---|
592 | |
---|
593 | Angle angle(outside); |
---|
594 | int deg = AW_INT(angle.degrees()); |
---|
595 | |
---|
596 | const int INSIDE = 2; |
---|
597 | const int OUTSIDE = 15; |
---|
598 | |
---|
599 | Vector vRadius(radius, radius); |
---|
600 | device->arc(GC, AW::FillStyle::EMPTY, c1, vRadius, deg+180+INSIDE, -(180+INSIDE+OUTSIDE)); |
---|
601 | device->arc(GC, AW::FillStyle::EMPTY, c2, vRadius, deg+INSIDE, -(180+INSIDE+OUTSIDE)); |
---|
602 | break; |
---|
603 | } |
---|
604 | |
---|
605 | case '+': // cross |
---|
606 | aside *= 2; |
---|
607 | device->line(GC, center-aside, center+aside); |
---|
608 | if (2*aside.length() < bondLen) { |
---|
609 | aside.rotate90deg(); |
---|
610 | device->line(GC, center-aside, center+aside); |
---|
611 | } |
---|
612 | else { |
---|
613 | device->line(GC, b1, b2); |
---|
614 | } |
---|
615 | break; |
---|
616 | |
---|
617 | case 'o': |
---|
618 | case '.': { // circles |
---|
619 | double radius = aside.length(); |
---|
620 | if (bondChar == 'o') radius *= 2; |
---|
621 | device->circle(GC, AW::FillStyle::EMPTY, center, Vector(radius, radius)); |
---|
622 | break; |
---|
623 | } |
---|
624 | |
---|
625 | case '@': // error in bonddef |
---|
626 | device->text(GC, "Err", center+Vector(0, char_radius), 0.5, AW_ALL_DEVICES_UNSCALED); |
---|
627 | break; |
---|
628 | |
---|
629 | default: |
---|
630 | sec_assert(0); // // illegal bond char |
---|
631 | break; |
---|
632 | } |
---|
633 | } |
---|
634 | |
---|
635 | // ----------------------- |
---|
636 | // Paint helices |
---|
637 | |
---|
638 | struct StrandPositionData { |
---|
639 | int abs[2]; // absolute sequence position |
---|
640 | int previous[2]; // previous drawn index |
---|
641 | bool drawn[2]; // draw position ? |
---|
642 | bool isPair; // true if position is pairing |
---|
643 | Position realpos[2]; // real position |
---|
644 | }; |
---|
645 | |
---|
646 | void SEC_helix_strand::paint_strands(AW_device *device, const Vector& strand_vec, const double& strand_len) { |
---|
647 | static StrandPositionData *data = NULp; |
---|
648 | static int allocated = 0; |
---|
649 | |
---|
650 | const SEC_region* Region[2] = { get_region(), other_strand->get_region() }; |
---|
651 | int base_count = Region[0]->get_base_count(); |
---|
652 | |
---|
653 | sec_assert(Region[1]->get_base_count() == base_count); // not aligned ? |
---|
654 | |
---|
655 | if (base_count<1) { |
---|
656 | return; // completely skip painting on strands w/o any base |
---|
657 | } |
---|
658 | |
---|
659 | if (allocated<base_count) { |
---|
660 | delete [] data; |
---|
661 | data = new StrandPositionData[base_count]; |
---|
662 | allocated = base_count; |
---|
663 | } |
---|
664 | |
---|
665 | SEC_root *root = get_root(); |
---|
666 | const BI_helix *helix = root->get_helixDef(); |
---|
667 | |
---|
668 | double base_dist = base_count>1 ? strand_len / (base_count-1) : 1; |
---|
669 | Vector vnext = strand_vec * base_dist; // vector from base to next base (in strand) |
---|
670 | |
---|
671 | // first calculate positions |
---|
672 | { |
---|
673 | StrandPositionData *curr = &data[0]; |
---|
674 | |
---|
675 | int idx[2] = { 0, base_count-1 }; |
---|
676 | Position pos[2] = { leftAttach, rightAttach }; |
---|
677 | Vector toNonBind[2]; // vectors from normal to non-binding positions |
---|
678 | toNonBind[1] = (strand_vec*0.5).rotate90deg(); |
---|
679 | toNonBind[0] = -toNonBind[1]; |
---|
680 | |
---|
681 | for (int strand = 0; strand<2; ++strand) { |
---|
682 | curr->abs[strand] = Region[strand]->getBasePos(idx[strand]); |
---|
683 | curr->previous[strand] = 0; |
---|
684 | curr->drawn[strand] = (curr->abs[strand] >= 0); |
---|
685 | } |
---|
686 | |
---|
687 | for (int dIdx = 1; ; ++dIdx) { |
---|
688 | sec_assert(pos[0].valid()); |
---|
689 | sec_assert(pos[1].valid()); |
---|
690 | |
---|
691 | int oneAbs = curr->drawn[0] ? curr->abs[0] : curr->abs[1]; |
---|
692 | sec_assert(oneAbs >= 0); // otherwise current position should have been eliminated by align_helix_strands |
---|
693 | curr->isPair = (helix->pairtype(oneAbs) != HELIX_NONE); |
---|
694 | |
---|
695 | for (int strand = 0; strand<2; ++strand) { |
---|
696 | if (curr->isPair) { |
---|
697 | curr->realpos[strand] = pos[strand]; |
---|
698 | curr->drawn[strand] = true; |
---|
699 | } |
---|
700 | else { |
---|
701 | curr->realpos[strand] = pos[strand]+toNonBind[strand]; |
---|
702 | } |
---|
703 | |
---|
704 | sec_assert(curr->realpos[strand].valid()); |
---|
705 | } |
---|
706 | |
---|
707 | if (dIdx >= base_count) break; |
---|
708 | |
---|
709 | ++idx[0]; |
---|
710 | --idx[1]; |
---|
711 | |
---|
712 | StrandPositionData *prev = curr; |
---|
713 | curr = &data[dIdx]; |
---|
714 | |
---|
715 | for (int strand = 0; strand<2; ++strand) { |
---|
716 | pos[strand] += vnext; |
---|
717 | curr->abs[strand] = Region[strand]->getBasePos(idx[strand]); |
---|
718 | curr->previous[strand] = prev->drawn[strand] ? dIdx-1 : prev->previous[strand]; |
---|
719 | curr->drawn[strand] = (curr->abs[strand] >= 0); |
---|
720 | } |
---|
721 | } |
---|
722 | } |
---|
723 | |
---|
724 | const int pair2helixGC[2] = { SEC_GC_NHELIX, SEC_GC_HELIX }; |
---|
725 | const int pair2skelGC[2] = { SEC_SKELE_NHELIX, SEC_SKELE_HELIX }; |
---|
726 | |
---|
727 | const SEC_db_interface *db = root->get_db(); |
---|
728 | const SEC_displayParams& disp = root->display_params(); |
---|
729 | |
---|
730 | // draw background and skeleton |
---|
731 | for (int pos = 1; pos<base_count; ++pos) { |
---|
732 | StrandPositionData *curr = &data[pos]; |
---|
733 | for (int strand = 0; strand<2; ++strand) { |
---|
734 | if (curr->drawn[strand]) { |
---|
735 | StrandPositionData *prev = &data[curr->previous[strand]]; |
---|
736 | |
---|
737 | int backAbs = disp.edit_rightward |
---|
738 | ? max(prev->abs[strand], curr->abs[strand]) |
---|
739 | : min(prev->abs[strand], curr->abs[strand]); |
---|
740 | |
---|
741 | AW_click_cd cd(device, self(), backAbs); |
---|
742 | root->paintBackgroundColor(device, |
---|
743 | pos == base_count-1 ? BG_PAINT_NONE : BG_PAINT_SECOND, |
---|
744 | prev->realpos[strand], root->getBackgroundColor(prev->abs[strand]), pair2helixGC[prev->isPair], |
---|
745 | curr->realpos[strand], root->getBackgroundColor(curr->abs[strand]), pair2helixGC[curr->isPair], |
---|
746 | pair2skelGC[curr->isPair && prev->isPair]); |
---|
747 | } |
---|
748 | } |
---|
749 | } |
---|
750 | |
---|
751 | // draw base characters and bonds |
---|
752 | char baseBuf[20] = "x"; |
---|
753 | for (int pos = 0; pos<base_count; ++pos) { |
---|
754 | StrandPositionData *curr = &data[pos]; |
---|
755 | char base[2] = { 0, 0 }; |
---|
756 | |
---|
757 | int gc = pair2helixGC[curr->isPair]; |
---|
758 | Vector center_char = root->get_center_char_vector(gc); |
---|
759 | |
---|
760 | for (int strand = 0; strand<2; ++strand) { |
---|
761 | if (curr->drawn[strand]) { |
---|
762 | int abs = curr->abs[strand]; |
---|
763 | const Position& realPos = curr->realpos[strand]; |
---|
764 | |
---|
765 | sec_assert(abs >= 0); |
---|
766 | |
---|
767 | base[strand] = db->baseAt(abs); |
---|
768 | root->announce_base_position(abs, realPos); |
---|
769 | |
---|
770 | if (!disp.hide_bases) { |
---|
771 | baseBuf[0] = base[strand]; |
---|
772 | Position base_pos = realPos + center_char; // center base at realpos |
---|
773 | AW_click_cd cd(device, self(), abs); |
---|
774 | #if defined(DEBUG) |
---|
775 | if (disp.show_debug) device->line(gc, realPos, base_pos); |
---|
776 | #endif // DEBUG |
---|
777 | |
---|
778 | device->text(gc, baseBuf, base_pos, 0.0, AW_ALL_DEVICES_SCALED); |
---|
779 | } |
---|
780 | } |
---|
781 | } |
---|
782 | |
---|
783 | if (disp.show_bonds == SHOW_NHELIX_BONDS || (disp.show_bonds == SHOW_HELIX_BONDS && curr->isPair)) { |
---|
784 | AW_click_cd cd(device, self(), curr->abs[0]); |
---|
785 | db->bonds()->paint(device, base[0], base[1], curr->realpos[0], curr->realpos[1], vnext, |
---|
786 | root->get_char_radius(pair2helixGC[curr->isPair]) |
---|
787 | +root->get_bondThickWorld()/2 // see .@LINE_THICKNESS |
---|
788 | ); |
---|
789 | } |
---|
790 | } |
---|
791 | } |
---|
792 | |
---|
793 | void SEC_helix_strand::paint(AW_device *device) { |
---|
794 | sec_assert(isRootsideFixpoint()); |
---|
795 | |
---|
796 | Vector strand_vec(rightAttach, other_strand->leftAttach); |
---|
797 | double strand_len = strand_vec.length(); // length of strand |
---|
798 | |
---|
799 | if (strand_len>0) { |
---|
800 | strand_vec.normalize(); // normalize |
---|
801 | } |
---|
802 | else { // strand with zero length (contains only one base-pair) |
---|
803 | strand_vec = Vector(rightAttach, leftAttach).rotate90deg(); |
---|
804 | } |
---|
805 | |
---|
806 | other_strand->origin_loop->paint(device); // first paint next loop |
---|
807 | paint_strands(device, strand_vec, strand_len); // then paint strand |
---|
808 | |
---|
809 | SEC_root *root = get_root(); |
---|
810 | const SEC_displayParams& disp = root->display_params(); |
---|
811 | |
---|
812 | if (disp.show_strSkeleton && !disp.show_bonds && disp.hide_bases) { |
---|
813 | // display strand direction |
---|
814 | LineVector strandArrow; |
---|
815 | if (strand_len>0) { |
---|
816 | strandArrow = LineVector(get_fixpoint(), strand_vec); |
---|
817 | } |
---|
818 | else { |
---|
819 | Vector fix2arrowStart(get_fixpoint(), leftAttachPoint()); |
---|
820 | fix2arrowStart.rotate90deg(); |
---|
821 | strandArrow = LineVector(get_fixpoint()-fix2arrowStart, 2*fix2arrowStart); |
---|
822 | } |
---|
823 | |
---|
824 | AW_click_cd cd(device, get_helix()->self(), startAttachAbspos()); |
---|
825 | device->line(SEC_GC_HELIX, strandArrow); |
---|
826 | |
---|
827 | Vector right = strandArrow.line_vector(); // left arrowhead vector |
---|
828 | right = (right * (disp.distance_between_strands*0.35/right.length())).rotate135deg(); |
---|
829 | |
---|
830 | Vector left = Vector(right).rotate90deg(); |
---|
831 | |
---|
832 | Position head = strandArrow.head(); |
---|
833 | device->line(SEC_GC_HELIX, LineVector(head, left)); |
---|
834 | device->line(SEC_GC_HELIX, LineVector(head, right)); |
---|
835 | } |
---|
836 | |
---|
837 | #if defined(DEBUG) |
---|
838 | if (disp.show_debug) paintStrandDebugInfo(device, SEC_GC_HELIX, other_strand); |
---|
839 | #endif // DEBUG |
---|
840 | |
---|
841 | if (root->get_show_constraints() & SEC_HELIX) paint_constraints(device); |
---|
842 | } |
---|
843 | |
---|
844 | |
---|
845 | // --------------------- |
---|
846 | // Paint loops |
---|
847 | |
---|
848 | void SEC_segment::paint(AW_device *device, SEC_helix_strand *previous_strand_pointer) { |
---|
849 | int base_count = get_region()->get_base_count(); // bases in segment |
---|
850 | |
---|
851 | const Position& startP = previous_strand_pointer->rightAttachPoint(); |
---|
852 | const Position& endP = next_helix_strand->leftAttachPoint(); |
---|
853 | |
---|
854 | Angle current; // start/current angle |
---|
855 | Angle end; // end angle |
---|
856 | double radius1; // start and.. |
---|
857 | double radius2; // end radius of segment |
---|
858 | |
---|
859 | { |
---|
860 | Vector seg_start_radius(center1, startP); |
---|
861 | radius1 = seg_start_radius.length(); |
---|
862 | current = seg_start_radius; |
---|
863 | |
---|
864 | Vector seg_end_radius(center2, endP); |
---|
865 | radius2 = seg_end_radius.length(); |
---|
866 | end = seg_end_radius; |
---|
867 | } |
---|
868 | |
---|
869 | int steps = base_count+1; |
---|
870 | |
---|
871 | double step = ((end-current)/steps).radian(); |
---|
872 | |
---|
873 | // correct if we have to paint more than a full loop |
---|
874 | if ((alpha - (step*steps)) > M_PI) { |
---|
875 | step += (2*M_PI)/steps; |
---|
876 | } |
---|
877 | |
---|
878 | double radStep = (radius2-radius1)/steps; |
---|
879 | |
---|
880 | Vector cstep(center1, center2); |
---|
881 | cstep /= steps; |
---|
882 | |
---|
883 | SEC_root *root = get_root(); |
---|
884 | const SEC_db_interface *db = root->get_db(); |
---|
885 | const SEC_displayParams& disp = root->display_params(); |
---|
886 | #if defined(DEBUG) |
---|
887 | if (disp.show_debug) { |
---|
888 | paintStrandDebugInfo(device, SEC_GC_LOOP, previous_strand_pointer); |
---|
889 | |
---|
890 | int startAbsPos = previous_strand_pointer->rightAttachAbspos(); |
---|
891 | int endAbsPos = next_helix_strand->leftAttachAbspos(); |
---|
892 | |
---|
893 | AW_click_cd cd(device, self(), startAbsPos); |
---|
894 | paintDebugInfo(device, SEC_GC_LOOP, center1, GBS_global_string("SC1 (step=%5.3f)", step)); |
---|
895 | device->line(SEC_GC_LOOP, center1, startP); |
---|
896 | device->line(SEC_GC_LOOP, center1, center2); |
---|
897 | |
---|
898 | cd.set_cd2(endAbsPos); |
---|
899 | paintDebugInfo(device, SEC_GC_LOOP, center2, "SC2"); |
---|
900 | device->line(SEC_GC_LOOP, center2, endP); |
---|
901 | } |
---|
902 | #endif // DEBUG |
---|
903 | |
---|
904 | char baseBuf[5] = "?"; // contains base char during print |
---|
905 | Position pos = startP; |
---|
906 | int abs = previous_strand_pointer->rightAttachAbspos(); |
---|
907 | int back = root->getBackgroundColor(abs); |
---|
908 | int gc = root->getBondtype(abs) == HELIX_NONE ? SEC_GC_NHELIX : SEC_GC_HELIX; |
---|
909 | int nextGc = SEC_GC_LOOP; |
---|
910 | |
---|
911 | Position currCenter = center1; |
---|
912 | double currRadius = radius1; |
---|
913 | |
---|
914 | Angle step_angle(step); |
---|
915 | |
---|
916 | for (int i = -1; i<base_count; i++) { // for each segment position (plus one pre-loop) |
---|
917 | current += step_angle; // iterate over angles |
---|
918 | currCenter += cstep; |
---|
919 | currRadius += radStep; |
---|
920 | |
---|
921 | Position nextPos = currCenter + current.normal()*currRadius; |
---|
922 | int nextAbs; |
---|
923 | |
---|
924 | if (i == (base_count-1)) { // last position (belongs to strand) |
---|
925 | nextAbs = next_helix_strand->leftAttachAbspos(); |
---|
926 | if (nextAbs<0) { // helix doesn't start with pair |
---|
927 | nextAbs = next_helix_strand->getNextAbspos(); |
---|
928 | } |
---|
929 | nextGc = root->getBondtype(nextAbs) == HELIX_NONE ? SEC_GC_NHELIX : SEC_GC_HELIX; |
---|
930 | } |
---|
931 | else { |
---|
932 | nextAbs = get_region()->getBasePos(i+1); |
---|
933 | } |
---|
934 | |
---|
935 | int nextBack = root->getBackgroundColor(nextAbs); |
---|
936 | |
---|
937 | // paint background (from pos to nextPos) |
---|
938 | AW_click_cd cd(device, self(), disp.edit_rightward ? nextAbs : abs); |
---|
939 | root->paintBackgroundColor(device, i == -1 ? BG_PAINT_BOTH : BG_PAINT_SECOND, |
---|
940 | pos, back, gc, nextPos, nextBack, nextGc, SEC_SKELE_LOOP); |
---|
941 | |
---|
942 | if (i >= 0) { |
---|
943 | // paint base char at pos |
---|
944 | baseBuf[0] = abs>0 ? db->baseAt(abs) : '?'; |
---|
945 | Vector center_char = root->get_center_char_vector(gc); |
---|
946 | Position base_pos = pos + center_char; // center base character at pos |
---|
947 | |
---|
948 | cd.set_cd2(abs); |
---|
949 | if (!disp.hide_bases) { |
---|
950 | #if defined(DEBUG) |
---|
951 | // show line from base paint pos to calculated center of char |
---|
952 | // (which is currently calculated wrong!) |
---|
953 | if (disp.show_debug) device->line(SEC_GC_LOOP, pos, base_pos); |
---|
954 | #endif // DEBUG |
---|
955 | device->text(SEC_GC_LOOP, baseBuf, base_pos, 0.0, AW_ALL_DEVICES_SCALED); |
---|
956 | } |
---|
957 | root->announce_base_position(abs, pos); |
---|
958 | } |
---|
959 | |
---|
960 | // prepare next loop |
---|
961 | pos = nextPos; |
---|
962 | abs = nextAbs; |
---|
963 | back = nextBack; |
---|
964 | gc = nextGc; |
---|
965 | } |
---|
966 | } |
---|
967 | |
---|
968 | void SEC_loop::paint(AW_device *device) { |
---|
969 | for (SEC_segment_iterator seg(this); seg; ++seg) { // first paint all segments |
---|
970 | seg->paint(device, seg->get_previous_strand()); |
---|
971 | } |
---|
972 | for (SEC_strand_iterator strand(this); strand; ++strand) { // then paint all outgoing strands |
---|
973 | if (strand->isRootsideFixpoint()) strand->paint(device); |
---|
974 | } |
---|
975 | |
---|
976 | SEC_root *sroot = get_root(); |
---|
977 | #if defined(DEBUG) |
---|
978 | if (sroot->display_params().show_debug) { |
---|
979 | SEC_helix_strand *fixpoint_strand = get_fixpoint_strand(); |
---|
980 | int abspos = fixpoint_strand->startAttachAbspos(); |
---|
981 | AW_click_cd cd(device, self(), abspos); |
---|
982 | |
---|
983 | device->set_line_attributes(SEC_GC_CURSOR, 1, AW_SOLID); |
---|
984 | device->line(SEC_GC_CURSOR, get_center(), fixpoint_strand->get_fixpoint()); |
---|
985 | |
---|
986 | paintStrandDebugInfo(device, SEC_GC_CURSOR, fixpoint_strand); |
---|
987 | paintDebugInfo(device, SEC_GC_CURSOR, get_center(), "LC"); |
---|
988 | } |
---|
989 | #endif // DEBUG |
---|
990 | if (sroot->get_show_constraints() & SEC_LOOP) paint_constraints(device); |
---|
991 | } |
---|
992 | |
---|
993 | // ------------------------------------------------------------ |
---|
994 | // Paint the whole structure (starting with SEC_root) |
---|
995 | |
---|
996 | GB_ERROR SEC_root::paint(AW_device *device) { |
---|
997 | SEC_loop *rootLoop = get_root_loop(); |
---|
998 | sec_assert(rootLoop); |
---|
999 | clear_announced_positions(); // reset positions next to cursor |
---|
1000 | |
---|
1001 | const BI_helix *helix = get_helixDef(); |
---|
1002 | sec_assert(helix); |
---|
1003 | |
---|
1004 | GB_ERROR error = helix->get_error(); |
---|
1005 | |
---|
1006 | if (!error) { |
---|
1007 | sec_assert(SEC_GC_FIRST_FONT == 0); |
---|
1008 | // @@@ font group should be built at startup and after each font change (no need to do on every paint!) |
---|
1009 | font_group.unregisterAll(); |
---|
1010 | for (int gc = SEC_GC_FIRST_FONT; gc <= SEC_GC_LAST_FONT; ++gc) { |
---|
1011 | font_group.registerFont(device, gc, "ACGTU-."); |
---|
1012 | center_char[gc] = device->rtransform(Vector(-0.5*font_group.get_width(gc), 0.5*font_group.get_ascent(gc))); |
---|
1013 | } |
---|
1014 | |
---|
1015 | // calculate size for background painting |
---|
1016 | sec_assert(SEC_GC_FIRST_DATA == 0); |
---|
1017 | for (int gc = SEC_GC_FIRST_DATA; gc <= SEC_GC_LAST_DATA; ++gc) { |
---|
1018 | int maxSize = hypotenuse(font_group.get_width(gc), font_group.get_ascent(gc)); |
---|
1019 | bg_linewidth[gc] = maxSize*0.75; |
---|
1020 | |
---|
1021 | maxSize += 2; // add 2 extra pixels |
---|
1022 | charRadius[gc] = device->rtransform_size(maxSize) * 0.5; // was 0.75 |
---|
1023 | } |
---|
1024 | |
---|
1025 | skelThickWorld = device->rtransform_size(displayParams.skeleton_thickness); |
---|
1026 | bondThickWorld = device->rtransform_size(displayParams.bond_thickness); |
---|
1027 | |
---|
1028 | cacheBackgroundColor(); |
---|
1029 | |
---|
1030 | device->set_line_attributes(SEC_SKELE_HELIX, displayParams.skeleton_thickness, AW_SOLID); |
---|
1031 | device->set_line_attributes(SEC_SKELE_NHELIX, displayParams.skeleton_thickness, AW_SOLID); |
---|
1032 | device->set_line_attributes(SEC_SKELE_LOOP, displayParams.skeleton_thickness, AW_SOLID); |
---|
1033 | device->set_line_attributes(SEC_GC_BONDS, displayParams.bond_thickness, AW_SOLID); |
---|
1034 | |
---|
1035 | // mark the rootLoop with a box and print structure number |
---|
1036 | { |
---|
1037 | const Position& loop_center = rootLoop->get_center(); |
---|
1038 | const char *structId = db->structure()->name(); |
---|
1039 | |
---|
1040 | AW_click_cd cd(device, rootLoop->self(), -1); |
---|
1041 | |
---|
1042 | Vector center2corner(-1, -1); |
---|
1043 | center2corner.set_length(rootLoop->drawnSize()*0.33); |
---|
1044 | |
---|
1045 | Position upperleft_corner = loop_center+center2corner; |
---|
1046 | Vector diagonal = -2*center2corner; |
---|
1047 | |
---|
1048 | Position textPos(loop_center.xpos(), upperleft_corner.ypos()); |
---|
1049 | |
---|
1050 | device->box(SEC_GC_DEFAULT, AW::FillStyle::EMPTY, upperleft_corner, diagonal, AW_ALL_DEVICES_UNSCALED); |
---|
1051 | device->text(SEC_GC_DEFAULT, structId, textPos, 0.5); |
---|
1052 | } |
---|
1053 | |
---|
1054 | #if defined(CHECK_INTEGRITY) |
---|
1055 | check_integrity(CHECK_ALL); |
---|
1056 | #endif // CHECK_INTEGRITY |
---|
1057 | |
---|
1058 | rootLoop->paint(device); |
---|
1059 | |
---|
1060 | // paint ecoli positions: |
---|
1061 | if (displayParams.show_ecoli_pos) paintEcoliPositions(device); |
---|
1062 | |
---|
1063 | if (displayParams.show_helixNrs) { |
---|
1064 | paintHelixNumbers(device); |
---|
1065 | } |
---|
1066 | |
---|
1067 | #if defined(PAINT_ABSOLUTE_POSITION) |
---|
1068 | if (displayParams.show_debug) showSomeAbsolutePositions(device); |
---|
1069 | #endif // PAINT_ABSOLUTE_POSITION |
---|
1070 | |
---|
1071 | // paint cursor: |
---|
1072 | if (!drawnPositions->empty() && |
---|
1073 | (device->get_filter()&(AW_PRINTER|AW_PRINTER_EXT)) == 0) // don't print/xfig-export cursor |
---|
1074 | { |
---|
1075 | size_t abs1, abs2; |
---|
1076 | Position pos1, pos2; |
---|
1077 | size_t curAbs; |
---|
1078 | |
---|
1079 | if (displayParams.edit_rightward) { |
---|
1080 | pos1 = drawnPositions->drawn_before(cursorAbsPos, &abs1); |
---|
1081 | pos2 = drawnPositions->drawn_after(cursorAbsPos-1, &abs2); |
---|
1082 | curAbs = abs2; |
---|
1083 | } |
---|
1084 | else { |
---|
1085 | pos1 = drawnPositions->drawn_before(cursorAbsPos+1, &abs1); |
---|
1086 | pos2 = drawnPositions->drawn_after(cursorAbsPos, &abs2); |
---|
1087 | curAbs = abs1; |
---|
1088 | } |
---|
1089 | |
---|
1090 | AW_click_cd cd(device, 0, curAbs); |
---|
1091 | #if defined(DEBUG) && 1 |
---|
1092 | // draw a testline to see the baseline on that the cursor is positioned |
---|
1093 | device->set_line_attributes(SEC_GC_CURSOR, 1, AW_DASHED); |
---|
1094 | device->line(SEC_GC_CURSOR, pos1, pos2); |
---|
1095 | #endif |
---|
1096 | |
---|
1097 | Position mid = centroid(pos1, pos2); |
---|
1098 | Vector v(pos1, pos2); |
---|
1099 | { |
---|
1100 | Vector v_drawn = device->transform(v); |
---|
1101 | double drawn_length = v_drawn.length(); |
---|
1102 | |
---|
1103 | sec_assert(drawn_length>0.0); |
---|
1104 | |
---|
1105 | double cursor_size = 1.3 * max(font_group.get_max_width(), font_group.get_max_ascent()); // 30% bigger than max font size |
---|
1106 | double stretch = cursor_size*0.5/drawn_length; // stretch cursor (half fontsize in each direction) |
---|
1107 | |
---|
1108 | v.rotate90deg() *= stretch; |
---|
1109 | } |
---|
1110 | |
---|
1111 | LineVector cursor(mid+v, mid-v); |
---|
1112 | device->set_line_attributes(SEC_GC_CURSOR, 3, AW_SOLID); |
---|
1113 | device->line(SEC_GC_CURSOR, cursor); |
---|
1114 | set_last_drawed_cursor_position(cursor); |
---|
1115 | |
---|
1116 | LineVector cursor_dir(cursor.head(), displayParams.edit_rightward ? v.rotate270deg() : v.rotate90deg()); |
---|
1117 | device->line(SEC_GC_CURSOR, cursor_dir); |
---|
1118 | |
---|
1119 | |
---|
1120 | int cursor_gc = -1; |
---|
1121 | int disp_pos = -1; |
---|
1122 | |
---|
1123 | switch (displayParams.show_curpos) { |
---|
1124 | case SHOW_ABS_CURPOS: |
---|
1125 | cursor_gc = SEC_GC_CURSOR; |
---|
1126 | disp_pos = info2bio(curAbs); |
---|
1127 | break; |
---|
1128 | case SHOW_BASE_CURPOS: |
---|
1129 | cursor_gc = SEC_GC_DEFAULT; |
---|
1130 | disp_pos = host().get_base_position(curAbs+1); // show bases up to cursorpos (inclusive) |
---|
1131 | break; |
---|
1132 | case SHOW_ECOLI_CURPOS: { |
---|
1133 | cursor_gc = SEC_GC_ECOLI; |
---|
1134 | disp_pos = db->ecoli()->abs_2_rel(curAbs+1); // show ecoli base position (inclusive cursorpos) |
---|
1135 | break; |
---|
1136 | } |
---|
1137 | case SHOW_NO_CURPOS: |
---|
1138 | cursor_gc = -1; |
---|
1139 | break; |
---|
1140 | } |
---|
1141 | |
---|
1142 | if (cursor_gc >= 0) { |
---|
1143 | paintPosAnnotation(device, cursor_gc, curAbs, GBS_global_string("%u", disp_pos), true, true); |
---|
1144 | } |
---|
1145 | } |
---|
1146 | } |
---|
1147 | return error; |
---|
1148 | } |
---|
1149 | |
---|
1150 | void SEC_region::align_helix_strands(SEC_root *root, SEC_region *other_region) { |
---|
1151 | if (abspos_array) { |
---|
1152 | const BI_helix *helix = root->get_helixDef(); |
---|
1153 | if (helix && !helix->get_error()) { |
---|
1154 | SEC_region *reg[2] = { this, other_region }; |
---|
1155 | int incr[2] = { 1, -1 }; // this is iterated forward, other_region backward |
---|
1156 | int *absarr[2]; |
---|
1157 | int *new_absarr[2] = { NULp, NULp }; |
---|
1158 | |
---|
1159 | |
---|
1160 | for (int r = 0; r<2; ++r) { |
---|
1161 | absarr[r] = reg[r]->abspos_array; |
---|
1162 | } |
---|
1163 | |
---|
1164 | for (int write = 0; write < 2; ++write) { |
---|
1165 | int curr[2] = { 0, reg[1]->baseCount-1 }; |
---|
1166 | int last[2] = { reg[0]->baseCount-1, 0 }; |
---|
1167 | int newp[2] = { 0, 0 }; |
---|
1168 | |
---|
1169 | while (curr[0] <= last[0] && curr[1] >= last[1]) { |
---|
1170 | int abs[2]; |
---|
1171 | bool ispair[2]; |
---|
1172 | |
---|
1173 | for (int r = 0; r<2; ++r) { |
---|
1174 | abs[r] = absarr[r][curr[r]]; |
---|
1175 | ispair[r] = abs[r] >= 0 && (helix->pairtype(abs[r]) != HELIX_NONE); |
---|
1176 | } |
---|
1177 | |
---|
1178 | if (ispair[0] && ispair[1]) { |
---|
1179 | if (helix->opposite_position(abs[0]) != size_t(abs[1]) || |
---|
1180 | helix->opposite_position(abs[1]) != size_t(abs[0])) |
---|
1181 | { |
---|
1182 | GB_ERROR error = GBS_global_string("Helix '%s/%s' folded at wrong position. Please refold.", |
---|
1183 | helix->helixNr(abs[0]), helix->helixNr(abs[1])); |
---|
1184 | aw_message(error); |
---|
1185 | } |
---|
1186 | |
---|
1187 | for (int r = 0; r<2; ++r) { // fill up to align binding positions |
---|
1188 | while (newp[r]<newp[1-r]) { |
---|
1189 | if (write) { |
---|
1190 | new_absarr[r][newp[r]] = -1; |
---|
1191 | } |
---|
1192 | newp[r]++; |
---|
1193 | } |
---|
1194 | } |
---|
1195 | |
---|
1196 | sec_assert(newp[0] == newp[1]); |
---|
1197 | |
---|
1198 | for (int r = 0; r<2; ++r) { // copy binding positions |
---|
1199 | if (write) new_absarr[r][newp[r]] = abs[r]; |
---|
1200 | newp[r]++; curr[r] += incr[r]; |
---|
1201 | } |
---|
1202 | } |
---|
1203 | else { |
---|
1204 | bool collected = false; |
---|
1205 | for (int r = 0; r<2; ++r) { |
---|
1206 | if (abs[r] >= 0 && !ispair[r]) { // collect non-pairing bases |
---|
1207 | if (write) { |
---|
1208 | new_absarr[r][newp[r]] = abs[r]; |
---|
1209 | } |
---|
1210 | newp[r]++; curr[r] += incr[r]; |
---|
1211 | collected = true; |
---|
1212 | } |
---|
1213 | } |
---|
1214 | if (!collected) { |
---|
1215 | for (int r = 0; r<2; ++r) { |
---|
1216 | if (abs[r]<0) curr[r] += incr[r]; |
---|
1217 | } |
---|
1218 | } |
---|
1219 | } |
---|
1220 | } |
---|
1221 | |
---|
1222 | sec_assert(newp[0] == newp[1]); // alignment failed |
---|
1223 | |
---|
1224 | for (int r = 0; r<2; ++r) { |
---|
1225 | if (write) { |
---|
1226 | if (r == 1) { // reverse positions |
---|
1227 | int p2 = newp[1]-1; |
---|
1228 | int *arr = new_absarr[1]; |
---|
1229 | for (int p = 0; p<p2; ++p, --p2) { // LOOP_VECTORIZED[!<810] |
---|
1230 | swap(arr[p], arr[p2]); |
---|
1231 | } |
---|
1232 | } |
---|
1233 | |
---|
1234 | delete [] reg[r]->abspos_array; |
---|
1235 | reg[r]->abspos_array = new_absarr[r]; |
---|
1236 | #if defined(ASSERTION_USED) |
---|
1237 | reg[r]->abspos_array_size = newp[r]; |
---|
1238 | #endif // ASSERTION_USED |
---|
1239 | reg[r]->set_base_count(newp[r]); |
---|
1240 | } |
---|
1241 | else { |
---|
1242 | // allocate buffers for second pass |
---|
1243 | new_absarr[r] = new int[newp[r]]; |
---|
1244 | } |
---|
1245 | } |
---|
1246 | } |
---|
1247 | } |
---|
1248 | } |
---|
1249 | } |
---|
1250 | |
---|