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