2 libgstroke - a GNOME stroke interface library
3 Copyright (c) 1996,1997,1998,1999,2000,2001 Mark F. Willey, ETLA Technical
5 See the file COPYING for distribution information.
7 This file contains the stroke recognition algorithm.
19 #include "gstroke-internal.h"
23 _gstroke_init (struct gstroke_metrics
*metrics
)
25 if (metrics
->pointList
!= NULL
) {
26 g_slist_free_full(metrics
->pointList
, g_free
);
27 metrics
->pointList
= NULL
;
28 metrics
->point_count
= 0;
32 /* figure out which bin the point falls in */
34 _gstroke_bin (p_point point_p
, gint bound_x_1
, gint bound_x_2
,
35 gint bound_y_1
, gint bound_y_2
)
40 if (point_p
->x
> bound_x_1
) bin_num
+= 1;
41 if (point_p
->x
> bound_x_2
) bin_num
+= 1;
42 if (point_p
->y
> bound_y_1
) bin_num
+= 3;
43 if (point_p
->y
> bound_y_2
) bin_num
+= 3;
49 _gstroke_trans (gchar
*sequence
, struct gstroke_metrics
*metrics
)
52 /* number of bins recorded in the stroke */
53 guint sequence_count
= 0;
55 /* points-->sequence translation scratch variables */
60 /* flag indicating the start of a stroke - always count it in the sequence */
61 gint first_bin
= TRUE
;
63 /* bin boundary and size variables */
64 gint delta_x
, delta_y
;
65 gint bound_x_1
, bound_x_2
;
66 gint bound_y_1
, bound_y_2
;
69 /* determine size of grid */
70 delta_x
= metrics
->max_x
- metrics
->min_x
;
71 delta_y
= metrics
->max_y
- metrics
->min_y
;
73 /* calculate bin boundary positions */
74 bound_x_1
= metrics
->min_x
+ (delta_x
/ 3);
75 bound_x_2
= metrics
->min_x
+ 2 * (delta_x
/ 3);
77 bound_y_1
= metrics
->min_y
+ (delta_y
/ 3);
78 bound_y_2
= metrics
->min_y
+ 2 * (delta_y
/ 3);
80 if (delta_x
> GSTROKE_SCALE_RATIO
* delta_y
) {
81 bound_y_1
= (metrics
->max_y
+ metrics
->min_y
- delta_x
) / 2 + (delta_x
/ 3);
82 bound_y_2
= (metrics
->max_y
+ metrics
->min_y
- delta_x
) / 2 + 2 * (delta_x
/ 3);
83 } else if (delta_y
> GSTROKE_SCALE_RATIO
* delta_x
) {
84 bound_x_1
= (metrics
->max_x
+ metrics
->min_x
- delta_y
) / 2 + (delta_y
/ 3);
85 bound_x_2
= (metrics
->max_x
+ metrics
->min_x
- delta_y
) / 2 + 2 * (delta_y
/ 3);
89 printf ("DEBUG:: point count: %d\n", metrics
->point_count
);
90 printf ("DEBUG:: metrics->min_x: %d\n", metrics
->min_x
);
91 printf ("DEBUG:: metrics->max_x: %d\n", metrics
->max_x
);
92 printf ("DEBUG:: metrics->min_y: %d\n", metrics
->min_y
);
93 printf ("DEBUG:: metrics->max_y: %d\n", metrics
->max_y
);
94 printf ("DEBUG:: delta_x: %d\n", delta_x
);
95 printf ("DEBUG:: delta_y: %d\n", delta_y
);
96 printf ("DEBUG:: bound_x_1: %d\n", bound_x_1
);
97 printf ("DEBUG:: bound_x_2: %d\n", bound_x_2
);
98 printf ("DEBUG:: bound_y_1: %d\n", bound_y_1
);
99 printf ("DEBUG:: bound_y_2: %d\n", bound_y_2
);
103 build string by placing points in bins, collapsing bins and
104 discarding those with too few points... */
106 crt_elem
= metrics
->pointList
;
107 while (crt_elem
!= NULL
)
109 /* figure out which bin the point falls in */
111 /*printf ("X = %d Y = %d\n", ((p_point)crt_elem->data)->x,
112 ((p_point)crt_elem->data)->y); */
115 current_bin
= _gstroke_bin ((p_point
)crt_elem
->data
, bound_x_1
,
116 bound_x_2
, bound_y_1
, bound_y_2
);
118 /* if this is the first point, consider it the previous bin, too. */
120 prev_bin
= current_bin
;
122 /*printf ("DEBUG:: current bin: %d x=%d y = %d\n", current_bin,
123 ((p_point)crt_elem->data)->x,
124 ((p_point)crt_elem->data)->y); */
126 if (prev_bin
== current_bin
)
129 /* we are moving to a new bin -- consider adding to the sequence */
130 if ((bin_count
> (metrics
->point_count
* GSTROKE_BIN_COUNT_PERCENT
))
131 || (first_bin
== TRUE
)) {
134 gchar val = '0' + prev_bin;
135 printf ("%c", val);fflush (stdout);
136 g_string_append (&sequence, &val);
140 sequence
[sequence_count
++] = '0' + prev_bin
;
141 /* printf ("DEBUG:: adding sequence: %d\n", prev_bin); */
145 /* restart counting points in the new bin */
147 prev_bin
= current_bin
;
150 /* move to next point, freeing current point from list */
152 free (crt_elem
->data
);
153 crt_elem
= g_slist_next (crt_elem
);
155 /* add the last run of points to the sequence */
156 sequence
[sequence_count
++] = '0' + current_bin
;
157 /* printf ("DEBUG:: adding final sequence: %d\n", current_bin); */
159 _gstroke_init (metrics
);
162 /* FIXME: get rid of this block
163 gchar val = '0' + current_bin;
164 printf ("%c\n", val);fflush (stdout);
165 g_string_append (&sequence, '\0');
167 sequence
[sequence_count
] = '\0';
173 /* my plan is to make a stroke training program where you can enter all of
174 the variations of slop that map to a canonical set of strokes. When the
175 application calls gstroke_canonical, it gets one of the recognized strokes,
176 or "", if it's not a recognized variation. I will probably use a hash
177 table. Right now, it just passes the values through to gstroke_trans */
179 _gstroke_canonical (gchar
*sequence
, struct gstroke_metrics
*metrics
)
181 return _gstroke_trans (sequence
, metrics
);
186 _gstroke_record (gint x
, gint y
, struct gstroke_metrics
*metrics
)
192 g_return_if_fail( metrics
!= NULL
);
195 printf ("%d:%d ", x
, y
); fflush (stdout
);
198 if (metrics
->point_count
< GSTROKE_MAX_POINTS
) {
199 if (metrics
->pointList
== NULL
) {
201 /* first point in list - initialize metrics */
202 metrics
->min_x
= 10000;
203 metrics
->min_y
= 10000;
207 new_point_p
= g_new0(struct s_point
, 1);
208 metrics
->pointList
= g_slist_prepend(metrics
->pointList
, new_point_p
);
209 metrics
->point_count
= 0;
212 p_point last_point
= (p_point
)g_slist_last(metrics
->pointList
)->data
;
214 /* interpolate between last and current point */
215 delx
= x
- last_point
->x
;
216 dely
= y
- last_point
->y
;
218 if (abs(delx
) > abs(dely
)) { /* step by the greatest delta direction */
221 /* go from the last point to the current, whatever direction it may be */
222 for (ix
= last_point
->x
; (delx
> 0) ? (ix
< x
) : (ix
> x
); ix
+= (delx
> 0) ? 1 : -1) {
224 /* step the other axis by the correct increment */
225 iy
+= fabs(((float) dely
/ (float) delx
)) * (float) ((dely
< 0) ? -1.0 : 1.0);
227 /* add the interpolated point */
228 new_point_p
= g_new0(struct s_point
, 1);
231 metrics
->pointList
= g_slist_append (metrics
->pointList
, new_point_p
);
234 if (((gint
) ix
) < metrics
->min_x
) metrics
->min_x
= (gint
) ix
;
235 if (((gint
) ix
) > metrics
->max_x
) metrics
->max_x
= (gint
) ix
;
236 if (((gint
) iy
) < metrics
->min_y
) metrics
->min_y
= (gint
) iy
;
237 if (((gint
) iy
) > metrics
->max_y
) metrics
->max_y
= (gint
) iy
;
238 metrics
->point_count
++;
241 } else { /* same thing, but for dely larger than delx case... */
242 p_point last_point
= (p_point
)g_slist_last(metrics
->pointList
)->data
;
246 /* go from the last point to the current, whatever direction it may be
248 for (iy
= last_point
->y
; (dely
> 0) ? (iy
< y
) : (iy
> y
); iy
+= (dely
> 0) ? 1 : -1) {
250 /* step the other axis by the correct increment */
251 ix
+= fabs(((float) delx
/ (float) dely
)) * (float) ((delx
< 0) ? -1.0 : 1.0);
253 /* add the interpolated point */
254 new_point_p
= g_new0(struct s_point
, 1);
257 metrics
->pointList
= g_slist_append(metrics
->pointList
, new_point_p
);
260 if (((gint
) ix
) < metrics
->min_x
) metrics
->min_x
= (gint
) ix
;
261 if (((gint
) ix
) > metrics
->max_x
) metrics
->max_x
= (gint
) ix
;
262 if (((gint
) iy
) < metrics
->min_y
) metrics
->min_y
= (gint
) iy
;
263 if (((gint
) iy
) > metrics
->max_y
) metrics
->max_y
= (gint
) iy
;
264 metrics
->point_count
++;
268 /* add the sampled point */
269 new_point_p
= g_new0(struct s_point
, 1);
270 metrics
->pointList
= g_slist_append(metrics
->pointList
, new_point_p
);
273 /* record the sampled point values */
279 GSList
*crt
= metrics
->pointList
;
283 printf ("(%d,%d)", ((p_point
)crt
->data
)->x
, ((p_point
)crt
->data
)->y
);
284 crt
= g_slist_next (crt
);