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