1 /* draw.c: Drawing functions (continuous stroke) in a hf_struct
2 *
3 * Copyright (C) 2003-2004 Patrice St-Gelais
4 * patrstg@users.sourceforge.net
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21 #include "draw.h"
22 #include "waves.h"
23 #define MULT_SIZE 2
24 #define DIV_SIZE 1
25
pen_new()26 pen_struct *pen_new() {
27 pen_struct *p;
28 p = (pen_struct *) x_malloc(sizeof(pen_struct), "pen_struct");
29 p->size = 83;
30 p->level = 25; // 1% to 100%
31 p->smoothing = SMOOTHING_AUTO;
32 p->spacing = SPACING_HIGH_QUALITY;
33 p->merge = ADD;
34 p->wrap = TILING_AUTO;
35 p->shape = NO_WAVE_SHAPE;
36 p->map = map_new();
37 p->overlap = FALSE;
38 map_init(p->map, p->size, p->level, p->shape, p->spacing) ;
39 return p;
40 }
41
pen_free(pen_struct * ps)42 void pen_free(pen_struct *ps) {
43 if (!ps)
44 return;
45 if (ps->map)
46 map_free(ps->map);
47 x_free(ps);
48 }
49
draw_buf_new(gint size)50 draw_buf *draw_buf_new (gint size) {
51 // Buffer containing a "moving window" of the last pixels drawn
52 // Allows a proper merging of the pixels drawn with the original image.
53 // so that there are no holes nor superimposition when the angles
54 // of successive and connected segments are not the same
55 draw_buf *db;
56 db = (draw_buf *) x_malloc(sizeof(draw_buf), "draw_buf");
57 db->size = size; // Diameter
58 db->data = (hf_type *) x_calloc(db->size * db->size,sizeof(hf_type), "hf_type (db->data in draw_buf_new)");
59 db->tmp = (hf_type *) x_calloc(db->size * db->size,sizeof(hf_type), "hf_type (db->tmp in draw_buf_new)");
60 db->active = FALSE;
61 db->tail = NULL;
62 db->max_tail = 0;
63 db->delayed_dot = FALSE;
64 return db;
65 }
66
draw_buf_init(map_struct * map,gdouble spacing)67 void draw_buf_init (map_struct *map, gdouble spacing) {
68 // Reinitialize the draw buffer to 0 - public method
69 gint i;
70 draw_buf *d=map->dr_buf;
71 d->active = FALSE;
72 for (i=0; i<(d->size*d->size); i++) {
73 *(d->data+i) = (hf_type) 0;
74 }
75 d->max_tail = (gint) (1.5+(1.0 / spacing));
76 // printf("MAX TAIL: %d\n",d->max_tail);
77 d->current_tail = d->max_tail-1; // before the 1st
78 if (d->tail)
79 x_free(d->tail);
80 d->tail = (tail_struct*) x_calloc(d->max_tail,sizeof(tail_struct), "tail_struct (d->tail in draw_buf_init)");
81 d->delayed_dot = FALSE;
82 }
83
draw_buf_reinit(map_struct * map)84 void draw_buf_reinit (map_struct *map) {
85 // Reinitialize the draw buffer to 0 - public method
86 gint i, j, ii;
87 draw_buf *d = map->dr_buf;
88 // printf("RADIUS: %d; SIZE: %d\n",map->radius, d->size);
89 for (i=0; i<d->size; i++)
90 for (j=0; j<d->size; j++) {
91 ii = i + j*d->size;
92 // *(d->data+ii) = 0.7 * *(d->data+ii);
93 *(d->data+ii) = 0;
94 }
95 if (d->tail)
96 x_free(d->tail);
97 d->tail = (tail_struct *) x_calloc(d->max_tail,sizeof(tail_struct), "tail_struct (d->tail in draw_buf_reinit)" );
98 d->current_tail = d->max_tail-1; // before the 1st
99 }
100
101
subtract_older_dot(map_struct * map)102 void subtract_older_dot (map_struct *map) {
103 draw_buf *d;
104 gint older_x, older_y, current_x, current_y;
105 gint map_size = 2*map->radius+1;
106 // printf("Subtract older dot\n");
107 d = map->dr_buf;
108 older_x = (d->tail+(d->current_tail+1)%d->max_tail)->x;
109 older_y = (d->tail+(d->current_tail+1)%d->max_tail)->y;
110 if ((!older_x) && (!older_y))
111 return;
112 current_x = (d->tail+d->current_tail)->x;
113 current_y = (d->tail+d->current_tail)->y;
114
115 generalized_merge( (gpointer) map->data,
116 HF_TYPE_ID,
117 map_size, map_size,
118 d->data, HF_TYPE_ID,
119 d->size, d->size,
120 d->size/2 - current_x + older_x,
121 d->size/2 - current_y + older_y,
122 SUBTRACT,
123 FALSE, // no wrap
124 map->square_symmetry);
125 }
126
translate_draw_buf(map_struct * map,gint new_x,gint new_y,gint shape)127 void translate_draw_buf (map_struct *map, gint new_x, gint new_y, gint shape) {
128 gint dx, dy, x, y, s, sx, sy;
129 gint previous_x, previous_y, previous_previous_x, previous_previous_y;
130 gdouble scal_prod;
131 hf_type *map_to_use;
132 draw_buf *d = map->dr_buf;
133 gint map_size = 2*map->radius+1;
134 dx = (d->tail+d->current_tail)->x - new_x;
135 dy = (d->tail+d->current_tail)->y - new_y;
136
137 previous_x = (d->tail+d->current_tail)->x;
138 previous_y = (d->tail+d->current_tail)->y;
139
140 previous_previous_x = (d->tail+((d->current_tail+d->max_tail-1)%d->max_tail))->x;
141 previous_previous_y = (d->tail+((d->current_tail+d->max_tail-1)%d->max_tail))->y;
142 // The scalar product
143 // is used as an approximation of the curvature
144 // Higher curvature -> drawing is less additive
145
146 scal_prod = NORM_SCALAR_PRODUCT(previous_previous_x, previous_previous_y, previous_x, previous_y, new_x, new_y);
147
148 // printf("SCALAR_PRODUCT: (%d,%d), (%d,%d), (%d,%d), %5.2f, %5.2f; %5.2f;\n",previous_previous_x, previous_previous_y, previous_x, previous_y, new_x, new_y, scal_prod, acos(scal_prod)*180.0/M_PI, acos(scal_prod)*180.0/M_PI);
149
150 // If the angle <= 90 degrees, we start a fresh buffer
151 // with the previous dot (we start a new stroke),
152 // to avoid "overflow" due to overlapping
153 if ((previous_previous_x || previous_previous_y) && (scal_prod>=0.3)) {
154 draw_buf_reinit(map);
155
156 // printf("********* SCAL PROD *********: %5.2f = %5.2f˚\n",scal_prod,acos(scal_prod)*180.0/M_PI);
157
158 // Non-symmetrical pen tips must be rotated
159
160 if (shape != NO_WAVE_SHAPE) {
161 rotate ( (gdouble) dx, (gdouble) dy,
162 map->data, map->tmp,
163 map_size, map_size,
164 HF_TYPE_ID, OVERFLOW_ZERO);
165 map_to_use = map->tmp;
166 }
167 else
168 map_to_use = map->data;
169
170 generalized_merge( (gpointer) map_to_use,
171 HF_TYPE_ID,
172 map_size, map_size,
173 d->data, HF_TYPE_ID,
174 d->size, d->size,
175 d->size / 2, d->size / 2,
176 ADD,
177 FALSE, // no wrap
178 map->square_symmetry);
179 (d->tail+d->current_tail)->x = previous_x;
180 (d->tail+d->current_tail)->y = previous_y;
181
182 }
183 else
184 {
185 subtract_older_dot (map);
186
187 }
188 d->current_tail = (d->current_tail+1)%d->max_tail;
189
190 (d->tail+d->current_tail)->x = new_x;
191 (d->tail+d->current_tail)->y = new_y;
192 (d->tail+d->current_tail)->scal_prod = scal_prod;
193
194 if ((new_x==0) && (new_y==0)) { // We begin a stroke
195 return;
196 }
197
198 s = d->size;
199 memcpy(d->tmp, d->data, s * s * sizeof(hf_type));
200 for (x=0; x<s; x++)
201 for (y=0; y<s; y++) {
202 sx = x-dx;
203 sy = y-dy;
204 // == 0 if out of boundaries
205 if ((sx<0) || (sy<0) || (sx>=s) || (sy>=s)) {
206 *(d->data + x + s*y) =0;
207 }
208 else {
209 *(d->data + x + s*y) = *(d->tmp + sx + s*sy);
210 }
211 }
212 }
213
map_new()214 map_struct *map_new () {
215 // A simple allocator
216 map_struct *ms;
217 ms = (map_struct *) x_malloc(sizeof(map_struct), "map_struct (ms in map_new in draw.c)");
218 // We allocate the map to the max radius*2+1, to allow maps without square symmetry
219 ms->data = NULL;
220 ms->tmp = NULL;
221 ms->map_to_use = NULL;
222 ms->units = NULL;
223 ms->radius = 0;
224 ms->square_symmetry = TRUE;
225 ms->dr_buf = NULL;
226 return ms;
227 }
228
draw_buf_free(draw_buf * dr_buf)229 void draw_buf_free (draw_buf *dr_buf) {
230 if (dr_buf->data)
231 x_free(dr_buf->data);
232 if (dr_buf->tmp)
233 x_free(dr_buf->tmp);
234 if (dr_buf->tail)
235 x_free(dr_buf->tail);
236 x_free(dr_buf);
237 }
238
map_init(map_struct * map,gint size,gint level,gint shape,gdouble spacing)239 void map_init (map_struct *map, gint size,
240 gint level, gint shape, gdouble spacing) {
241
242 gint x,y, radius, ss;
243 gdouble dlevel, offset, ddist, p, maxd = (gdouble) MAX_HF_VALUE;
244 shape_type *shape_data=NULL;
245 radius = size >> 1;
246 // The map size should always be odd
247 size = 2*radius+1;
248 ss = size * size;
249 map->data = (hf_type *) x_realloc (map->data, sizeof(hf_type) * ss, "hf_type (map->data in draw.c)");
250 map->tmp = (hf_type *) x_realloc (map->tmp, sizeof(hf_type) * ss, "hf_type (map->tmp in draw.c)");
251 map->map_to_use = map->data;
252 map->units = (hf_type *) x_realloc (map->units, sizeof(hf_type) * ss, "hf_type (map->units in draw.c)");
253
254 FILL_MAP(map->units,ss,1);
255
256 if (spacing == SPACING_HIGH_QUALITY) {
257 dlevel = LEVEL_HQ_SPACING;
258 }
259 else {
260 if (spacing == SPACING_STANDARD_QUALITY)
261 dlevel = LEVEL_SQ_SPACING;
262 else
263 if (spacing == SPACING_VHIGH_QUALITY)
264 dlevel = LEVEL_VHQ_SPACING;
265 else
266 dlevel = LEVEL_LQ_SPACING;
267 }
268 if (shape != NO_WAVE_SHAPE) {
269 shape_data = shape_type_new(shape, size);
270 map->square_symmetry = FALSE;
271 }
272 else
273 map->square_symmetry = TRUE;
274
275 // Position (0,0) in the map is the minimum value
276 // We "clamp" all the edges to 0
277 // The maximum edge value is at (0,radius) or (radius,0) or (2*radius, radius) or (radius, 2*radius)
278 offset = BELLD(CONST_E,2.0,1.5*DIST2(0,0,0,radius),radius);
279 if (shape_data) { // square_symmetry == FALSE
280 dlevel = ((gdouble) level)*dlevel/(100.0*(gdouble) MAX_HF_VALUE);
281 // We intersect the gaussian bell (values from 0.0 to 1.0) with the pen tip shape
282 for (x=0; x<size; x++)
283 for (y=0; y<size; y++) {
284 // Technique #1: gaussian bell on x axis, shape on y axis
285 // A basic technique, giving, with sharps tips, straight lines when the stroke curves
286 /* *(map->data+VECTORIZE(x,y,size)) = (hf_type)
287 (dlevel * *(shape_data+y) *
288 MAX(0.0, BELLD(e,2.0,1.5*ABS(radius-x),radius) - offset) );
289 */
290 // Technique #2: same as technique #1, but the shape
291 // is emphasized only around the center
292 // Towards the edges, we average it with a gaussian bell
293 // in proportion with the square of the distance
294 // Increasing the base to 10.0 shortens the shape
295 // so that even with sharp tips, straight lines are invisible in stroke curves
296 p = MAX(0.0, BELLD(10.0,2.0,1.5*ABS(radius-x),radius) - offset);
297 *(map->data+VECTORIZE(x,y,size)) = (hf_type) (dlevel *
298 (p * *(shape_data+y) + (1.0-p) * MAX(0.0, BELLD(CONST_E,2.0,1.5*ABS(radius-y),radius) - offset) * maxd ) *
299 MAX(0.0, BELLD(CONST_E,2.0,1.5*ABS(radius-x),radius) - offset) );
300
301 }
302 }
303 else {
304 // Level is relative (50 means 50%)
305 dlevel = ((gdouble) level)*dlevel/100.0;
306 for (x=0; x<(radius+1); x++)
307 for (y=0; y<(radius+1); y++) {
308 ddist = (gdouble) DIST2(0,0,x,y);
309 if (ddist >= radius)
310 *(map->data+VECTORIZE(radius-x,radius-y,radius+1)) = 0;
311 else
312 *(map->data+VECTORIZE(radius-x,radius-y,radius+1)) =
313 (hf_type) (dlevel * MAX(0.0, BELLD(CONST_E,2.0,1.5*ddist,radius) - offset) );
314 // 0x04FF; // TEST
315 }
316 }
317 map->radius = radius;
318 if (map->dr_buf)
319 draw_buf_free(map->dr_buf);
320 map->dr_buf = draw_buf_new (MULT_SIZE*size/DIV_SIZE);
321 draw_buf_init (map, spacing);
322 if (shape_data)
323 free(shape_data);
324 }
325
map_free(map_struct * map)326 void map_free (map_struct *map) {
327 if (!map)
328 return;
329 if (map->data) {
330 x_free(map->data);
331 map->data = NULL;
332 }
333 if (map->tmp) {
334 x_free(map->tmp);
335 map->tmp = NULL;
336 }
337 if (map->units) {
338 x_free(map->units);
339 map->units = NULL;
340 }
341 map->radius = 0;
342 if (map->dr_buf) {
343 draw_buf_free(map->dr_buf);
344 map->dr_buf = NULL;
345 }
346 }
347
draw_dot(hf_struct_type * hf,pen_struct * pen,gint x,gint y,gdouble ** gauss_list)348 void draw_dot (hf_struct_type *hf, pen_struct *pen, gint x, gint y, gdouble **gauss_list) {
349 // Drawing a unique dot, usually for drawing the first or the last dot of a stroke
350 // The remainder of the stroke is drawn with draw_continuous_line_by_dot
351 gboolean wrap;
352 draw_buf *d;
353 gint map_size, i, j, ii;
354 map_struct *map;
355 map = pen->map;
356 d = map->dr_buf;
357 map_size = 2*map->radius+1;
358 if (hf->if_tiles==NULL)
359 wrap = (pen->wrap==TILING_YES) || (pen->wrap==TILING_AUTO);
360 else
361 wrap = (pen->wrap==TILING_YES) ||
362 ((pen->wrap==TILING_AUTO) && *hf->if_tiles);
363
364 if (pen->merge == SMOOTH_OP)
365 map_convolve (map->map_to_use, map_size, map_size,
366 hf->hf_buf, hf->max_x, hf->max_y, x, y,
367 wrap, 100, gauss_list, TRUE);
368 else {
369 if (pen->overlap) {
370 generalized_merge(
371 (gpointer) map->map_to_use, HF_TYPE_ID,
372 2*map->radius+1, 2*map->radius+1,
373 hf->hf_buf, HF_TYPE_ID,
374 hf->max_x, hf->max_y, x, y,
375 pen->merge,
376 wrap,
377 map->square_symmetry);
378 }
379 else {
380 // 1. Translate the drawing buffer "under" the
381 // current map (pen tip)
382 // 2. Write in the drawing buffer
383 // 3. Write the drawing buffer in the layer buffer
384
385 translate_draw_buf (map, x, y, pen->shape);
386
387 generalized_merge( (gpointer) map->map_to_use,
388 HF_TYPE_ID,
389 map_size, map_size,
390 d->data, HF_TYPE_ID,
391 d->size, d->size,
392 d->size/2, d->size/2,
393 // map->radius, map->radius,
394 ADD,
395 FALSE, // no wrap
396 map->square_symmetry);
397
398 generalized_merge( (gpointer) d->data,
399 HF_TYPE_ID,
400 d->size, d->size,
401 hf->layer_buf, HF_TYPE_ID,
402 hf->max_x, hf->max_y,
403 x, y,
404 MAX_MERGE,
405 wrap,
406 FALSE ); // square_symmetry
407 }
408 }
409 }
410
draw_one_dot(hf_struct_type * hf,pen_struct * pen,gint x,gint y,gdouble ** gauss_list)411 void draw_one_dot (hf_struct_type *hf, pen_struct *pen, gint x, gint y, gdouble **gauss_list) {
412 // Drawing a unique dot, usually for drawing the first or the last dot of a stroke
413 // The remainder of the stroke is drawn with draw_continuous_line_by_dot
414
415 draw_buf *d = pen->map->dr_buf;
416
417 d->current_tail = (d->current_tail+1)%d->max_tail;
418
419 (d->tail+d->current_tail)->x = x;
420 (d->tail+d->current_tail)->y = y;
421
422 pen->map->map_to_use = pen->map->data;
423
424 // With a non-symmetrical pen tip, we postpone the first dot,
425 // so that we can draw it with the right angle (given by 1st-2nd dots)
426 if (pen->shape != NO_WAVE_SHAPE) {
427 pen->map->dr_buf->delayed_dot = TRUE;
428 return;
429 }
430 else
431 draw_dot (hf, pen, x, y, gauss_list);
432 }
433
draw_continuous_line_by_dot(hf_struct_type * hf,pen_struct * pen,gdouble begin_x,gdouble begin_y,gdouble * end_x_ptr,gdouble * end_y_ptr,gboolean draw_end,gdouble ** gauss_list)434 gboolean draw_continuous_line_by_dot (hf_struct_type *hf, pen_struct *pen,
435 gdouble begin_x, gdouble begin_y,
436 gdouble *end_x_ptr, gdouble *end_y_ptr,
437 gboolean draw_end, gdouble **gauss_list) {
438
439 // Same concept as draw_hf_line in draw_hf.c
440 // Draw in real coordinates (interpolated from (gdouble))
441 // Instead of drawing a HF, we draw a gaussian map, whose size is more versatile
442 // We smooth progressively what is drawn
443 // Return TRUE if something has been drawn, FALSE otherwise
444
445 gint h, steps, map_size, i, j, ii;
446 gdouble spacing, dist, dx, dy, end_x, end_y;
447 gboolean wrap;
448 map_struct *map;
449 draw_buf *d;
450
451 map = pen->map;
452 d = map->dr_buf;
453
454 // We force the map size to be odd
455 map_size = 2*map->radius+1;
456
457 end_x = *end_x_ptr;
458 end_y = *end_y_ptr;
459
460 if (hf->if_tiles==NULL)
461 wrap = (pen->wrap==TILING_YES) || (pen->wrap==TILING_AUTO);
462 else
463 wrap = (pen->wrap==TILING_YES) ||
464 ((pen->wrap==TILING_AUTO) && *hf->if_tiles);
465
466 // Radius is 0 for a size of 1, 1 for a size of 3... up to 127 for a size of 255
467 // Size should always be odd
468
469 // Draw each dot from the last x,y
470 // Won't draw the last (just before the mouse release)
471 // spacing is the increment applied, in pixels, each time we draw a dot
472
473 spacing = pen->spacing * (gdouble) map_size;
474
475 dist = (gdouble) DIST2((gdouble) begin_x,
476 (gdouble) begin_y,
477 (gdouble) end_x, (gdouble) end_y);
478
479 dx = spacing * ((gdouble) (end_x - begin_x)) / dist;
480 dy = spacing * ((gdouble) (end_y - begin_y)) /dist ;
481
482 steps = (gint) floor(dist/spacing);
483 if (!steps) {
484 // We return FALSE when there is nothing to draw,
485 // so that the (begin_x, begin_y) coordinates are not changed
486 // for the next line to draw
487 return FALSE;
488 }
489
490 if (pen->shape != NO_WAVE_SHAPE) {
491 rotate ( (gdouble) dx, (gdouble) dy,
492 map->data, map->tmp,
493 map_size, map_size,
494 HF_TYPE_ID, OVERFLOW_ZERO);
495 map->map_to_use = map->tmp;
496 }
497 else
498 map->map_to_use = map->data;
499
500 if (d->delayed_dot) {
501 draw_dot (hf, pen, (d->tail+d->current_tail)->x, (d->tail+d->current_tail)->y, gauss_list);
502 d->delayed_dot = FALSE;
503 }
504
505 // printf("DX: %5.2f; DY: %5.2f; shape: %d; symm: %d\n",dx,dy,pen->shape, pen->map->square_symmetry);
506 for (h=1; h<=steps; h++) {
507 end_x = begin_x + dx * (gdouble) h;
508 end_y = begin_y + dy * (gdouble) h;
509 if (pen->merge == SMOOTH_OP)
510 map_convolve (map->map_to_use,
511 map_size, map_size,
512 hf->hf_buf, hf->max_x, hf->max_y,
513 (gint) end_x, (gint) end_y,
514 wrap, 100, gauss_list, TRUE);
515 else {
516
517 if (pen->overlap) {
518
519 generalized_merge(
520 (gpointer) map->map_to_use, HF_TYPE_ID,
521 map_size, map_size,
522 hf->hf_buf, HF_TYPE_ID,
523 hf->max_x, hf->max_y,
524 (gint) end_x, (gint) end_y,
525 pen->merge,
526 wrap,
527 map->square_symmetry);
528 }
529 else {
530 // 1. Translate the drawing buffer "under" the
531 // current map (pen tip)
532 // 2. Write in the drawing buffer
533 // 3. Write the drawing buffer in the layer buffer
534
535 translate_draw_buf (map, (gint) end_x,
536 (gint) end_y, pen->shape);
537
538 generalized_merge( (gpointer) map->map_to_use,
539 HF_TYPE_ID,
540 map_size, map_size,
541 d->data, HF_TYPE_ID,
542 d->size, d->size,
543 d->size/2, d->size/2,
544 ADD,
545 FALSE, // no wrap
546 pen->map->square_symmetry);
547
548 generalized_merge( (gpointer) map->dr_buf->data,
549 HF_TYPE_ID,
550 d->size, d->size,
551 hf->layer_buf, HF_TYPE_ID,
552 hf->max_x, hf->max_y,
553 (gint) end_x, (gint) end_y,
554 MAX_MERGE,
555 wrap,
556 FALSE ); // square_symmetry
557 }
558 }
559 }
560
561 // The last dot drawn doesn't fall on the coordinates where the motion motify event was emitted,
562 // so we must adjust these coordinates
563 (*end_x_ptr) = end_x;
564 (*end_y_ptr) = end_y;
565
566 return TRUE;
567 }
568