source: branches/alilink/WINDOW/AW_print.cxx

Last change on this file was 18126, checked in by westram, 5 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.8 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : AW_print.cxx                                      //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "aw_root.hxx"
12#include "aw_common.hxx"
13#include "aw_xfont.hxx"
14#include "aw_rgb.hxx"
15
16#include <arb_msg.h>
17
18#include <map>
19#include <list>
20#include <vector>
21#include <string>
22
23using namespace AW;
24using namespace std;
25
26#define XFIG_DEFAULT_COLOR_COUNT 32  // colors "hardcoded" in fig format
27#define XFIG_USER_COLOR_COUNT    512 // restriction defined by fig format
28
29#define XFIG_USER_COLOR_FIRST XFIG_DEFAULT_COLOR_COUNT
30#define XFIG_USER_COLOR_LAST  (XFIG_USER_COLOR_FIRST+XFIG_USER_COLOR_COUNT-1)
31
32// -------------------
33//      Spoolable
34
35struct Spoolable {
36    virtual ~Spoolable() {}
37
38    virtual const char *text() const  = 0;
39    virtual const AW_rgb *rgb() const = 0;
40};
41
42class SpoolableString : public Spoolable {
43    string s;
44public:
45    SpoolableString(const char *txt) : s(txt) {}
46
47    const char *text() const OVERRIDE { return s.c_str(); }
48    const AW_rgb *rgb() const OVERRIDE { return NULp; }
49};
50class SpoolableColor : public Spoolable {
51    AW_rgb color;
52public:
53    SpoolableColor(const AW_rgb& color_) : color(color_) {}
54
55    const char *text() const OVERRIDE { return NULp; }
56    const AW_rgb *rgb() const OVERRIDE { return &color; }
57};
58
59typedef SmartPtr<Spoolable> SpoolablePtr;
60
61// ---------------------
62//      color mapping
63
64static AW_rgb figStdColors[32] = {
65    // color names were taken from xfig color chooser.
66    // corresponding color values were taken from X11 source code:
67    // https://cgit.freedesktop.org/xorg/xserver/tree/os/oscolor.c
68
69    0x000000, // Black [index 0]
70    0x0000ff, // Blue
71    0x00ff00, // Green
72    0x00ffff, // Cyan
73    0xff0000, // Red
74    0xff00ff, // Magenta
75    0xffff00, // Yellow
76    0xffffff, // White
77    0x00008b, // Blue4
78    0x0000cd, // Blue3
79    0x0000ee, // Blue2
80    0xadd8e6, // LtBlue
81    0x008b00, // Green4
82    0x00cd00, // Green3
83    0x00ee00, // Green2
84    0x008b8b, // Cyan4
85    0x00cdcd, // Cyan3
86    0x00eeee, // Cyan2
87    0x8b0000, // Red4
88    0xcd0000, // Red3
89    0xee0000, // Red2
90    0x8b008b, // Magenta4
91    0xcd00cd, // Magenta3
92    0xee00ee, // Magenta2
93    0x8b2323, // Brown4
94    0xcd3333, // Brown3
95    0xee3b3b, // Brown2
96    0x8b636c, // Pink4
97    0xcd919e, // Pink3
98    0xeea9b8, // Pink2
99    0xffc0cb, // Pink
100    0xffd700, // Gold [index 31]
101};
102
103class UsedColor {
104    int count; // how often color occurred (=weight)
105    int index; // xfig color index (-2 = not assigned; -1 .. 31 = fixed xfig color; 32..543 = user defined color)
106
107    static const int NOT_ASSIGNED = -2;
108
109public:
110    UsedColor() : count(0), index(NOT_ASSIGNED) {}
111    explicit UsedColor(int idx) : count(0), index(idx) {} // used to add figStdColors
112
113    void inc() { ++count; }
114    void addCount(int toAdd) { count += toAdd; }
115    int getCount() const { return count; }
116
117    bool isAssigned() const { return index != NOT_ASSIGNED; }
118    bool isStdColor() const { return index>=0 && index<XFIG_USER_COLOR_FIRST; }
119
120    void assign(int idx) {
121        aw_assert(!isAssigned());
122        aw_assert(idx>=XFIG_USER_COLOR_FIRST && idx<=XFIG_USER_COLOR_LAST); // only allow to assign user colors
123        index = idx;
124        aw_assert(isAssigned());
125    }
126
127    int getIndex() const { aw_assert(isAssigned()); return index; }
128};
129
130inline double colorDistSquare(const AW_rgb& col1, const AW_rgb& col2) {
131    AW_rgb_diff diff = AW_rgb_normalized(AW_rgb16(col1)) - AW_rgb_normalized(AW_rgb16(col2));
132    return diff.r()*diff.r() + diff.g()*diff.g() + diff.b()*diff.b();
133}
134
135class ColorMap {
136    // maps RGB values to xfig color indices
137    typedef map<AW_rgb,UsedColor> ColMap;
138    typedef map<AW_rgb,AW_rgb>    Remap; // colors -> replaced by different color
139
140    ColMap color;
141    Remap  replaced;
142
143    AW_rgb findCheapestReplaceableColor(AW_rgb *replaceBy) {
144        AW_rgb cheapest;
145        double  penalty = numeric_limits<float>::max();
146
147        for (ColMap::const_iterator i = color.begin(); i != color.end(); ++i) {
148            const AW_rgb& col1 = i->first;
149            if (i->second.isStdColor()) continue; // do not attempt to replace standard colors
150
151            const int amount = i->second.getCount();
152            aw_assert(amount>0); // otherwise color should not appear in table
153
154            for (ColMap::const_iterator j = color.begin(); j != color.end(); ++j) {
155                const AW_rgb& col2 = j->first;
156
157                if (&col1 != &col2) {
158                    double currPenalty = amount*colorDistSquare(col1, col2);
159#if defined(DEBUG) && 0
160                    fprintf(stderr, "replace %s ", AW_rgb16(col1).ascii());
161                    fprintf(stderr, "by %s (amount=%i) -> penalty = %f\n", AW_rgb16(col2).ascii(), amount, currPenalty);
162#endif
163
164                    if (currPenalty<penalty) {
165                        penalty    = currPenalty;
166                        cheapest   = col1;
167                        *replaceBy = col2;
168                    }
169                }
170            }
171        }
172
173        aw_assert(penalty<numeric_limits<float>::max()); // no color found
174        aw_assert(penalty>0); // should not occur!
175        return cheapest;
176    }
177
178public:
179    ColorMap() {
180        // add fig standard colors to ColMap [=color indices 0..31]
181        for (int i = 0; i<XFIG_DEFAULT_COLOR_COUNT; ++i) {
182            color[figStdColors[i]] = UsedColor(i);
183        }
184    }
185
186    void add(AW_rgb rgb) { color[rgb].inc(); }
187
188    bool operator()(const AW_rgb& c1, const AW_rgb& c2) const {
189        // operator to sort colors (often used colors first; strict order)
190        ColMap::const_iterator f1 = color.find(c1);
191        ColMap::const_iterator f2 = color.find(c2);
192
193        aw_assert(f1 != color.end());
194        aw_assert(f2 != color.end());
195
196        int cmp = f1->second.getCount() - f2->second.getCount();
197        if (!cmp) cmp = c1-c2; // define strict order using rgb value
198        return cmp>0;
199    }
200
201    void mapColors(size_t allowed) {
202        size_t usedColors = color.size(); // with std-colors
203
204        allowed += XFIG_DEFAULT_COLOR_COUNT;
205
206        // reduce colors if too many colors in map:
207        while (usedColors>allowed) {
208            AW_rgb replaceBy;
209            AW_rgb cheapest = findCheapestReplaceableColor(&replaceBy);
210
211            aw_assert(!color[cheapest].isStdColor());
212
213            // @@@ if amount of 'cheapest' and 'replaceBy' is (nearly) the same -> create mixed color and replace both?
214
215            replaced[cheapest] = replaceBy; // store color replacement
216            color[replaceBy].addCount(color[cheapest].getCount()); // add occurrences of removed color to replacement color
217
218            IF_ASSERTION_USED(size_t erased = )color.erase(cheapest);
219            aw_assert(erased == 1);
220
221            --usedColors;
222        }
223
224        aw_assert(color.size() <= allowed); // failed to reduce colors
225
226        vector<AW_rgb> sortedColors;
227        for (ColMap::iterator i = color.begin(); i != color.end(); ++i) {
228            sortedColors.push_back(i->first);
229        }
230        sort(sortedColors.begin(), sortedColors.end(), *this);
231
232        // stupid mapping:
233        int nextIdx = XFIG_USER_COLOR_FIRST;
234        for (ColMap::iterator i = color.begin(); i != color.end(); ++i) {
235            UsedColor& used = i->second;
236            if (!used.isStdColor()) {
237                used.assign(nextIdx++);
238            }
239        }
240    }
241
242    void printUserColorDefinitions(FILE *xout) {
243        const AW_rgb *used[XFIG_USER_COLOR_LAST+1];
244
245        for (int i = 0; i<=XFIG_USER_COLOR_LAST; ++i) {
246            used[i] = NULp;
247        }
248
249        for (ColMap::const_iterator i = color.begin(); i != color.end(); ++i) {
250#if defined(ASSERTION_USED)
251            const AW_rgb& rgb = i->first;
252            aw_assert(rgb != AW_NO_COLOR);
253#endif
254            const UsedColor& ucol = i->second;
255            aw_assert(ucol.isAssigned());
256
257            int idx   = ucol.getIndex();
258            aw_assert(!used[idx]);
259            used[idx] = &(i->first);
260        }
261
262        // print custom color definitions sorted by index:
263        for (int i = XFIG_USER_COLOR_FIRST; i<=XFIG_USER_COLOR_LAST; ++i) {
264            if (used[i]) {
265                fprintf(xout, "0 %d #%06lx\n", i, *used[i]);
266            }
267        }
268    }
269
270    int rgb2index(AW_rgb rgb) const {
271        ColMap::const_iterator found = color.find(rgb);
272        while (found == color.end()) {
273            Remap::const_iterator remapped = replaced.find(rgb);
274            aw_assert(remapped != replaced.end());
275
276            rgb   = remapped->second;
277            found = color.find(rgb);
278        }
279        return found->second.getIndex();
280    }
281};
282
283// -----------------
284//      SPOOLER
285
286class SPOOLER {
287    // SPOOLER delays output of xfig objects:
288    // 1. collect object code (while tracing information about used colors)
289    // 2. generate color table (probably reduced)
290    // 3. print object code (and remap colors)
291
292    typedef list<SpoolablePtr> Spoolables;
293
294    Spoolables objs;
295
296    void initColors(ColorMap& colmap) {
297        for (Spoolables::const_iterator i = objs.begin(); i != objs.end(); ++i) {
298            const AW_rgb *rgb = (*i)->rgb();
299            if (rgb) colmap.add(*rgb);
300        }
301    }
302
303public:
304    SPOOLER() {}
305
306    void put(const char *str) { objs.push_back(new SpoolableString(str)); }
307    void putColor(AW_rgb rgb) { objs.push_back(new SpoolableColor(rgb)); }
308    void putDefaultColor() { put("-1 "); }
309
310    void spool(FILE *xout) {
311        ColorMap colmap;
312
313        initColors(colmap);
314        colmap.mapColors(XFIG_USER_COLOR_COUNT);
315        colmap.printUserColorDefinitions(xout);
316
317        while (!objs.empty()) {
318            SpoolablePtr next = objs.front();
319            objs.pop_front();
320
321            const char *txt = next->text();
322            if (txt) {
323                fputs(txt, xout);
324            }
325            else {
326                const AW_rgb *rgb = next->rgb();
327                arb_assert(rgb);
328
329                int idx    = colmap.rgb2index(*rgb);
330                fprintf(xout, "%d ", idx);
331            }
332        }
333    }
334
335};
336
337// -------------------------
338//      AW_device_print
339
340const double dpi_screen2printer = double(DPI_PRINTER)/DPI_SCREEN;
341
342inline double screen2printer(double val) { return val*dpi_screen2printer; }
343inline int print_pos(AW_pos screen_pos) { return AW_INT(screen2printer(screen_pos)); }
344
345AW_DEVICE_TYPE AW_device_print::type() { return AW_DEVICE_PRINTER; }
346
347// Note: parameters to spoolf HAVE TO be wrapped in "()"!
348#define spoolf(format)      spooler->put(GBS_global_string format)
349#define spools(str)         spooler->put(str)
350#define spoolColor(rgb)     color_mode ? spooler->putColor(rgb) : spooler->putDefaultColor()
351#define spoolDefaultColor() spooler->putDefaultColor()
352
353bool AW_device_print::line_impl(int gc, const LineVector& Line, AW_bitset filteri) {
354    bool drawflag = false;
355    if (filteri & filter) {
356        LineVector transLine = transform(Line);
357        LineVector clippedLine;
358        drawflag = clip(transLine, clippedLine);
359
360        if (drawflag) {
361            const AW_GC *gcm        = get_common()->map_gc(gc);
362            int          line_width = gcm->get_line_width();
363
364            int    line_mode = 0;
365            double gap_ratio = 0.0;
366            switch (gcm->get_line_style()) {
367                case AW_SOLID: /* use defaults from above*/ break;
368                case AW_DASHED:  line_mode = 1; gap_ratio = 4.0; break;
369                case AW_DOTTED:  line_mode = 2; gap_ratio = 2.0; break;
370            }
371
372            aw_assert(xfig); // file has to be good!
373
374            // type, subtype, style, thickness, pen_color,
375            // fill_color(new), depth, pen_style, area_fill, style_val,
376            // join_style(new), cap_style(new), radius, forward_arrow,
377            // backward_arrow, npoints
378            spoolf(("2 1 %d %d ",
379                    line_mode,
380                    AW_INT(line_width)));
381            spoolColor(gcm->get_last_fg_color());
382            spoolf(("0 0 0 0 %5.3f 0 1 0 0 0 2\n\t%d %d %d %d\n",
383                    gap_ratio,
384                    print_pos(clippedLine.xpos()),
385                    print_pos(clippedLine.ypos()),
386                    print_pos(clippedLine.head().xpos()),
387                    print_pos(clippedLine.head().ypos())));
388        }
389    }
390    return drawflag;
391}
392
393bool AW_device_print::invisible_impl(const AW::Position& pos, AW_bitset filteri) {
394    bool drawflag = false;
395    if (filteri & filter) {
396        Position trans = transform(pos);
397
398        drawflag = !is_outside_clip(trans);
399        if (drawflag) {
400            spoolf(("2 1 0 1 7 7 50 -1 -1 0.000 0 0 -1 0 0 1\n\t%d %d\n",
401                    print_pos(trans.xpos()),
402                    print_pos(trans.ypos())));
403        }
404    }
405    return drawflag;
406}
407
408void AW_device_print::draw_text(int gc, const char *textBuffer, size_t textStart, size_t textLen, const AW::Position& pos) {
409    const AW_GC *gcm = get_common()->map_gc(gc);
410
411    AW::Position POS(transform(pos));
412
413    char *pstr                                = strdup(textBuffer+textStart); // @@@ only copy textLen characters!
414    if (textLen < strlen(pstr)) pstr[textLen] = 0; // @@@ caller asserts textLen <= strlen(pstr)
415    else  textLen                             = strlen(pstr);
416
417    size_t i;
418    for (i=0; i<textLen; i++) {
419        if (pstr[i] < ' ') pstr[i] = '?';
420    }
421
422    int fontnr           = AW_font_2_xfig(gcm->get_fontnr());
423    if (fontnr<0) fontnr = - fontnr;
424    if (textBuffer[0]) { // @@@ incorrect or useless?
425        // 4=string 0=left color depth penstyle font font_size angle
426        // font_flags height length x y string
427        // (font/fontsize and color/depth have been switched from format 2.1 to 3.2)
428
429        SizedCstr sstr(textBuffer); // @@@ shouldn't this use only part of textBuffer??? maybe 'pstr'?
430
431        spools("4 0 ");
432        spoolColor(gcm->get_last_fg_color());
433        spoolf(("0 0 %d %d 0.000 4 %d %d %d %d ",
434                fontnr,
435                gcm->get_fontsize(),
436                (int)gcm->get_font_limits().get_height(),
437                gcm->get_string_size(sstr),
438                print_pos(POS.xpos()),
439                print_pos(POS.ypos())));
440
441        spoolf(("%s\\001\n", pstr));
442    }
443    free(pstr);
444}
445
446GB_ERROR AW_device_print::open(const char *path) {
447    if (xfig) return "You cannot reopen a device";
448
449    xfig = fopen(path, "w");
450    if (!xfig) return GB_IO_error("writing", path);
451
452    fprintf(xfig,
453            "#FIG 3.2\n"   // version
454            "Landscape\n"  // "Portrait"
455            "Center\n"     // "Flush Left"
456            "Metric\n"     // "Inches"
457            "A4\n"         // papersize
458            "100.0\n"      // export&print magnification %
459            "Single\n"     // Single/Multiple Pages
460            "-3\n"         // background = transparent for gif export
461            "%i 2\n"       // dpi, 2  = origin in upper left corner
462            , DPI_PRINTER);
463
464    spooler = new SPOOLER;
465
466    return NULp;
467}
468
469int AW_common::find_data_color_idx(AW_rgb color) const {
470    for (int i=0; i<data_colors_size; i++) {
471        if (color == data_colors[i]) {
472            return i;
473        }
474    }
475    return -1;
476}
477
478int AW_device_print::find_color_idx(AW_rgb color) {
479    int idx = -1;
480    if (color_mode) {
481        idx = get_common()->find_data_color_idx(color);
482        if (idx >= 0) idx += XFIG_USER_COLOR_FIRST;
483    }
484    return idx;
485}
486
487void AW_device_print::close() {
488    arb_assert(correlated(spooler, xfig));
489
490    if (spooler) {
491        spooler->spool(xfig);
492
493        delete spooler;
494        spooler = NULp;
495
496        fclose(xfig);
497        xfig = NULp;
498    }
499}
500
501static bool AW_draw_string_on_printer(AW_device *device, int gc, const char *textBuffer, size_t textStart, size_t textLen, const AW::Position& pos, AW_CL /*cduser*/) {
502    DOWNCAST(AW_device_print*, device)->draw_text(gc, textBuffer, textStart, textLen, pos);
503    return true;
504}
505bool AW_device_print::text_impl(int gc, const SizedCstr& cstr, const AW::Position& pos, AW_pos alignment, AW_bitset filteri) {
506    return text_overlay(gc, cstr, pos, alignment, filteri, AW_draw_string_on_printer);
507}
508
509bool AW_device_print::box_impl(int gc, AW::FillStyle filled, const Rectangle& rect, AW_bitset filteri) {
510    bool drawflag = false;
511    if (filter & filteri) {
512        if (filled.somehow()) {
513            Position q[4];
514            q[0] = rect.upper_left_corner();
515            q[1] = rect.upper_right_corner();
516            q[2] = rect.lower_right_corner();
517            q[3] = rect.lower_left_corner();
518
519            drawflag = polygon(gc, filled, 4, q, filteri);
520        }
521        else {
522            drawflag = generic_box(gc, rect, filteri);
523        }
524    }
525    return drawflag;
526}
527
528AW_device::Fill_Style AW_device_print::detectFillstyleForGreylevel(int gc, AW::FillStyle filled) { // @@@ directly pass greylevel (or AW_GC?)
529    // @@@ DRY code here vs setFillstyleForGreylevel
530
531    switch (filled.get_style()) {
532        case AW::FillStyle::SOLID: return FS_SOLID;
533        case AW::FillStyle::EMPTY: return FS_EMPTY;
534
535        case AW::FillStyle::SHADED:
536        case AW::FillStyle::SHADED_WITH_BORDER:
537            break; // detect using greylevel
538    }
539
540    AW_grey_level greylevel = get_grey_level(gc);
541
542    static const float PART = 1.0/22; // exact for color_mode==false
543
544    if (greylevel<PART) return FS_EMPTY;
545    if (greylevel>(1.0-PART)) return FS_SOLID;
546
547    return FS_GREY;
548}
549
550int AW_device_print::calcAreaFill(AW_device::Fill_Style fillStyle, AW_grey_level grey_level) {
551    int area_fill = -1;
552    if (fillStyle == FS_SOLID) { // @@@ use switch here
553        area_fill = 20;
554    }
555    else if (fillStyle != FS_EMPTY) {
556        aw_assert(grey_level>=0.0 && grey_level<=1.0);
557        if (color_mode) {
558            area_fill = AW_INT(40-20*grey_level); // 20 = full saturation; 40 = white
559            aw_assert(area_fill != 40); // @@@ should better use fillStyle==FS_EMPTY in that case
560        }
561        else {
562            area_fill = AW_INT(20*grey_level); // 1=light 19=dark 20=solid
563            aw_assert(area_fill != 0); // @@@ should better use fillStyle==FS_EMPTY in that case
564        }
565        aw_assert(area_fill != 20); // @@@ should better use fillStyle==FS_SOLID in that case
566    }
567    return area_fill;
568}
569
570bool AW_device_print::circle_impl(int gc, AW::FillStyle filled, const Position& center, const AW::Vector& radius, AW_bitset filteri) {
571    bool drawflag = false;
572    if (filteri & filter) {
573        aw_assert(radius.x()>0 && radius.y()>0);
574        Rectangle Box(center-radius, center+radius);
575        Rectangle screen_box = transform(Box);
576        Rectangle clipped_box;
577        drawflag          = box_clip(screen_box, clipped_box);
578        bool half_visible = (clipped_box.surface()*2) > screen_box.surface();
579
580        drawflag = drawflag && half_visible;
581        // @@@ correct behavior would be to draw an arc if only partly visible
582
583        if (drawflag) {
584            const AW_GC *gcm = get_common()->map_gc(gc);
585
586            // force into clipped_box (hack):
587            Position Center        = clipped_box.centroid();
588            Vector   screen_radius = clipped_box.diagonal()/2;
589
590            int cx = print_pos(Center.xpos());
591            int cy = print_pos(Center.ypos());
592            int rx = print_pos(screen_radius.x());
593            int ry = print_pos(screen_radius.y());
594
595            {
596                int subtype = (rx == ry) ? 3 : 1; // 3(circle) 1(ellipse)
597                subtype     = 3; // @@@ remove after refactoring
598                spoolf(("1 %d  ", subtype));  // type + subtype:
599            }
600
601            {
602                int line_width = gcm->get_line_width();
603                spoolf(("%d %d ", AW_SOLID, line_width));   // line_style + line_width
604
605                {
606                    AW_rgb     color     = gcm->get_last_fg_color();
607                    Fill_Style fillStyle = detectFillstyleForGreylevel(gc, filled);
608                    int        area_fill = calcAreaFill(fillStyle, gcm->get_grey_level());
609
610                    spoolColor(color); // pen_color
611                    if (area_fill == -1) {
612                        spoolDefaultColor(); // fill color
613                    }
614                    else {
615                        spoolColor(color); // fill color
616                    }
617                    spoolf(("0 0 %d ", area_fill)); // depth + pen_style + area_fill
618                }
619                spoolf(("0.000 1 0.0000 "));                // style_val + direction + angle (x-axis)
620            }
621
622            spoolf(("%d %d ", cx, cy)); // center
623            spoolf(("%d %d ", rx, ry)); // radius
624            spoolf(("%d %d ", cx, cy)); // start
625            spoolf(("%d %d\n", print_pos(Center.xpos()+screen_radius.x()), cy)); // end
626        }
627    }
628    return drawflag;
629}
630
631bool AW_device_print::arc_impl(int gc, AW::FillStyle filled, const AW::Position& center, const AW::Vector& radius, int start_degrees, int arc_degrees, AW_bitset filteri) {
632    bool drawflag = false;
633    if (filteri && filter) {
634        aw_assert(radius.x()>0 && radius.y()>0);
635        Rectangle Box(center-radius, center+radius);
636        Rectangle screen_box = transform(Box);
637        Rectangle clipped_box;
638        drawflag          = box_clip(screen_box, clipped_box);
639        bool half_visible = (clipped_box.surface()*2) > screen_box.surface();
640
641        drawflag = drawflag && half_visible;
642        // @@@ correct behavior would be to draw an arc if only partly visible
643
644        UNCOVERED(); // @@@ AW_device_print::arc_impl not triggered from test code.
645                     // Uses via method arc():
646                     // - from SECEDIT to draw '~'-bonds (may be saved to xfig)
647                     // - from EDIT4 to mark protein codon triples (NEVER saved to xfig)
648                     // AW_device_Xm version is used indirectly via method circle_impl().
649
650        if (drawflag) {
651            const AW_GC *gcm = get_common()->map_gc(gc);
652
653            // force into clipped_box (hack):
654            Position Center        = clipped_box.centroid();
655            Vector   screen_radius = clipped_box.diagonal()/2;
656
657            int cx = print_pos(Center.xpos());
658            int cy = print_pos(Center.ypos());
659            int rx = print_pos(screen_radius.x());
660            int ry = print_pos(screen_radius.y());
661
662            bool use_spline = (rx != ry); // draw interpolated spline for ellipsoid arcs
663
664            spools(use_spline ? "3 4 " : "5 1 ");  // type + subtype:
665
666            {
667                int line_width = gcm->get_line_width();
668                spoolf(("%d %d ", AW_SOLID, line_width));   // line_style + line_width
669
670                {
671                    AW_rgb     color     = gcm->get_last_fg_color();
672                    Fill_Style fillStyle = detectFillstyleForGreylevel(gc, filled);
673                    int        area_fill = calcAreaFill(fillStyle, gcm->get_grey_level());
674
675                    spoolColor(color); // pen_color
676                    if (area_fill == -1) {
677                        spoolDefaultColor(); // fill color
678                    }
679                    else {
680                        spoolColor(color); // fill color
681                    }
682                    spoolf(("0 0 %d ", area_fill)); // depth + pen_style + area_fill
683#if defined(UNIT_TESTS)
684                    if (area_fill != -1) { UNCOVERED(); } // @@@ filled arcs not tested
685                    else                 { UNCOVERED(); } // @@@ unfilled arcs not tested
686#endif
687                }
688                spools("0.000 1 ");                         // style_val + cap_style
689                if (!use_spline) spools("1 ");              // direction
690                spools("0 0 ");                             // 2 arrows
691            }
692
693            Angle a0(Angle::deg2rad*start_degrees);
694            Angle a1(Angle::deg2rad*(start_degrees+arc_degrees));
695
696            if (use_spline) {
697                const int MAX_ANGLE_STEP = 45; // degrees
698
699                int steps = (abs(arc_degrees)-1)/MAX_ANGLE_STEP+1;
700                Angle step(Angle::deg2rad*double(arc_degrees)/steps);
701
702                spoolf(("%d\n\t", steps+1)); // npoints
703
704                double rmax, x_factor, y_factor;
705                if (rx>ry) {
706                    rmax     = rx;
707                    x_factor = 1.0;
708                    y_factor = double(ry)/rx;
709                }
710                else {
711                    rmax     = ry;
712                    x_factor = double(rx)/ry;
713                    y_factor = 1.0;
714                }
715
716                for (int n = 0; n <= steps; ++n) {
717                    Vector   toCircle  = a0.normal()*rmax;
718                    Vector   toEllipse(toCircle.x()*x_factor, toCircle.y()*y_factor);
719                    Position onEllipse = Center+toEllipse;
720
721                    int x = print_pos(onEllipse.xpos());
722                    int y = print_pos(onEllipse.ypos());
723
724                    spoolf((" %d %d", x, y));
725
726                    if (n<steps) {
727                        if (n == steps-1) a0 = a1;
728                        else              a0 += step;
729                    }
730                }
731                spools("\n\t");
732                for (int n = 0; n <= steps; ++n) {
733                    // -1 = interpolate; 0 = discontinuity; 1 = approximate
734                    spoolf((" %d", (n == 0 || n == steps) ? 0 : -1));
735                }
736                spools("\n");
737            }
738            else {
739                spoolf(("%d %d ", cx, cy)); // center
740
741                Angle am(Angle::deg2rad*(start_degrees+arc_degrees*0.5));
742
743                double   r  = screen_radius.x();
744                Position p0 = Center+a0.normal()*r;
745                Position pm = Center+am.normal()*r;
746                Position p1 = Center+a1.normal()*r;
747
748                spoolf(("%d %d ",  print_pos(p0.xpos()), print_pos(p0.ypos())));
749                spoolf(("%d %d ",  print_pos(pm.xpos()), print_pos(pm.ypos())));
750                spoolf(("%d %d\n", print_pos(p1.xpos()), print_pos(p1.ypos())));
751            }
752        }
753    }
754    return drawflag;
755}
756
757bool AW_device_print::polygon_impl(int gc, AW::FillStyle filled, int npos, const Position *pos, AW_bitset filteri) {
758    bool drawflag = false;
759    if (filter & filteri) {
760        drawflag = generic_polygon(gc, npos, pos, filteri);
761        if (drawflag) { // line visible -> area fill needed
762            const AW_GC *gcm = get_common()->map_gc(gc);
763
764            int line_width = gcm->get_line_width();
765
766            spools("2 3 ");
767            spoolf(("%d %d ", AW_SOLID, line_width));
768
769            {
770                AW_rgb     color     = gcm->get_last_fg_color();
771                Fill_Style fillStyle = detectFillstyleForGreylevel(gc, filled);
772                int        area_fill = calcAreaFill(fillStyle, gcm->get_grey_level());
773
774                spoolColor(color); // pen_color
775                if (area_fill == -1) {
776                    spoolDefaultColor(); // fill color
777                }
778                else {
779                    spoolColor(color); // fill color
780                }
781                spoolf(("0 0 %d ", area_fill));             // depth + pen_style + area_fill
782            }
783
784            spools("0.000 0 0 -1 0 0 ");
785
786            spoolf(("%d\n", npos+1));
787
788            // @@@ method used here for clipping leads to wrong results,
789            // since group border (drawn by generic_polygon() above) is clipped correctly,
790            // but filled content is clipped different.
791            //
792            // fix: clip the whole polygon before drawing border
793
794            for (int i=0; i <= npos; i++) {
795                int j = i == npos ? 0 : i; // print pos[0] after pos[n-1]
796
797                Position transPos = transform(pos[j]);
798                Position clippedPos;
799                ASSERT_RESULT(bool, true, force_into_clipbox(transPos, clippedPos));
800                spoolf(("   %d %d\n", print_pos(clippedPos.xpos()), print_pos(clippedPos.ypos())));
801            }
802        }
803    }
804    return drawflag;
805}
Note: See TracBrowser for help on using the repository browser.