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 /* FIXME: does this free the data too?*/
27 g_slist_free (metrics
->pointList
);
28 metrics
->pointList
= NULL
;
29 metrics
->point_count
= 0;
33 /* figure out which bin the point falls in */
35 _gstroke_bin (p_point point_p
, gint bound_x_1
, gint bound_x_2
,
36 gint bound_y_1
, gint bound_y_2
)
41 if (point_p
->x
> bound_x_1
) bin_num
+= 1;
42 if (point_p
->x
> bound_x_2
) bin_num
+= 1;
43 if (point_p
->y
> bound_y_1
) bin_num
+= 3;
44 if (point_p
->y
> bound_y_2
) bin_num
+= 3;
50 _gstroke_trans (gchar
*sequence
, struct gstroke_metrics
*metrics
)
53 /* number of bins recorded in the stroke */
54 guint sequence_count
= 0;
56 /* points-->sequence translation scratch variables */
61 /* flag indicating the start of a stroke - always count it in the sequence */
62 gint first_bin
= TRUE
;
64 /* bin boundary and size variables */
65 gint delta_x
, delta_y
;
66 gint bound_x_1
, bound_x_2
;
67 gint bound_y_1
, bound_y_2
;
70 /* determine size of grid */
71 delta_x
= metrics
->max_x
- metrics
->min_x
;
72 delta_y
= metrics
->max_y
- metrics
->min_y
;
74 /* calculate bin boundary positions */
75 bound_x_1
= metrics
->min_x
+ (delta_x
/ 3);
76 bound_x_2
= metrics
->min_x
+ 2 * (delta_x
/ 3);
78 bound_y_1
= metrics
->min_y
+ (delta_y
/ 3);
79 bound_y_2
= metrics
->min_y
+ 2 * (delta_y
/ 3);
81 if (delta_x
> GSTROKE_SCALE_RATIO
* delta_y
) {
82 bound_y_1
= (metrics
->max_y
+ metrics
->min_y
- delta_x
) / 2 + (delta_x
/ 3);
83 bound_y_2
= (metrics
->max_y
+ metrics
->min_y
- delta_x
) / 2 + 2 * (delta_x
/ 3);
84 } else if (delta_y
> GSTROKE_SCALE_RATIO
* delta_x
) {
85 bound_x_1
= (metrics
->max_x
+ metrics
->min_x
- delta_y
) / 2 + (delta_y
/ 3);
86 bound_x_2
= (metrics
->max_x
+ metrics
->min_x
- delta_y
) / 2 + 2 * (delta_y
/ 3);
90 printf ("DEBUG:: point count: %d\n", metrics
->point_count
);
91 printf ("DEBUG:: metrics->min_x: %d\n", metrics
->min_x
);
92 printf ("DEBUG:: metrics->max_x: %d\n", metrics
->max_x
);
93 printf ("DEBUG:: metrics->min_y: %d\n", metrics
->min_y
);
94 printf ("DEBUG:: metrics->max_y: %d\n", metrics
->max_y
);
95 printf ("DEBUG:: delta_x: %d\n", delta_x
);
96 printf ("DEBUG:: delta_y: %d\n", delta_y
);
97 printf ("DEBUG:: bound_x_1: %d\n", bound_x_1
);
98 printf ("DEBUG:: bound_x_2: %d\n", bound_x_2
);
99 printf ("DEBUG:: bound_y_1: %d\n", bound_y_1
);
100 printf ("DEBUG:: bound_y_2: %d\n", bound_y_2
);
104 build string by placing points in bins, collapsing bins and
105 discarding those with too few points... */
107 crt_elem
= metrics
->pointList
;
108 while (crt_elem
!= NULL
)
110 /* figure out which bin the point falls in */
112 /*printf ("X = %d Y = %d\n", ((p_point)crt_elem->data)->x,
113 ((p_point)crt_elem->data)->y); */
116 current_bin
= _gstroke_bin ((p_point
)crt_elem
->data
, bound_x_1
,
117 bound_x_2
, bound_y_1
, bound_y_2
);
119 /* if this is the first point, consider it the previous bin, too. */
121 prev_bin
= current_bin
;
123 /*printf ("DEBUG:: current bin: %d x=%d y = %d\n", current_bin,
124 ((p_point)crt_elem->data)->x,
125 ((p_point)crt_elem->data)->y); */
127 if (prev_bin
== current_bin
)
130 /* we are moving to a new bin -- consider adding to the sequence */
131 if ((bin_count
> (metrics
->point_count
* GSTROKE_BIN_COUNT_PERCENT
))
132 || (first_bin
== TRUE
)) {
135 gchar val = '0' + prev_bin;
136 printf ("%c", val);fflush (stdout);
137 g_string_append (&sequence, &val);
141 sequence
[sequence_count
++] = '0' + prev_bin
;
142 /* printf ("DEBUG:: adding sequence: %d\n", prev_bin); */
146 /* restart counting points in the new bin */
148 prev_bin
= current_bin
;
151 /* move to next point, freeing current point from list */
153 free (crt_elem
->data
);
154 crt_elem
= g_slist_next (crt_elem
);
156 /* add the last run of points to the sequence */
157 sequence
[sequence_count
++] = '0' + current_bin
;
158 /* printf ("DEBUG:: adding final sequence: %d\n", current_bin); */
160 _gstroke_init (metrics
);
163 /* FIXME: get rid of this block
164 gchar val = '0' + current_bin;
165 printf ("%c\n", val);fflush (stdout);
166 g_string_append (&sequence, '\0');
168 sequence
[sequence_count
] = '\0';
174 /* my plan is to make a stroke training program where you can enter all of
175 the variations of slop that map to a canonical set of strokes. When the
176 application calls gstroke_canonical, it gets one of the recognized strokes,
177 or "", if it's not a recognized variation. I will probably use a hash
178 table. Right now, it just passes the values through to gstroke_trans */
180 _gstroke_canonical (gchar
*sequence
, struct gstroke_metrics
*metrics
)
182 return _gstroke_trans (sequence
, metrics
);
187 _gstroke_record (gint x
, gint y
, struct gstroke_metrics
*metrics
)
193 g_return_if_fail( metrics
!= NULL
);
196 printf ("%d:%d ", x
, y
); fflush (stdout
);
199 if (metrics
->point_count
< GSTROKE_MAX_POINTS
) {
200 new_point_p
= g_malloc(sizeof (struct s_point
));
202 if (metrics
->pointList
== NULL
) {
204 /* first point in list - initialize metrics */
205 metrics
->min_x
= 10000;
206 metrics
->min_y
= 10000;
210 metrics
->pointList
= g_slist_prepend(metrics
->pointList
, new_point_p
);
211 metrics
->point_count
= 0;
215 #define LAST_POINT ((p_point)(g_slist_last (metrics->pointList)->data))
217 /* interpolate between last and current point */
218 delx
= x
- LAST_POINT
->x
;
219 dely
= y
- LAST_POINT
->y
;
221 if (abs(delx
) > abs(dely
)) { /* step by the greatest delta direction */
224 /* go from the last point to the current, whatever direction it may be */
225 for (ix
= LAST_POINT
->x
; (delx
> 0) ? (ix
< x
) : (ix
> x
); ix
+= (delx
> 0) ? 1 : -1) {
227 /* step the other axis by the correct increment */
228 iy
+= fabs(((float) dely
/ (float) delx
)) * (float) ((dely
< 0) ? -1.0 : 1.0);
230 /* add the interpolated point */
233 metrics
->pointList
= g_slist_append (metrics
->pointList
, new_point_p
);
236 if (((gint
) ix
) < metrics
->min_x
) metrics
->min_x
= (gint
) ix
;
237 if (((gint
) ix
) > metrics
->max_x
) metrics
->max_x
= (gint
) ix
;
238 if (((gint
) iy
) < metrics
->min_y
) metrics
->min_y
= (gint
) iy
;
239 if (((gint
) iy
) > metrics
->max_y
) metrics
->max_y
= (gint
) iy
;
240 metrics
->point_count
++;
242 new_point_p
= malloc(sizeof(struct s_point
));
244 } else { /* same thing, but for dely larger than delx case... */
247 /* go from the last point to the current, whatever direction it may be
249 for (iy
= LAST_POINT
->y
; (dely
> 0) ? (iy
< y
) : (iy
> y
); iy
+= (dely
> 0) ? 1 : -1) {
251 /* step the other axis by the correct increment */
252 ix
+= fabs(((float) delx
/ (float) dely
)) * (float) ((delx
< 0) ? -1.0 : 1.0);
254 /* add the interpolated point */
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
++;
266 new_point_p
= malloc(sizeof(struct s_point
));
270 /* add the sampled point */
271 metrics
->pointList
= g_slist_append(metrics
->pointList
, new_point_p
);
274 /* record the sampled point values */
280 GSList
*crt
= metrics
->pointList
;
284 printf ("(%d,%d)", ((p_point
)crt
->data
)->x
, ((p_point
)crt
->data
)->y
);
285 crt
= g_slist_next (crt
);