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