source: branches/profile/SECEDIT/SEC_paint.cxx

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