source: projects/paper-tape-project/trunk/visualisator/lochstreifen.c @ 77

Last change on this file since 77 was 77, checked in by sven, 11 years ago

Bugfixes, Recovered Exporting function, ...

File size: 38.0 KB
Line 
1/**
2 * The Paper Tape Project -- Visualisation sub project
3 * lochstreifen.c - Painting Paper Tapes according to ECMA-10, with cairographics.
4 * $Id$
5 *
6 * This is a rewrite from the famous Cairo paper tape drawing routines. This
7 * c file implements a (simple) object called "LOCHSTREIFEN".
8 *
9 *
10 *
11 *
12 *
13 * (c) Copyright 2007, 2008 Sven Köppel
14 *
15 *  This program is free software; you can redistribute
16 *  it and/or modify it under the terms of the GNU General
17 *  Public License as published by the Free Software
18 *  Foundation; either version 3 of the License, or (at
19 *  your option) any later version.
20 *
21 *  This program is distributed in the hope that it will
22 *  be useful, but WITHOUT ANY WARRANTY; without even the
23 *  implied warranty of MERCHANTABILITY or FITNESS FOR A
24 *  PARTICULAR PURPOSE. See the GNU General Public License
25 *  for more details.
26 *
27 *  You should have received a copy of the GNU General
28 *  Public License along with this program; if not, see
29 *  http://www.gnu.org/licenses/
30 *
31 **/
32
33#include <math.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include "lochstreifen.h"
38
39/**
40 * Create a new LOCHSTREIFEN object and return it. This is a quite light
41 * object system implementation, as you get a reference to the object.
42 * You are not supposed to manipulate the elements of the LOCHSTREIFEN
43 * structure directly.
44 *
45 * Btw, the german word "LOCHSTREIFEN" only means "PAPER TAPE".
46 *
47 **/
48LOCHSTREIFEN *lochstreifen_new() {
49        LOCHSTREIFEN *l = malloc (sizeof(LOCHSTREIFEN));
50
51        // set default values:
52        l->data_length                = 0;
53        l->data                       = NULL;
54        l->highlight_region_start     = 0;
55        l->highlight_region_end       = 0;
56        l->highlight_region_color     = NULL;
57        l->highlight_row_number       = 0;
58        l->highlight_row_color        = NULL;
59        l->highlight_bit_row          = 0;
60        l->highlight_bit_track        = 0;
61        l->highlight_bit_color        = NULL;
62        l->clip                       = NULL;
63        l->outer_background_color     = LOCHSTREIFEN_DEFAULT_OUTER_BACKGROUND_COLOR;
64        l->papertape_background_color = LOCHSTREIFEN_DEFAULT_PAPERTAPE_BACKGROUND_COLOR;
65        l->one_code_hole_color        = LOCHSTREIFEN_DEFAULT_ONE_CODE_HOLE_COLOR;
66        l->zero_code_hole_color       = LOCHSTREIFEN_DEFAULT_ZERO_CODE_HOLE_COLOR;
67        l->feed_hole_color            = LOCHSTREIFEN_DEFAULT_FEED_HOLE_COLOR;
68        cairo_matrix_init(&l->matrix, 1, 0, 0, 1, 0, 0); // default = 1:1 original
69        l->matrix_inverse             = l->matrix;
70
71        return l;
72}
73
74/**
75 * Copy everything from the LOCHSTREIFEN template, except the data. They
76 * won't be copied, just set, because LOCHSTREIFEN doesn't manage your
77 * data. If you want to copy them, call something like
78 *   LOCHSTREIFEN* copy = lochstreifen_copy(old);
79 *   byte_t* new_data = malloc(old->data_length);
80 *   memcpy(new_data, old->data, old->data_length);
81 *   lochstreifen_set_data(copy, old->data_length, new_data);
82 **/
83LOCHSTREIFEN* lochstreifen_copy(const LOCHSTREIFEN* template) {
84        LOCHSTREIFEN *l = malloc (sizeof(LOCHSTREIFEN));
85        #define COPY_RGBA_PATTERN(name) { double r,g,b,a; \
86                if(template->name != NULL && \
87                   !cairo_pattern_get_rgba(template->name, &r, &g, &b, &a)) \
88                        l->name = cairo_pattern_create_rgba(r,g,b,a); }
89       
90        // make it quite verbose ;-)
91        l->data_length                = template->data_length;
92        l->data                       = template->data;
93        l->highlight_region_start     = template->highlight_region_start;
94        l->highlight_region_end       = template->highlight_region_end;
95        printf("Copy1\n");
96        COPY_RGBA_PATTERN(highlight_region_color);
97        printf("Copy2\n");
98        l->highlight_row_number       = template->highlight_row_number;
99        COPY_RGBA_PATTERN(highlight_row_color);
100        l->highlight_bit_row          = template->highlight_bit_row;
101        l->highlight_bit_track        = template->highlight_bit_track;
102        COPY_RGBA_PATTERN(highlight_bit_color);
103        l->clip                       = malloc(sizeof(cairo_rectangle_t));
104        memcpy(l->clip, template->clip, sizeof(cairo_rectangle_t));
105        COPY_RGBA_PATTERN(outer_background_color);
106        COPY_RGBA_PATTERN(papertape_background_color);
107        COPY_RGBA_PATTERN(one_code_hole_color);
108        COPY_RGBA_PATTERN(zero_code_hole_color);
109        COPY_RGBA_PATTERN(feed_hole_color);
110        l->matrix                     = template->matrix;
111        l->matrix_inverse             = template->matrix_inverse;
112        l->row_callback               = template->row_callback;
113        l->row_callback_user_data     = template->row_callback_user_data;
114
115        return l;
116}
117
118/**
119 * Delete every mallocated structure in the LOCHSTREIFEN (colors, etc.),
120 * *except* from the data. You have to manage them on your own.
121 * In the end, it will free the LOCHSTREIFEN itself.
122 * This won't touch your row_callback_user_data.
123 * @return Nothing, because there's nothing left. Except your data, okay.
124 **/
125void lochstreifen_free(LOCHSTREIFEN* l) {
126        if(l->highlight_region_color     != NULL)  free(l->highlight_region_color);
127        if(l->highlight_row_color        != NULL)  free(l->highlight_row_color);
128        if(l->highlight_bit_color        != NULL)  free(l->highlight_bit_color);
129        if(l->clip                       != NULL)  free(l->clip);
130        if(l->outer_background_color     != NULL)  free(l->outer_background_color);
131        if(l->papertape_background_color != NULL)  free(l->papertape_background_color);
132        if(l->one_code_hole_color        != NULL)  free(l->one_code_hole_color);
133        if(l->zero_code_hole_color       != NULL)  free(l->zero_code_hole_color);
134        if(l->feed_hole_color            != NULL)  free(l->feed_hole_color);
135}
136
137/**
138 * A small debug function that will print out all fields of the LOCHSTREIFEN
139 * structure well ordered to stderr.
140 *
141 **/
142void lochstreifen_print_debug(LOCHSTREIFEN* l) {
143        int x;
144        #define COLORP(color) if(color == NULL) fprintf(stderr, "   color ist NULL.\n"); \
145                else if(cairo_pattern_get_type(color) == CAIRO_PATTERN_TYPE_SOLID) {\
146                double r,g,b,a; cairo_pattern_get_rgba(color, &r, &g, &b, &a); \
147                fprintf(stderr, "   SOLID: R=%f, G=%f, B=%f, A=%f\n", r, g, b, a); \
148                } else fprintf(stderr, "    NO SOLID PATTERN (special pattern)\n");
149        #define COLORP_VERBOSE(color1, name) \
150                fprintf(stderr, " * "name": %s\n", (color1) != NULL ? "ENABLED" : "disabled");\
151                COLORP(color1);
152
153       
154        if(l != NULL)
155                fprintf(stderr, "Debugging papertape at %p:\n", l);
156        else {
157                fprintf(stderr, "Cannot debug: Papertape is NULL!\n");
158                return;
159        }
160       
161        // 1. Point: Print out data.
162        fprintf(stderr, "1# DATA AND OUTPUT IMAGE SIZE INFORMATION\n");
163        fprintf(stderr, " * Data: length=%i bytes", l->data_length);
164        if(l->data_length > 0 && l->data != NULL) {
165                // there are data
166                fprintf(stderr, ":\n   { ");
167                for(x=0;;) {
168                        fprintf(stderr, "0x%x", l->data[x]);
169                        if(++x == l->data_length) {
170                                fprintf(stderr, " };\n");
171                                break;
172                        } else if(x == 10) { // magic number: print first 10 bytes.
173                                fprintf(stderr, ", ... };\n");
174                                break;
175                        } else
176                                fprintf(stderr, ", ");
177                }
178        } else if(l->data == NULL) {
179                fprintf(stderr, " BUT data are NULL!\n");
180        } else  // data_length == 0.
181                fprintf(stderr, " (empty data)\n");
182        fprintf(stderr, " * Unscaled dimensions:\n");
183        fprintf(stderr, "   width of image (length of papertape) = %f\n", lochstreifen_get_length(l));
184        fprintf(stderr, "   height of image (width of papertape) = 1\n");
185       
186        // 2. Point: Print out colors
187        fprintf(stderr, "\n2# ELEMENT COLORS\n");
188        COLORP_VERBOSE(l->outer_background_color, "outer background");
189        COLORP_VERBOSE(l->papertape_background_color, "papertape background");
190        COLORP_VERBOSE(l->one_code_hole_color, "one code holes");
191        COLORP_VERBOSE(l->zero_code_hole_color, "zero code holes");
192        COLORP_VERBOSE(l->feed_hole_color, "feed holes");
193       
194        // 3. Point: Print out highlighted thingies
195        fprintf(stderr, "\n3# HIGHLIGHT COLORS\n");
196        fprintf(stderr, l->highlight_region_color != NULL ? " * Highlight region: start=%i end=%i\n"
197                : " * Highlight region: disabled\n", l->highlight_region_start, l->highlight_region_end);
198        COLORP(l->highlight_region_color);
199        fprintf(stderr, l->highlight_row_color != NULL ? " * Highlight row: number=%i\n"
200                : " * Highlight row: disabled\n", l->highlight_row_number);
201        COLORP(l->highlight_row_color);
202        fprintf(stderr, l->highlight_bit_color != NULL ? " * Higlight bit: row=%i track=%i\n"
203                : " * Highlight bit: disabled\n", l->highlight_bit_row, l->highlight_bit_track);
204        COLORP(l->highlight_bit_color);
205       
206        // 4. Point: Print out information about clippings
207        fprintf(stderr, "\n4# SPECIAL STRUCTURES\n");
208        if(l->clip == NULL)
209                fprintf(stderr, " * Clipping: disabled\n");
210        else {
211                fprintf(stderr, " * Clipping: ENABLED\n");
212                fprintf(stderr, "  x=%f, y=%f, width=%f, height=%f\n",
213                        l->clip->x, l->clip->y, l->clip->width, l->clip->height);
214        }
215       
216        // 5. Point: Affine Matrix
217        fprintf(stderr, " * Current transformation matrix:\n");
218        fprintf(stderr, "   x_new = [xx=%f]*x + [xy=%f]*y + [x0=%f]\n",
219                l->matrix.xx, l->matrix.xy, l->matrix.x0);
220        fprintf(stderr, "   y_new = [yx=%f]*x + [yy=%f]*y + [y0=%f]\n",
221                l->matrix.yx, l->matrix.yy, l->matrix.y0);
222        fprintf(stderr, "   source dimensions: x_max = %f x y_max = %f\n",
223                LOCHSTREIFEN_LENGTH, LOCHSTREIFEN_WIDTH);
224        fprintf(stderr, "   target dimensions: width = %i x height = %i\n",
225                lochstreifen_get_target_width(l),
226                lochstreifen_get_target_height(l));
227       
228        // 6. Point: Debugging
229        fprintf(stderr, " * Debugging: %s\n", l->debug ? "ENABLED" : "disabled");
230       
231        fprintf(stderr, "END OF LOCHSTREIFEN\n");
232}
233
234/**
235 * Set the data which LOCHSTREIFEN is supposed to punch out. You shall give
236 * the LOCSTREIFEN object only a copy of your data, never give it a sensible
237 * data array. The LOCHSTREIFEN object won't write into your data, but afterwards
238 * it will free them. So it treats the data as it's own.
239 *
240 **/
241void lochstreifen_set_data(LOCHSTREIFEN *l, int data_length, byte_t *data) {
242        l->data_length = data_length;
243        //if(l->data != NULL)
244        //      free(l->data);
245        l->data = data;
246}
247
248/**
249 * Adds Null Bytes (0x00) at start and/or end of the data array. This dynamically
250 * increases the data array.
251 *
252 *
253 **/
254void lochstreifen_add_null_bytes(LOCHSTREIFEN *l, int start, int end) {
255        byte_t *new_data;
256
257        // only to be sure realloc won't decrease the size of the array:
258        if(start < 0) start = 0;
259        if(end < 0)   end = 0;
260       
261        // make array bigger to contain new start/end chunks
262        new_data = realloc(l->data, l->data_length + start + end);
263        // check wheter realloc exited successfully. We don't want segfaults
264        if(new_data == NULL)
265                return;
266        // move data to the middle
267        memmove(new_data + start, new_data, l->data_length);
268        // fill new start chunk with zero bytes
269        memset(new_data, 0x00, start);
270        // fill new end chunk with zero bytes
271        memset(new_data + start + l->data_length, 0x00, end);
272        // that's it.
273       
274        // fix the data link. Don't free the old data. Don't know exactly
275        // how realloc works, sorry ;-)
276        l->data = new_data;
277}
278
279/**
280 * Get the length of a LOCHSTREIFEN. It depends on the length of the data array
281 * and the papertape dimension constants.
282 *
283 *
284 **/
285double lochstreifen_get_length(LOCHSTREIFEN *l) {
286        return LOCHSTREIFEN_LENGTH;
287}
288
289
290/**
291 * Tell LOCHSTREIFEN to highlight a region of rows. Start counts from 0 and is already
292 * in the selection, end is no more in selection. Example with start = 2; end = 5:
293 *
294 * 0    1     2     3     4     5    6  <- rows
295 *         |_________________|
296 *          highlight region
297 **/
298void lochstreifen_set_highlight_region(LOCHSTREIFEN *l, row_t start, row_t end) {
299        l->highlight_region_start = start;
300        l->highlight_region_end   = end;
301        if(l->highlight_region_color == NULL)
302                l->highlight_row_color = LOCHSTREIFEN_DEFAULT_HIGHLIGHT_REGION_COLOR;
303}
304
305/**
306 * Get the selection bounds of the highlighted rows. Give Links to your variables
307 * where the function stores the start/end integers.
308 * @return TRUE if there is an highlight selection, FALSE otherwiese
309 **/
310int lochstreifen_get_highlight_region(LOCHSTREIFEN *l, row_t *start, row_t *end) {
311        if(l->highlight_region_color == NULL || l->highlight_region_start == LOCHSTREIFEN_NO_ROW)
312                return 0;
313        if(start != NULL)
314                *start = l->highlight_region_start;
315        if(end != NULL)
316                *end   = l->highlight_region_end;
317        return 1;
318}
319
320/**
321 * Remove the highlighted region (that is, make it no more highlighted).
322 **/
323void lochstreifen_remove_highlight_region(LOCHSTREIFEN *l) {
324        //l->highlight_region_color = NULL;
325        // was stupid, this is more intelligent:
326        l->highlight_region_start = LOCHSTREIFEN_NO_ROW;
327}
328
329/**
330 * Tell LOCHSTREIFEN to highlight one, only one special row. Starts counting from 0.
331 * Example with start = 3:
332 *
333 * 0   1    2     3     4     5
334 *             |_____|
335 *        highlighted row
336 *
337 * Principally this is the same feature like lochstreifen_set_highlight_region, having
338 * (in the above example) start = 3 and end = 4. You can just use another color and
339 * use both techniques concurrently, whereby the highlighted row will ALWAYS be ABOVE
340 * the highlighted chunk of rows. An application example is e.g.: The highlighted region
341 * of rows are selected rows, the highlighted special row is the cursor position row.
342 * This would be quite usable with an implementation of the GtkEditable interface, like
343 * GtkPaperTape.
344 **/
345void lochstreifen_set_highlight_row(LOCHSTREIFEN *l, row_t number_of_row) {
346        l->highlight_row_number = number_of_row;
347        if(l->highlight_row_color == NULL)
348                l->highlight_row_color = LOCHSTREIFEN_DEFAULT_HIGHLIGHT_ROW_COLOR;
349}
350
351/**
352 * Get the highlighted special row. If there is no highlighted row, it will return
353 * LOCHSTREIFEN_NO_ROW.
354 * @return the number of the special row, counting from zero.
355 **/
356int lochstreifen_get_highlight_row(LOCHSTREIFEN *l) {
357        return l->highlight_row_number;
358}
359
360/**
361 * Remove the highlight at the special selected row.
362 **/
363void lochstreifen_remove_highlight_row(LOCHSTREIFEN *l) {
364        //l->highlight_row_color = NULL;  /// nein, das ist dumm, weil es den highlight komplett entfernen würde.
365        l->highlight_row_number = LOCHSTREIFEN_NO_ROW; // das ist schlau.
366}
367
368/**
369 * Tell LOCHSTREIFEN to highlight one special bit on the complete papertape. To identify
370 * this bit, you give the row and track number (byte id and bit id) for this bit. E.g.
371 * with row = 3, bit = 5 you'll get something like:
372 *
373 * ------- DIRECTION OF MOVEMENT ------->
374 * track 8     8     8     8     8     8
375 * track 7     7     7     7     7     7
376 * track 6     6     6  +-----+  6     6
377 * track 5     5     5  |  5  |  5     5       (of course the highlight won't affect any
378 * track 4     4     4  +-----+  4     4        other bit, row 3, track 6 and track 4
379 * feed  -     -     -     -     -     -        are not visible due to ASCII limitations
380 * track 3     3     3     3     3     3        ;-) )
381 * track 2     2     2     2     2     2
382 * track 1     1     1     1     1     1
383 *
384 * row:  0     1     2     3     4     5
385 *                         ^-- this row
386 *
387 * The bit highlight will appear on top of the row highlight and region highlight. You
388 * can combine the bit highlight with row highlight (e.g. in the example above you can
389 * highlight row 3, too) and even with the region highlight (e.g. having a region from
390 * row 2 to row 5).
391 **/
392void lochstreifen_set_highlight_bit(LOCHSTREIFEN *l, row_t number_of_row, track_t number_of_bit) {
393        l->highlight_bit_row = number_of_row;
394        l->highlight_bit_track = number_of_bit;
395        if(l->highlight_bit_color == NULL)
396                l->highlight_bit_color = LOCHSTREIFEN_DEFAULT_HIGHLIGHT_BIT_COLOR;
397}
398
399/**
400 * Get the highlighted special bit. This works exactly like lochstreifen_get_highlight_region,
401 * returning FALSE (0) if there's no bit selected.
402 *
403 **/
404int lochstreifen_get_highlight_bit(LOCHSTREIFEN *l, row_t *number_of_row, track_t *number_of_bit) {
405        if(l->highlight_bit_row == LOCHSTREIFEN_NO_ROW)
406                return 0;
407        if(number_of_row != NULL)
408                *number_of_row = l->highlight_bit_row;
409        if(number_of_bit != NULL)
410                *number_of_bit = l->highlight_bit_track;
411        return 1;
412}
413
414/**
415 * Remove the highlighting of the special bit.
416 **/
417void lochstreifen_remove_highlight_bit(LOCHSTREIFEN *l) {
418        //l->highlight_bit_color = NULL; // stupid.
419        l->highlight_bit_row = LOCHSTREIFEN_NO_ROW; // more intelligent.
420}
421
422/**
423 * Whenever you change the matrix, call this function. It will just perform some
424 * cleanup work, espacially calculate the inverse matrix for the other-side calculations.
425 **/
426void lochstreifen_check_matrix(LOCHSTREIFEN* l) {
427        l->matrix_inverse = l->matrix; // copy matrix
428        if(cairo_matrix_invert(&l->matrix_inverse) != CAIRO_STATUS_SUCCESS) { // invert the copy
429                // there are some matrizes which are not invertable
430                fprintf(stderr, "lochstreifen_check_matrix: Matrix is not invertable!\n");
431        }
432}
433
434/**
435 * If you want LOCHSTREIFEN only to paint a small area from the whole papertape, this
436 * is the function you can call.
437 * If you are using an affine transformation matrix for scaling/rotation/... purpose,
438 * this function will automatically transform these values. So don't give here values
439 * like "LOCHSTREIFEN_WIDTH" or "LOCHSTREIFEN_HEIGHT" ;-)
440 *
441 **/
442void lochstreifen_set_clip(LOCHSTREIFEN *l, double x, double y, double width, double height) {
443        if(l->clip == NULL)
444                l->clip = malloc(sizeof(cairo_rectangle_t));
445
446        cairo_matrix_transform_point(&l->matrix_inverse, &x, &y);
447        cairo_matrix_transform_distance(&l->matrix_inverse, &width, &height);
448        l->clip->x = x;
449        l->clip->y = y;
450        l->clip->width = width;
451        l->clip->height = height;
452}
453
454/**
455 * I fyou want LOCHSTREIFEN no more to paint only a small area but the whole paper tape,
456 * simply call this function.
457 **/
458void lochstreifen_remove_clip(LOCHSTREIFEN *l) {
459        free(l->clip);
460        l->clip = NULL; // not neccessary, I suppose.
461}
462
463/**
464 * Affect the current transformation matrix so the papertape finally has a length of
465 * exactly the value given in the argument, in pixels. The length depends from the
466 * number of rows on the papertape, so the length will increase if you increase the
467 * number of rows after calling lochstreifen_set_scaling_by_length.
468 **/
469void lochstreifen_set_scaling_by_length(LOCHSTREIFEN *l, int length) {
470        double scale_factor = ((double)length) / LOCHSTREIFEN_LENGTH;
471        printf("Scaling to length=%i, having length=%f, getting %f as scale factor\n",
472                length, LOCHSTREIFEN_LENGTH, scale_factor);
473        cairo_matrix_scale(&l->matrix, scale_factor, scale_factor);
474        lochstreifen_check_matrix(l);
475}
476
477void lochstreifen_set_scaling_by_width(LOCHSTREIFEN *l, int width) {
478        double scale_factor = ((double)width) / LOCHSTREIFEN_WIDTH;
479        cairo_matrix_scale(&l->matrix, scale_factor, scale_factor);
480        lochstreifen_check_matrix(l);
481}
482
483void lochstreifen_set_scaling_by_code_hole(LOCHSTREIFEN *l, int diameter) {
484        double scale_factor = ((double)diameter)/2 / LOCHSTREIFEN_RADIUS_CODE_HOLE;
485        cairo_matrix_scale(&l->matrix, scale_factor, scale_factor);
486        lochstreifen_check_matrix(l);
487}
488
489/**
490 * Get the dimensions of the paper tape in the output image. Be careful: While
491 * width/length are well defined in the LOCHSTREIFEN process, hereby width and
492 * height mean the dimensions of the rectangular surface picture.
493 * If you need only the value of one dimension, use
494 * lochstreifen_get_target_width or lochstreifen_get_target_height, respectively.
495 *
496 * The calculation of these values is quite expensive, so you do well when
497 * caching the results.
498 * @return nothing, because Call-by-Reference.
499 **/
500void lochstreifen_get_target_dimensions(LOCHSTREIFEN *l, int *width, int *height) {
501        int i;
502        // preset the dimensions to 0
503        if(width  != NULL)  *width = 0;
504        if(height != NULL)  *height = 0;
505        // define the "bounding box" where the papertape resides in
506        // user space cordinates (like lochstreifen_draw paints)
507        double x[] = { 0, LOCHSTREIFEN_LENGTH,                  0, LOCHSTREIFEN_LENGTH };
508        double y[] = { 0,                   0, LOCHSTREIFEN_WIDTH, LOCHSTREIFEN_WIDTH  };
509        // transform the "bounding box" throught the matrix
510        for(i=0; i < (sizeof(x) / sizeof(double)); i++) {
511                cairo_matrix_transform_point(&l->matrix, &x[i], &y[i]);
512                // store biggest x/y values
513                if(width != NULL && x[i] > *width)
514                        *width = x[i];
515                if(height != NULL && y[i] > *height)
516                        *height = y[i];
517        }
518}
519
520/**
521 * Get width of the output picture. This is only a nicer frontend to
522 * lochstreifen_get_target_dimensions that returns the width value.
523 **/
524int lochstreifen_get_target_width(LOCHSTREIFEN *l) {
525        int width;
526        lochstreifen_get_target_dimensions(l, &width, NULL);
527        return width;
528}
529
530/**
531 * Get height of the output picture. This is only a nicer frontend to
532 * lochstreifen_get_target_dimensions that returns the height value.
533 **/
534int lochstreifen_get_target_height(LOCHSTREIFEN *l) {
535        int height;
536        lochstreifen_get_target_dimensions(l, NULL, &height);
537        return height;
538}
539
540/**
541 * Get a bit tuple from the positions on the target surface. This function
542 * translates the points x/y with the inverse current translation matrix. Then
543 * it calculates the row number that equals with the x coordinate and afterwards
544 * the track number that equals with the y coordinate.
545 *
546 * If you need only one dimension, set the other NULL.
547 *
548 * You can use lochstreifen_get_traget_row_from_point and
549 * lochstreifen_get_target_track_from_point if you need only one dimension. Furthermore
550 * they will nicely return the requested data -- no need for call by reference.
551 *
552 * @param bit The target lochstreifen_bit_t (call by reference). Should not be NULL.
553 * @param x  The x coordinate you want to check
554 * @param y  The y coordinate you want to check
555 **/
556void lochstreifen_get_target_bit_from_point(LOCHSTREIFEN* l, row_t* row, track_t* track, double x, double y) {
557        cairo_matrix_transform_point(&l->matrix_inverse, &x, &y); // transform points
558       
559        if(y < 0 || y > LOCHSTREIFEN_WIDTH) {
560                // we are not at data, at all
561                if(row != NULL)
562                        *row = LOCHSTREIFEN_NO_ROW;
563                if(track != NULL)
564                        *track = LOCHSTREIFEN_NO_TRACK;
565                return;
566        }
567       
568        if(row == NULL) {
569                // if only the track was requested, create a temporary row buffer, because
570                // we need the row number as a basis for the correct track calculation.
571                row_t row_buffer;
572                row = &row_buffer;
573        }
574       
575        // Row calculation is fairly easy:
576        if(x < LOCHSTREIFEN_TEAR_OFF_LENGTH || x > (LOCHSTREIFEN_LENGTH - LOCHSTREIFEN_TEAR_OFF_LENGTH)) {
577                // we are not at data
578                *row = LOCHSTREIFEN_NO_ROW;
579        } else {
580                // getting the row from the x value. This is pretty much the same algorithm
581                // like used in papertape_draw for checking up the rectangle bounding box (clipping).
582                *row = floor(
583                        (x - LOCHSTREIFEN_TEAR_OFF_LENGTH) / LOCHSTREIFEN_ROW_DISTANCE
584                );
585        }
586       
587        // Track calculation is much more complex:
588        if(track != NULL) {
589                /* get the track from the y value. This is not simply something like
590                 *   *track = floor( y / LOCHSTREIFEN_TRACK_DISTANCE );
591                 * because we must take care that a track is a round thing with a
592                 * specific height.
593                 */
594                double M_x, M_y; // the centers of the circles
595                int i; // some iterator
596                // The center of this circle, fairly complex to calculate, see lochstreifen_draw.
597                M_x = LOCHSTREIFEN_TEAR_OFF_LENGTH + (*row + 0.5) * LOCHSTREIFEN_ROW_DISTANCE;
598                M_y = LOCHSTREIFEN_TRACK_DISTANCE; // the first track, to get onto papertape
599                // this a genious canidate for loop unrolling:
600                for(i=0; i < 8; i++) {
601                        // simple linear algebra: Check if point is in the circle.
602                        //fprintf(stderr, "bit %d: (%f|%f) <-> P(%f|%f) <= %f\n", i, M_x, M_y, x, y, LOCHSTREIFEN_RADIUS_CODE_HOLE);
603                        if(hypot( (x-M_x) , (y-M_y) ) <= LOCHSTREIFEN_RADIUS_CODE_HOLE) {
604                                // the point is in this circle, so we have found the correct track.
605                                //fprintf(stderr, "FOUND %d\n", i);
606                                *track = i;
607                                return;
608                        } else {
609                                // go on, set M for next circle
610                                M_y += LOCHSTREIFEN_TRACK_DISTANCE;
611                                // jump over feed track before i == 3.
612                                if(i == 2)
613                                        M_y += LOCHSTREIFEN_TRACK_DISTANCE;
614                        }
615                } // for tracks
616               
617                // cursor not over circle:
618                *track = LOCHSTREIFEN_NO_TRACK;
619                //fprintf(stderr, "NOT FOUND TRACK.\n");
620        } // y dimension
621} // lochstreifen_get_target_bit_on_point
622
623/**
624 * Get the row number from an x/y target coordinate. That is: At first, translate
625 * the points with the current translation matrix, afterwards calculate the row
626 * number for the position.
627 *
628 * @returns Row number, counting from zero. Negative value if point not on papertape data chunk.
629 **/
630row_t lochstreifen_get_target_row_from_point(LOCHSTREIFEN* l, double x, double y) {
631        row_t row;
632        lochstreifen_get_target_bit_from_point(l, &row, NULL, x, y);
633        return row;
634}
635
636track_t lochstreifen_get_target_track_from_point(LOCHSTREIFEN* l, double x, double y) {
637        track_t track;
638        lochstreifen_get_target_bit_from_point(l, NULL, &track, x, y);
639        return track;
640}
641
642
643/**
644 * Calculate the rectangle that contains the complete row on the LOCHSTREIFEN,
645 * in target surface dimensions (using the transformation matrix). If the operation
646 * fails (if the row doesn't exist), the rect is not touched at all.
647 *
648 * @param row Number of Row you want
649 * @param rect Call by reference like rectangle type
650 * @return LOCHSTREIFEN_SUCCESS or LOCHSTREIFEN_NO_ROW.
651 **/
652int lochstreifen_get_target_rect_from_row(LOCHSTREIFEN* l, row_t row, cairo_rectangle_t* rect) {
653        // check wheter we are in bounds
654        if(row < 0 || row > l->data_length)
655                return LOCHSTREIFEN_NO_ROW;
656
657        // This rectangle is a bit bigger than the real track, so that outlines, etc.
658        // could be drawn there, too.
659       
660        rect->x = LOCHSTREIFEN_TEAR_OFF_LENGTH + (row) * LOCHSTREIFEN_ROW_DISTANCE;
661        rect->y = 0;
662        rect->width = LOCHSTREIFEN_ROW_DISTANCE;
663        rect->height = LOCHSTREIFEN_WIDTH;
664       
665        // take this as padding:
666        #define TARGET_RECT_HOW_MUCH_BIGGER  (LOCHSTREIFEN_ROW_DISTANCE * 0.1)
667        rect->x -= TARGET_RECT_HOW_MUCH_BIGGER;
668        rect->y -= TARGET_RECT_HOW_MUCH_BIGGER;
669        rect->width += 2*TARGET_RECT_HOW_MUCH_BIGGER;
670        rect->height += 2*TARGET_RECT_HOW_MUCH_BIGGER;
671       
672        // transform the points
673        cairo_matrix_transform_point(&l->matrix, &rect->x, &rect->y);
674        cairo_matrix_transform_distance(&l->matrix, &rect->width, &rect->height);
675       
676        return LOCHSTREIFEN_SUCCESS;
677}
678
679/**
680 * This is just the same like lochstreifen_get_target_rect_from_row, just with
681 * bits. This is still a rectangle which contains the complete circle that represents
682 * the bit, useful e.g. for GDK expose events.
683 *
684 * @param row Number of Row
685 * @param track Number of Track
686 * @param rect Call by reference like rectangle type
687 * @return LOCHSTREIFEN_SUCCESS or LOCHSTREIFEN_NO_ROW or LOCHSTREIFEN_NO_TRACK
688 **/
689int lochstreifen_get_target_rect_from_bit(LOCHSTREIFEN* l, row_t row, track_t track, cairo_rectangle_t* rect) {
690        // check bounds
691        if(row < 0 || row > l->data_length)
692                return LOCHSTREIFEN_NO_ROW;
693        if(track < 0 || track > 7)
694                return LOCHSTREIFEN_NO_TRACK;
695
696        // the rectangle is doesn't touch the circle, but leaves quite a bit space around
697        // it, so outlines, etc. should not be a problem.
698
699        // this code is quite like the beginning x/y dimensions in lochstreifen_draw():
700        rect->x = LOCHSTREIFEN_TEAR_OFF_LENGTH + (row) * LOCHSTREIFEN_ROW_DISTANCE;
701                  //+ (LOCHSTREIFEN_ROW_DISTANCE - LOCHSTREIFEN_RADIUS_CODE_HOLE*2) / 2; // the center
702        rect->y = LOCHSTREIFEN_TRACK_DISTANCE * (track + (track > 2 ? 1 : 0) );
703        // The correct values for width/height would be:
704        //    rect->width = LOCHSTREIFEN_RADIUS_CODE_HOLE * 2;
705        // but that won't at exposures if the hole is painted e.g. with an outline (cairo_stroke).
706        // We therefore simply take the complete track as width
707        rect->width = LOCHSTREIFEN_ROW_DISTANCE;
708        rect->height = LOCHSTREIFEN_TRACK_DISTANCE * 2;
709       
710        // transform to user space
711        cairo_matrix_transform_point(&l->matrix, &rect->x, &rect->y);
712        cairo_matrix_transform_distance(&l->matrix, &rect->width, &rect->height);
713       
714        return LOCHSTREIFEN_SUCCESS;
715}
716
717/**
718 *
719 * Just to be sure that there are no misunderstandings: Rotation (wiht cairo)
720 * principally works like this:
721 *
722 *              |  B----D
723 *                 | V  |
724 *    180°      |  | V  |
725 * D--------C      | V  | 270°
726 * |>>>>>>>>|   |  | V  |
727 * B--------A      A----C
728 *              |
729 *   - - - - - -+------------------------>   x axis
730 *              |                    .
731 *      C----A  |  A------------B    .
732 *      | ^  |  |  |<<<<<<<<<<<<|    .
733 *  90° | ^  |  |  C------------D    .
734 *      | ^  |  |     original       .  <- borders of the target surface
735 *      | ^  |  |....................+
736 *      D----B  |
737 *              V
738 *           y axis
739 *
740 * Unfortunately every rotated box would be outside of the target surface
741 * (e.g. an 200x100px PNG image), thus we must move the matrix inside the
742 * positive x/y axis intercept.
743 * This can be performed quite simple
744 *
745 **/
746void lochstreifen_set_rotation(LOCHSTREIFEN *l, enum lochstreifen_direction direction) {
747        switch(direction) {
748                case LOCHSTREIFEN_MOVEMENT_TO_LEFT:
749                        // standard
750                case LOCHSTREIFEN_MOVEMENT_TO_TOP:
751                        //cairo_matrix_rotate();
752                case LOCHSTREIFEN_MOVEMENT_TO_RIGHT:
753                       
754                case LOCHSTREIFEN_MOVEMENT_TO_BOTTOM:
755               
756                case LOCHSTREIFEN_MOVEMENT_NONE:
757                default:
758                        break;
759        }
760        lochstreifen_check_matrix(l);
761}
762
763
764/**
765 * The very central main function for the LOCHSTREIFEN object: Drawing.
766 * You are supposed to create an adequate cairo context.
767 *
768 * This basic drawing function will draw relative to the width of the
769 * paper tape, like defined as 1 inch in the ECMA-10 standard. This
770 * width will have the value "1".
771 *
772 * Your cropping/... blub shall treat hereby. blabla.
773 **/
774void lochstreifen_draw(LOCHSTREIFEN *l, cairo_t *cr) {
775        // complete length of paper tape:
776        double length = LOCHSTREIFEN_LENGTH;
777
778        // iterators for the byte/bit loops
779        int row,track;
780        // position while iterating
781        double x,y;
782        // max positions to iterate to
783        int row_max, track_min, track_max;
784
785        // apply the matrix!
786        cairo_set_matrix(cr, &l->matrix);
787       
788        // check if we need to repaint the papertape
789        if(l->clip != NULL && (l->clip->y > LOCHSTREIFEN_WIDTH || l->clip->x > LOCHSTREIFEN_LENGTH) ) {
790                // Clipping region is outside of papertape!
791                //printf("draw: Outside papertape.\n");
792                return;
793        }
794
795        // paint outer background color.
796        if(l->outer_background_color != NULL) {
797                cairo_set_source(cr, l->outer_background_color);
798                cairo_paint(cr);
799        }
800
801        // paint the paper tape outline, including the tear-off
802        if(l->papertape_background_color != NULL) {
803                cairo_set_source(cr, l->papertape_background_color);
804
805                // tear-off at the beginning (0.5 of paper tape width)
806                cairo_move_to(cr, LOCHSTREIFEN_TEAR_OFF_LENGTH * 0.8, 0);
807                cairo_line_to(cr, 0, 4*LOCHSTREIFEN_TRACK_DISTANCE);
808                cairo_line_to(cr, LOCHSTREIFEN_TEAR_OFF_LENGTH, LOCHSTREIFEN_WIDTH);
809                cairo_line_to(cr, LOCHSTREIFEN_TEAR_OFF_LENGTH, 0);
810                cairo_close_path(cr);
811                cairo_fill(cr);
812               
813                // long rectangular area over complete paper tape
814                if(l->clip == NULL) {
815                        cairo_rectangle(cr, LOCHSTREIFEN_TEAR_OFF_LENGTH, 0.0,
816                                length - 2*LOCHSTREIFEN_TEAR_OFF_LENGTH, LOCHSTREIFEN_WIDTH);
817                        // irc.gnome.org, #gtk+:
818                        // <owen> sven__: a basic comment is that if you draw a *single* shape with cairo
819                        // that's bigger than 32k pixels in any dimension, it's unlikely to work
820                        // [17:51] <owen> sven__: for the Xlib backend. this is a lot more fixable now
821                        // that Cairo switched to 24.8 fixed point internally, but I don't think anybody
822                        // has done the work to fix it
823                } else {
824                        // calculate the neccessary width
825                        // if clipping region exceeds lochstreifen width.... make it shorter, else let it.
826                        double width = ( (l->clip->x + l->clip->width) > (LOCHSTREIFEN_LENGTH - LOCHSTREIFEN_TEAR_OFF_LENGTH) )
827                                        ? ( LOCHSTREIFEN_LENGTH - LOCHSTREIFEN_TEAR_OFF_LENGTH - l->clip->x )
828                                        : l->clip->width;
829                        // don't have the clipping region paint in the tear off
830                        // right, so crop it at the left.
831                        if(l->clip->x < LOCHSTREIFEN_TEAR_OFF_LENGTH)
832                                width -= LOCHSTREIFEN_TEAR_OFF_LENGTH;
833                        cairo_rectangle(cr,
834                                (l->clip->x >= LOCHSTREIFEN_TEAR_OFF_LENGTH) ? 
835                                        l->clip->x : LOCHSTREIFEN_TEAR_OFF_LENGTH,
836                                0,
837                                width,
838                                LOCHSTREIFEN_WIDTH);
839                }
840                cairo_fill(cr);
841               
842
843                // tear-off at the end (0.5 of paper tape width)
844                cairo_move_to(cr, length-LOCHSTREIFEN_TEAR_OFF_LENGTH, 0);
845                cairo_line_to(cr, length-LOCHSTREIFEN_TEAR_OFF_LENGTH, LOCHSTREIFEN_WIDTH);
846                cairo_line_to(cr, length, LOCHSTREIFEN_WIDTH);
847                cairo_line_to(cr, length-LOCHSTREIFEN_TEAR_OFF_LENGTH, 4*LOCHSTREIFEN_TRACK_DISTANCE);
848                cairo_line_to(cr, length-LOCHSTREIFEN_TEAR_OFF_LENGTH * 0.2, 0);
849                cairo_close_path(cr);
850                cairo_fill(cr);
851               
852        } // paper tape background finished.
853       
854        // paint feed holes in the tear-off at the beginning
855        // only if in clipped area
856        if( ( l->clip == NULL || (l->clip->x <= LOCHSTREIFEN_TEAR_OFF_LENGTH) )
857         && (l->feed_hole_color != NULL) ) {
858                cairo_set_source(cr, l->feed_hole_color);
859                for(y=4*LOCHSTREIFEN_TRACK_DISTANCE,
860                    x=LOCHSTREIFEN_TEAR_OFF_LENGTH - LOCHSTREIFEN_ROW_DISTANCE/2;
861                    x > 0; x -= LOCHSTREIFEN_ROW_DISTANCE)
862                        cairo_arc(cr, x, y, LOCHSTREIFEN_RADIUS_FEED_HOLE, 0., 2*M_PI);
863                cairo_fill(cr);
864        }
865
866       
867        if(l->clip != NULL) {
868                /* calculate clipping in dimension X (rows, bytes).
869                 * Clipping in this dimension is very important, especially at very long
870                 * papertapes. Application software (like GtkPaperTape) usually zooms the
871                 * papertape, so it won't fit completely on the screen. So skipping bytes
872                 * which are not displayed is very performant.
873                 */
874                if(l->clip->x < LOCHSTREIFEN_TEAR_OFF_LENGTH) {
875                        // begin before tear off
876                        row = 0;
877                        row_max = floor(
878                                ( l->clip->width - (l->clip->x - LOCHSTREIFEN_TEAR_OFF_LENGTH) )
879                                / LOCHSTREIFEN_ROW_DISTANCE
880                        );
881                } else {
882                        // after tear off
883                        row = floor (
884                                (l->clip->x - LOCHSTREIFEN_TEAR_OFF_LENGTH) / LOCHSTREIFEN_ROW_DISTANCE
885                        );
886                       
887                        // end with just one more row as neccessary
888                        row_max = row + 1 + ceil(
889                                ( l->clip->width ) / LOCHSTREIFEN_ROW_DISTANCE
890                        );
891                }
892               
893                // don't go over the data range
894                if(row_max > l->data_length)
895                        row_max = l->data_length;
896                // dont' have row_max smaller than row
897                //if(row_max < row)
898                //      row_max = row;
899
900                /* calculate clipping in dimension Y (tracks, bits)
901                 * Y clipping is not as important, because there are only 9 circles
902                 * to paint. The feed hole (which is painted just in the loop for the
903                 * 3. track hole is not counted, therefore the calculations become somewhat
904                 * inneccessary complex, compared to the painting overhead for the complete
905                 * 8 tracks instead of e.g. only 6 tracks. Apart from that, application software
906                 * usually doesn't clip the papertape this way, because users usually want to
907                 * see a complete byte and not only a half byte.
908                 */
909                // calculation of first track to begin with.
910                /*
911                track_min = l->clip->y == 0 ? 0 : floor( l->clip->y / LOCHSTREIFEN_TRACK_DISTANCE );
912               
913               
914                // calculation of last track to end with.
915                if(l->clip->y + l->clip->height > LOCHSTREIFEN_WIDTH)
916                        track_max = 8; // clipping region is *much* bigger than the papertape width
917                else {
918                        // just paint one more than neccessary
919                        track_max = 1 + 8 - floor(
920                                (LOCHSTREIFEN_WIDTH - (l->clip->y + l->clip->height))
921                                / LOCHSTREIFEN_TRACK_DISTANCE
922                        );
923                        // dont't go over the track range
924                        if(track_max > 8)
925                                track_max = 8;
926                }*/
927                track_min = 0;
928                track_max = 8;
929        } else {
930                // no clipping. Paint *everything*.
931                row = 0;
932                row_max = l->data_length;
933               
934                track_min = 0;
935                track_max = 8;
936        }
937       
938        if(l->clip != NULL) {
939                double x,y,width,height;
940                x=l->clip->x; y=l->clip->y; width=l->clip->width; height=l->clip->height;
941                cairo_user_to_device(cr, &x, &y);
942                cairo_user_to_device_distance(cr, &width, &height);
943                printf("draw: %i|%i %ix%i ", (int)x, (int)y, (int)width, (int)height);
944        } else    printf("draw: ");
945        printf("row %i->%i track %i->%i\n", row, row_max, track_min, track_max);
946       
947        // loop all the rows (bytes)
948        // x start: tear off + one half row offset + offset rows * length of row
949        for(x=LOCHSTREIFEN_TEAR_OFF_LENGTH + LOCHSTREIFEN_ROW_DISTANCE/2 + row*LOCHSTREIFEN_ROW_DISTANCE;
950            row < row_max; row++, x += LOCHSTREIFEN_ROW_DISTANCE) {
951
952                // call row callback
953                if(l->row_callback != NULL) {
954                        (*l->row_callback) (&row, cr, l->row_callback_user_data);
955                        // at least now that's all the magic ;)
956                }
957               
958                // highlight region?
959                if(l->highlight_region_color != NULL && l->highlight_region_start != LOCHSTREIFEN_NO_ROW &&
960                    (row >= l->highlight_region_start || row < l->highlight_region_end) ) {
961                        cairo_set_source(cr, l->highlight_region_color);
962                        // hier was schlaues schickeres als ein RECHTECK
963                        // einfallen lassen. Vielleicht mit abgerundetem
964                        // Rechteck? Wuerde zu den Löchern passen.
965                        // http://www.cairographics.org/cookbook/roundedrectangles/
966                        cairo_rectangle(cr, x - LOCHSTREIFEN_ROW_DISTANCE / 2, 0,
967                           LOCHSTREIFEN_TRACK_DISTANCE * (l->highlight_region_end - l->highlight_region_start),
968                           LOCHSTREIFEN_WIDTH);
969                        cairo_fill(cr);
970                }
971               
972                // highlight (only) this row?
973                if(l->highlight_row_color != NULL && row == l->highlight_row_number) {
974                        // if check again LOCHSTREIFEN_NO_ROW not neccessary because row iterator never
975                        // gets LOCHSTREIFEN_NO_ROW.
976                        cairo_set_source(cr, l->highlight_row_color);
977                        // genauso abgerundetes Rechteck wie bei der region
978                        // überlegen.
979                        cairo_rectangle(cr, x - LOCHSTREIFEN_ROW_DISTANCE/2, 0,
980                            LOCHSTREIFEN_TRACK_DISTANCE, LOCHSTREIFEN_WIDTH);
981                        cairo_fill(cr);
982                }
983               
984                // loop all the tracks (bits)
985                // y start: offset tracks * width of track + one track (to get onto the papertape)
986                for(track=track_min, y = LOCHSTREIFEN_TRACK_DISTANCE*(track+1);
987                    track < track_max; track++, y += LOCHSTREIFEN_TRACK_DISTANCE) {
988                        if(track == 3) {
989                                // the feed hole is at position 3
990                                if(l->feed_hole_color != NULL) {
991                                        // we paint a feed hole :-)
992                                        cairo_set_source(cr, l->feed_hole_color);
993                                        cairo_arc(cr, x, y, LOCHSTREIFEN_RADIUS_FEED_HOLE, 0., 2*M_PI);
994                                        cairo_fill(cr);
995                                }
996                                // jump one track, in every case.
997                                y += 0.1;
998                        }
999                       
1000                        if( ((l->data[row] >> track) & 0x01) != 0x01) {
1001                                // bit is logical ZERO (0)
1002                                if(l->zero_code_hole_color != NULL) {
1003                                        // we want to paint zeros.
1004                                        cairo_set_source(cr, l->zero_code_hole_color);
1005                                        cairo_arc(cr, x, y, LOCHSTREIFEN_RADIUS_CODE_HOLE, 0., 2*M_PI);
1006                                        cairo_fill(cr);
1007                                }
1008                        } else if(l->one_code_hole_color != NULL) {
1009                                // we want to paint logical ONEs.
1010                                cairo_set_source(cr, l->one_code_hole_color);
1011                                cairo_arc(cr, x, y, LOCHSTREIFEN_RADIUS_CODE_HOLE, 0., 2*M_PI);
1012                                cairo_fill(cr);
1013                        }
1014                       
1015                        if(l->highlight_bit_row == row && l->highlight_bit_track == track
1016                           && l->highlight_bit_color != NULL) {
1017                                // if check against l->highlight_bit_row == LOCHSTREIFEN_NO_ROW
1018                                // not neccessary because row never gets LOCHSTREIFEN_NO_ROW.
1019                               
1020                                // eigentlich wollen wir hier eine Umrahmung zeichnen,
1021                                // damit der Zustand (0/1) nicht unsichtbar wird. Jetzt
1022                                // erst mal aber die Billigversion mit der dümmlichen
1023                                // Vollfarbe ;-)
1024                                cairo_set_source(cr, l->highlight_bit_color);
1025                                cairo_arc(cr, x, y, LOCHSTREIFEN_RADIUS_CODE_HOLE, 0., 2*M_PI);
1026                                cairo_set_line_width(cr, 0.01);
1027                                cairo_stroke(cr); // mal testen :-)
1028                        }
1029                } // for tracks (bits)
1030        } // for rows (bytes)
1031} // function lochstreifen_draw
Note: See TracBrowser for help on using the repository browser.
© 2008 - 2013 technikum29 • Sven Köppel • Some rights reserved
Powered by Trac
Expect where otherwise noted, content on this site is licensed under a Creative Commons 3.0 License