| 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 | |
|---|
| 14 | #include <arb_msg.h> |
|---|
| 15 | |
|---|
| 16 | using namespace AW; |
|---|
| 17 | |
|---|
| 18 | const double dpi_screen2printer = double(DPI_PRINTER)/DPI_SCREEN; |
|---|
| 19 | |
|---|
| 20 | inline double screen2printer(double val) { return val*dpi_screen2printer; } |
|---|
| 21 | inline int print_pos(AW_pos screen_pos) { return AW_INT(screen2printer(screen_pos)); } |
|---|
| 22 | |
|---|
| 23 | AW_DEVICE_TYPE AW_device_print::type() { return AW_DEVICE_PRINTER; } |
|---|
| 24 | |
|---|
| 25 | bool AW_device_print::line_impl(int gc, const LineVector& Line, AW_bitset filteri) { |
|---|
| 26 | bool drawflag = false; |
|---|
| 27 | if (filteri & filter) { |
|---|
| 28 | LineVector transLine = transform(Line); |
|---|
| 29 | LineVector clippedLine; |
|---|
| 30 | drawflag = clip(transLine, clippedLine); |
|---|
| 31 | |
|---|
| 32 | if (drawflag) { |
|---|
| 33 | const AW_GC *gcm = get_common()->map_gc(gc); |
|---|
| 34 | int line_width = gcm->get_line_width(); |
|---|
| 35 | |
|---|
| 36 | int line_mode = 0; |
|---|
| 37 | double gap_ratio = 0.0; |
|---|
| 38 | switch (gcm->get_line_style()) { |
|---|
| 39 | case AW_SOLID: /* use defaults from above*/ break; |
|---|
| 40 | case AW_DASHED: line_mode = 1; gap_ratio = 4.0; break; |
|---|
| 41 | case AW_DOTTED: line_mode = 2; gap_ratio = 2.0; break; |
|---|
| 42 | } |
|---|
| 43 | |
|---|
| 44 | aw_assert(out); // file has to be good! |
|---|
| 45 | |
|---|
| 46 | // type, subtype, style, thickness, pen_color, |
|---|
| 47 | // fill_color(new), depth, pen_style, area_fill, style_val, |
|---|
| 48 | // join_style(new), cap_style(new), radius, forward_arrow, |
|---|
| 49 | // backward_arrow, npoints |
|---|
| 50 | fprintf(out, "2 1 %d %d %d 0 0 0 0 %5.3f 0 1 0 0 0 2\n\t%d %d %d %d\n", |
|---|
| 51 | line_mode, |
|---|
| 52 | AW_INT(line_width), |
|---|
| 53 | find_color_idx(gcm->get_last_fg_color()), |
|---|
| 54 | gap_ratio, |
|---|
| 55 | print_pos(clippedLine.xpos()), |
|---|
| 56 | print_pos(clippedLine.ypos()), |
|---|
| 57 | print_pos(clippedLine.head().xpos()), |
|---|
| 58 | print_pos(clippedLine.head().ypos())); |
|---|
| 59 | } |
|---|
| 60 | } |
|---|
| 61 | return drawflag; |
|---|
| 62 | } |
|---|
| 63 | |
|---|
| 64 | bool AW_device_print::invisible_impl(const AW::Position& pos, AW_bitset filteri) { |
|---|
| 65 | bool drawflag = false; |
|---|
| 66 | if (filteri & filter) { |
|---|
| 67 | Position trans = transform(pos); |
|---|
| 68 | |
|---|
| 69 | drawflag = !is_outside_clip(trans); |
|---|
| 70 | if (drawflag) { |
|---|
| 71 | fprintf(out, "2 1 0 1 7 7 50 -1 -1 0.000 0 0 -1 0 0 1\n\t%d %d\n", |
|---|
| 72 | print_pos(trans.xpos()), |
|---|
| 73 | print_pos(trans.ypos())); |
|---|
| 74 | } |
|---|
| 75 | } |
|---|
| 76 | return drawflag; |
|---|
| 77 | } |
|---|
| 78 | |
|---|
| 79 | static bool AW_draw_string_on_printer(AW_device *devicei, int gc, const char *str, size_t /* opt_strlen */, size_t start, size_t size, |
|---|
| 80 | AW_pos x, AW_pos y, AW_pos /*opt_ascent*/, AW_pos /*opt_descent*/, |
|---|
| 81 | AW_CL /*cduser*/) |
|---|
| 82 | { |
|---|
| 83 | AW_pos X, Y; |
|---|
| 84 | AW_device_print *device = (AW_device_print *)devicei; |
|---|
| 85 | AW_common *common = device->get_common(); |
|---|
| 86 | const AW_GC *gcm = common->map_gc(gc); |
|---|
| 87 | |
|---|
| 88 | device->transform(x, y, X, Y); |
|---|
| 89 | char *pstr = strdup(str+start); |
|---|
| 90 | if (size < strlen(pstr)) pstr[size] = 0; |
|---|
| 91 | else size = strlen(pstr); |
|---|
| 92 | size_t i; |
|---|
| 93 | for (i=0; i<size; i++) { |
|---|
| 94 | if (pstr[i] < ' ') pstr[i] = '?'; |
|---|
| 95 | } |
|---|
| 96 | int fontnr = AW_font_2_xfig(gcm->get_fontnr()); |
|---|
| 97 | if (fontnr<0) fontnr = - fontnr; |
|---|
| 98 | if (str[0]) { |
|---|
| 99 | // 4=string 0=left color depth penstyle font font_size angle |
|---|
| 100 | // font_flags height length x y string |
|---|
| 101 | // (font/fontsize and color/depth have been switched from format |
|---|
| 102 | // 2.1 to 3.2 |
|---|
| 103 | fprintf(device->get_FILE(), "4 0 %d 0 0 %d %d 0.000 4 %d %d %d %d ", |
|---|
| 104 | device->find_color_idx(gcm->get_last_fg_color()), |
|---|
| 105 | fontnr, |
|---|
| 106 | gcm->get_fontsize(), |
|---|
| 107 | (int)gcm->get_font_limits().height, |
|---|
| 108 | device->get_string_size(gc, str, 0), |
|---|
| 109 | print_pos(X), |
|---|
| 110 | print_pos(Y)); |
|---|
| 111 | char *p; |
|---|
| 112 | for (p = pstr; *p; p++) { |
|---|
| 113 | if (*p >= 32) putc(*p, device->get_FILE()); |
|---|
| 114 | } |
|---|
| 115 | fprintf(device->get_FILE(), "\\001\n"); |
|---|
| 116 | } |
|---|
| 117 | free(pstr); |
|---|
| 118 | return true; |
|---|
| 119 | } |
|---|
| 120 | |
|---|
| 121 | #define DATA_COLOR_OFFSET 32 |
|---|
| 122 | |
|---|
| 123 | GB_ERROR AW_device_print::open(const char *path) { |
|---|
| 124 | if (out) return "You cannot reopen a device"; |
|---|
| 125 | |
|---|
| 126 | out = fopen(path, "w"); |
|---|
| 127 | if (!out) return GB_IO_error("writing", path); |
|---|
| 128 | |
|---|
| 129 | fprintf(out, |
|---|
| 130 | "#FIG 3.2\n" // version |
|---|
| 131 | "Landscape\n" // "Portrait" |
|---|
| 132 | "Center\n" // "Flush Left" |
|---|
| 133 | "Metric\n" // "Inches" |
|---|
| 134 | "A4\n" // papersize |
|---|
| 135 | "100.0\n" // export&print magnification % |
|---|
| 136 | "Single\n" // Single/Multiple Pages |
|---|
| 137 | "-3\n" // background = transparent for gif export |
|---|
| 138 | "%i 2\n" // dpi, 2 = origin in upper left corner |
|---|
| 139 | , DPI_PRINTER); |
|---|
| 140 | |
|---|
| 141 | if (color_mode) { |
|---|
| 142 | for (int i=0; i<get_common()->get_data_color_size(); i++) { |
|---|
| 143 | unsigned long col = get_common()->get_data_color(i); |
|---|
| 144 | if (col != (unsigned long)AW_NO_COLOR) fprintf(out, "0 %d #%06lx\n", i+DATA_COLOR_OFFSET, col); |
|---|
| 145 | } |
|---|
| 146 | } |
|---|
| 147 | |
|---|
| 148 | return 0; |
|---|
| 149 | } |
|---|
| 150 | |
|---|
| 151 | int AW_common::find_data_color_idx(AW_rgb color) const { |
|---|
| 152 | for (int i=0; i<data_colors_size; i++) { |
|---|
| 153 | if (color == data_colors[i]) { |
|---|
| 154 | return i; |
|---|
| 155 | } |
|---|
| 156 | } |
|---|
| 157 | return -1; |
|---|
| 158 | } |
|---|
| 159 | |
|---|
| 160 | int AW_device_print::find_color_idx(AW_rgb color) { |
|---|
| 161 | int idx = -1; |
|---|
| 162 | if (color_mode) { |
|---|
| 163 | idx = get_common()->find_data_color_idx(color); |
|---|
| 164 | if (idx >= 0) idx += DATA_COLOR_OFFSET; |
|---|
| 165 | } |
|---|
| 166 | return idx; |
|---|
| 167 | } |
|---|
| 168 | |
|---|
| 169 | void AW_device_print::set_color_mode(bool mode) { |
|---|
| 170 | color_mode=mode; |
|---|
| 171 | } |
|---|
| 172 | |
|---|
| 173 | void AW_device_print::close() { |
|---|
| 174 | if (out) fclose(out); |
|---|
| 175 | out = 0; |
|---|
| 176 | } |
|---|
| 177 | |
|---|
| 178 | |
|---|
| 179 | bool AW_device_print::text_impl(int gc, const char *str, const Position& pos, AW_pos alignment, AW_bitset filteri, long opt_strlen) { |
|---|
| 180 | return text_overlay(gc, str, opt_strlen, pos, alignment, filteri, (AW_CL)this, 0.0, 0.0, AW_draw_string_on_printer); |
|---|
| 181 | } |
|---|
| 182 | |
|---|
| 183 | bool AW_device_print::box_impl(int gc, bool filled, const Rectangle& rect, AW_bitset filteri) { |
|---|
| 184 | bool drawflag = false; |
|---|
| 185 | if (filter & filteri) { |
|---|
| 186 | if (filled) { |
|---|
| 187 | Position q[4]; |
|---|
| 188 | q[0] = rect.upper_left_corner(); |
|---|
| 189 | q[1] = rect.upper_right_corner(); |
|---|
| 190 | q[2] = rect.lower_right_corner(); |
|---|
| 191 | q[3] = rect.lower_left_corner(); |
|---|
| 192 | |
|---|
| 193 | drawflag = filled_area(gc, 4, q, filteri); |
|---|
| 194 | } |
|---|
| 195 | else { |
|---|
| 196 | drawflag = generic_box(gc, false, rect, filteri); |
|---|
| 197 | } |
|---|
| 198 | } |
|---|
| 199 | return drawflag; |
|---|
| 200 | } |
|---|
| 201 | |
|---|
| 202 | bool AW_device_print::circle_impl(int gc, bool filled, const Position& center, const AW::Vector& radius, AW_bitset filteri) { |
|---|
| 203 | bool drawflag = false; |
|---|
| 204 | if (filteri & filter) { |
|---|
| 205 | aw_assert(radius.x()>0 && radius.y()>0); |
|---|
| 206 | Rectangle Box(center-radius, center+radius); |
|---|
| 207 | Rectangle screen_box = transform(Box); |
|---|
| 208 | Rectangle clipped_box; |
|---|
| 209 | drawflag = box_clip(screen_box, clipped_box); |
|---|
| 210 | bool half_visible = (clipped_box.surface()*2) > screen_box.surface(); |
|---|
| 211 | |
|---|
| 212 | drawflag = drawflag && half_visible; |
|---|
| 213 | // @@@ correct behavior would be to draw an arc if only partly visible |
|---|
| 214 | |
|---|
| 215 | if (drawflag) { |
|---|
| 216 | const AW_GC *gcm = get_common()->map_gc(gc); |
|---|
| 217 | |
|---|
| 218 | // force into clipped_box (hack): |
|---|
| 219 | Position Center = clipped_box.centroid(); |
|---|
| 220 | Vector screen_radius = clipped_box.diagonal()/2; |
|---|
| 221 | |
|---|
| 222 | int cx = print_pos(Center.xpos()); |
|---|
| 223 | int cy = print_pos(Center.ypos()); |
|---|
| 224 | int rx = print_pos(screen_radius.x()); |
|---|
| 225 | int ry = print_pos(screen_radius.y()); |
|---|
| 226 | |
|---|
| 227 | { |
|---|
| 228 | int subtype = (rx == ry) ? 3 : 1; // 3(circle) 1(ellipse) |
|---|
| 229 | subtype = 3; // @@@ remove after refactoring |
|---|
| 230 | fprintf(out, "1 %d ", subtype); // type + subtype: |
|---|
| 231 | } |
|---|
| 232 | |
|---|
| 233 | { |
|---|
| 234 | int colorIdx = find_color_idx(gcm->get_last_fg_color()); |
|---|
| 235 | int fill_color, area_fill; |
|---|
| 236 | if (filled) { |
|---|
| 237 | fill_color = colorIdx; |
|---|
| 238 | area_fill = AW_INT(20+20*gcm->get_grey_level()); // 20 = full saturation; 40 = white; |
|---|
| 239 | } |
|---|
| 240 | else { |
|---|
| 241 | fill_color = area_fill = -1; |
|---|
| 242 | } |
|---|
| 243 | int line_width = gcm->get_line_width(); |
|---|
| 244 | |
|---|
| 245 | fprintf(out, "%d %d ", AW_SOLID, line_width); // line_style + line_width |
|---|
| 246 | fprintf(out, "%d %d 0 ", colorIdx, fill_color); // pen_color + fill_color + depth |
|---|
| 247 | fprintf(out, "0 %d ", area_fill); // pen_style + area_fill (20 = full color, 40 = white) |
|---|
| 248 | fputs("0.000 1 0.0000 ", out); // style_val + direction + angle (x-axis) |
|---|
| 249 | } |
|---|
| 250 | |
|---|
| 251 | fprintf(out, "%d %d ", cx, cy); // center |
|---|
| 252 | fprintf(out, "%d %d ", rx, ry); // radius |
|---|
| 253 | fprintf(out, "%d %d ", cx, cy); // start |
|---|
| 254 | fprintf(out, "%d %d\n", print_pos(Center.xpos()+screen_radius.x()), cy); // end |
|---|
| 255 | } |
|---|
| 256 | } |
|---|
| 257 | return drawflag; |
|---|
| 258 | } |
|---|
| 259 | |
|---|
| 260 | bool AW_device_print::arc_impl(int gc, bool filled, const AW::Position& center, const AW::Vector& radius, int start_degrees, int arc_degrees, AW_bitset filteri) { |
|---|
| 261 | bool drawflag = false; |
|---|
| 262 | if (filteri && filter) { |
|---|
| 263 | aw_assert(radius.x()>0 && radius.y()>0); |
|---|
| 264 | Rectangle Box(center-radius, center+radius); |
|---|
| 265 | Rectangle screen_box = transform(Box); |
|---|
| 266 | Rectangle clipped_box; |
|---|
| 267 | drawflag = box_clip(screen_box, clipped_box); |
|---|
| 268 | bool half_visible = (clipped_box.surface()*2) > screen_box.surface(); |
|---|
| 269 | |
|---|
| 270 | drawflag = drawflag && half_visible; |
|---|
| 271 | // @@@ correct behavior would be to draw an arc if only partly visible |
|---|
| 272 | |
|---|
| 273 | if (drawflag) { |
|---|
| 274 | const AW_GC *gcm = get_common()->map_gc(gc); |
|---|
| 275 | |
|---|
| 276 | // force into clipped_box (hack): |
|---|
| 277 | Position Center = clipped_box.centroid(); |
|---|
| 278 | Vector screen_radius = clipped_box.diagonal()/2; |
|---|
| 279 | |
|---|
| 280 | int cx = print_pos(Center.xpos()); |
|---|
| 281 | int cy = print_pos(Center.ypos()); |
|---|
| 282 | int rx = print_pos(screen_radius.x()); |
|---|
| 283 | int ry = print_pos(screen_radius.y()); |
|---|
| 284 | |
|---|
| 285 | bool use_spline = (rx != ry); // draw interpolated spline for ellipsoid arcs |
|---|
| 286 | |
|---|
| 287 | // fputs(use_spline ? "3 2 " : "5 1 ", out); // type + subtype: |
|---|
| 288 | fputs(use_spline ? "3 4 " : "5 1 ", out); // type + subtype: |
|---|
| 289 | |
|---|
| 290 | { |
|---|
| 291 | int colorIdx = find_color_idx(gcm->get_last_fg_color()); |
|---|
| 292 | int fill_color, area_fill; |
|---|
| 293 | |
|---|
| 294 | if (filled) { |
|---|
| 295 | fill_color = colorIdx; |
|---|
| 296 | area_fill = AW_INT(20+20*gcm->get_grey_level()); // 20 = full saturation; 40 = white; |
|---|
| 297 | } |
|---|
| 298 | else { |
|---|
| 299 | fill_color = area_fill = -1; |
|---|
| 300 | } |
|---|
| 301 | int line_width = gcm->get_line_width(); |
|---|
| 302 | |
|---|
| 303 | fprintf(out, "%d %d ", AW_SOLID, line_width); // line_style + line_width |
|---|
| 304 | fprintf(out, "%d %d 0 ", colorIdx, fill_color); // pen_color + fill_color + depth |
|---|
| 305 | fprintf(out, "0 %d ", area_fill); // pen_style + area_fill (20 = full color, 40 = white) |
|---|
| 306 | fputs("0.000 1 ", out); // style_val + cap_style |
|---|
| 307 | if (!use_spline) fputs("1 ", out); // direction |
|---|
| 308 | fputs("0 0 ", out); // 2 arrows |
|---|
| 309 | } |
|---|
| 310 | |
|---|
| 311 | Angle a0(Angle::deg2rad*start_degrees); |
|---|
| 312 | Angle a1(Angle::deg2rad*(start_degrees+arc_degrees)); |
|---|
| 313 | |
|---|
| 314 | if (use_spline) { |
|---|
| 315 | const int MAX_ANGLE_STEP = 45; // degrees |
|---|
| 316 | |
|---|
| 317 | int steps = (abs(arc_degrees)-1)/MAX_ANGLE_STEP+1; |
|---|
| 318 | Angle step(Angle::deg2rad*double(arc_degrees)/steps); |
|---|
| 319 | |
|---|
| 320 | fprintf(out, "%d\n\t", steps+1); // npoints |
|---|
| 321 | |
|---|
| 322 | double rmax, x_factor, y_factor; |
|---|
| 323 | if (rx>ry) { |
|---|
| 324 | rmax = rx; |
|---|
| 325 | x_factor = 1.0; |
|---|
| 326 | y_factor = double(ry)/rx; |
|---|
| 327 | } |
|---|
| 328 | else { |
|---|
| 329 | rmax = ry; |
|---|
| 330 | x_factor = double(rx)/ry; |
|---|
| 331 | y_factor = 1.0; |
|---|
| 332 | } |
|---|
| 333 | |
|---|
| 334 | for (int n = 0; n <= steps; ++n) { |
|---|
| 335 | Vector toCircle = a0.normal()*rmax; |
|---|
| 336 | Vector toEllipse(toCircle.x()*x_factor, toCircle.y()*y_factor); |
|---|
| 337 | Position onEllipse = Center+toEllipse; |
|---|
| 338 | |
|---|
| 339 | int x = print_pos(onEllipse.xpos()); |
|---|
| 340 | int y = print_pos(onEllipse.ypos()); |
|---|
| 341 | |
|---|
| 342 | fprintf(out, " %d %d", x, y); |
|---|
| 343 | |
|---|
| 344 | if (n<steps) { |
|---|
| 345 | if (n == steps-1) a0 = a1; |
|---|
| 346 | else a0 += step; |
|---|
| 347 | } |
|---|
| 348 | } |
|---|
| 349 | fputs("\n\t", out); |
|---|
| 350 | for (int n = 0; n <= steps; ++n) { |
|---|
| 351 | // -1 = interpolate; 0 = discontinuity; 1 = approximate |
|---|
| 352 | fprintf(out, " %d", (n == 0 || n == steps) ? 0 : -1); |
|---|
| 353 | } |
|---|
| 354 | fputc('\n', out); |
|---|
| 355 | } |
|---|
| 356 | else { |
|---|
| 357 | fprintf(out, "%d %d ", cx, cy); // center |
|---|
| 358 | |
|---|
| 359 | Angle am(Angle::deg2rad*(start_degrees+arc_degrees*0.5)); |
|---|
| 360 | |
|---|
| 361 | double r = screen_radius.x(); |
|---|
| 362 | Position p0 = Center+a0.normal()*r; |
|---|
| 363 | Position pm = Center+am.normal()*r; |
|---|
| 364 | Position p1 = Center+a1.normal()*r; |
|---|
| 365 | |
|---|
| 366 | fprintf(out, "%d %d ", print_pos(p0.xpos()), print_pos(p0.ypos())); |
|---|
| 367 | fprintf(out, "%d %d ", print_pos(pm.xpos()), print_pos(pm.ypos())); |
|---|
| 368 | fprintf(out, "%d %d\n", print_pos(p1.xpos()), print_pos(p1.ypos())); |
|---|
| 369 | } |
|---|
| 370 | } |
|---|
| 371 | } |
|---|
| 372 | return drawflag; |
|---|
| 373 | } |
|---|
| 374 | |
|---|
| 375 | bool AW_device_print::filled_area_impl(int gc, int npos, const Position *pos, AW_bitset filteri) { |
|---|
| 376 | bool drawflag = false; |
|---|
| 377 | if (filter & filteri) { |
|---|
| 378 | drawflag = generic_filled_area(gc, npos, pos, filteri); |
|---|
| 379 | if (drawflag) { // line visible -> area fill needed |
|---|
| 380 | const AW_GC *gcm = get_common()->map_gc(gc); |
|---|
| 381 | |
|---|
| 382 | short greylevel = (short)(gcm->get_grey_level()*22); |
|---|
| 383 | if (greylevel>21) greylevel = 21; |
|---|
| 384 | |
|---|
| 385 | int line_width = gcm->get_line_width(); |
|---|
| 386 | |
|---|
| 387 | fprintf(out, "2 3 0 %d %d -1 0 0 %d 0.000 0 0 -1 0 0 %d\n", |
|---|
| 388 | line_width, find_color_idx(gcm->get_last_fg_color()), greylevel, npos+1); |
|---|
| 389 | |
|---|
| 390 | // @@@ method used here for clipping leads to wrong results, |
|---|
| 391 | // since group border (drawn by generic_filled_area() above) is clipped correctly, |
|---|
| 392 | // but filled content is clipped different. |
|---|
| 393 | // |
|---|
| 394 | // fix: clip the whole polygon before drawing border |
|---|
| 395 | |
|---|
| 396 | for (int i=0; i <= npos; i++) { |
|---|
| 397 | int j = i == npos ? 0 : i; // print pos[0] after pos[n-1] |
|---|
| 398 | |
|---|
| 399 | Position transPos = transform(pos[j]); |
|---|
| 400 | Position clippedPos; |
|---|
| 401 | ASSERT_RESULT(bool, true, force_into_clipbox(transPos, clippedPos)); |
|---|
| 402 | fprintf(out, " %d %d\n", print_pos(clippedPos.xpos()), print_pos(clippedPos.ypos())); |
|---|
| 403 | } |
|---|
| 404 | } |
|---|
| 405 | } |
|---|
| 406 | return drawflag; |
|---|
| 407 | } |
|---|