OSDN Git Service

modified: src/padarea.c
[kp123/kp123.git] / src / recognize_stroke.c
1
2 #include "defs.h"
3 #include <math.h>
4
5 #include "recognize_stroke.h"
6
7 float dist(GdkPoint *pt0, GdkPoint *pt1)
8 {
9     return sqrt(pow(pt1->x - pt0->x, 2) + pow(pt1->y - pt0->y, 2));
10 }
11
12 static float angle(GdkPoint *pt0, GdkPoint *pt, GdkPoint *pt2)
13 {
14     float d0 = dist(pt0, pt);
15     float d2 = dist(pt, pt2);
16     float d = dist(pt0, pt2);
17     return acos((pow(d0, 2) + pow(d2, 2) - pow(d, 2))/(2*d0*d2));
18 }
19
20 gchar get_stroke_char(GdkPoint *p0, GdkPoint *p1)
21 {
22     float x = p1->x - p0->x;
23     float y = p1->y - p0->y;
24     float a = acos(x/dist(p0, p1));
25     if(a <= M_PI/8)
26         return 'F';
27     if(a >= 7*M_PI/8)
28         return 'D';
29     if(y > 0)
30     {
31         if((5*M_PI/8 < a) && (a < 7*M_PI/8))
32             return 'A';
33         if((3*M_PI/8 <= a) && (a <= 5*M_PI/8))
34             return 'B';
35         if((M_PI/8 < a) && (a < 3*M_PI/8))
36             return 'C';
37     }
38     else
39     {
40         if((5*M_PI/8 < a) && (a < 7*M_PI/8))
41             return 'G';
42         if((3*M_PI/8 <= a) && (a <= 5*M_PI/8))
43             return 'H';
44         if((M_PI/8 < a) && (a < 3*M_PI/8))
45             return 'I';
46     }
47     return 0;
48 }
49
50 static float find_largest_angle(GList *s, GList **out)
51 {
52     float a = 0, a1;
53     GList *s0, *s1, *s2;
54     *out = 0;
55     if(g_list_length(s) < 3)
56         return 0;
57     for(s0 = s; s0 != g_list_last(s0); s0 = g_list_next(s0))
58     {
59         for(s1 = g_list_next(s0); s1 && (s1 != g_list_last(s1)); s1 = g_list_next(s1))
60         {
61             for(s2 = g_list_next(s1); s2 && (s2 != g_list_last(s2)); s2 = g_list_next(s2))
62             {
63                 GdkPoint *pt0 = (GdkPoint*)s0->data;
64                 GdkPoint *pt = (GdkPoint*)s1->data;
65                 GdkPoint *pt2 = (GdkPoint*)s2->data;
66                 a1 = angle(pt0, pt, pt2);
67                 if(a1 > a)
68                 {
69                     a = a1;
70                     *out = s1;
71                 }
72             }
73         }
74     }
75     return a;
76 }
77
78 static GList* filter_equal_points(GList *s)
79 {
80     GdkPoint *p, *p2;
81     for(;s != g_list_last(s); s = g_list_next(s))
82     {
83         p = (GdkPoint*)s->data;
84         p2 = (GdkPoint*)g_list_next(s)->data;
85         if((p->x == p2->x) && (p->y == p2->y))
86         {
87             s = g_list_delete_link(g_list_first(s), s);
88         }
89     }
90     return g_list_first(s);
91 }
92
93 /* let A, B, C be consecutive points
94  * if angle ABC larger then predefined value
95  * then remove B */
96 static GList* filter_points_by_angle(GList *s)
97 {
98     if(g_list_length(s) < 3)
99         return s;
100     gchar ch0, ch1;
101     GdkPoint *p0, *p, *p2;
102     for(s = g_list_next(s); s != g_list_last(s); s = g_list_next(s))
103     {
104         if(g_list_length(s) < 3)
105             break;
106         p0 = (GdkPoint*)g_list_previous(s)->data;
107         p = (GdkPoint*)s->data;
108         p2 = (GdkPoint*)g_list_next(s)->data;
109         ch0 = get_stroke_char(p0, p);
110         ch1 = get_stroke_char(p, p2);
111         if(ch0 == ch1)
112             s = g_list_delete_link(s, s);
113     }
114     return g_list_first(s);
115 }
116
117 static GList* filter_points_by_angle2(GList *s)
118 {
119     if(g_list_length(s) < 3)
120         return s;
121     const float MIN_MAX_ANGLE = 170*M_PI/180;
122     while(1)
123     {
124         if(g_list_length(s) < 3)
125             break;
126         GList *pt = 0;
127         if(find_largest_angle(s, &pt) > MIN_MAX_ANGLE)
128             s = g_list_delete_link(s, pt);
129         else
130             break;
131     }
132     return g_list_first(s);
133 }
134
135 static float find_average_and_smallest_dist(GList *s, GList **pt0, GList **pt1, float *pdmin)
136 {
137     float d = 0, dt, dmin = -1;
138     int i = 0;
139     for(; s != g_list_last(s); s = g_list_next(s), i++)
140     {
141         GdkPoint *p0 = (GdkPoint*)s->data;
142         GdkPoint *p1 = (GdkPoint*)g_list_next(s)->data;
143         dt = dist(p0, p1);
144         if((dmin < 0) || (dmin > dt))
145         {
146             dmin = dt;
147             *pt0 = s;
148             *pt1 = g_list_next(s);
149         }
150         d += dt;
151     }
152     *pdmin = dmin;
153     return d/i;
154 }
155
156 /* let A, B be consecutive points
157  * if dist(A,B) less than a certain percentage of average dist
158  * then remove B */
159 static GList* filter_points_by_dist(GList *s)
160 {
161     if(g_list_length(s) < 3)
162         return s;
163     const float MAX_MIN_DIST = 0.2;
164     float d0, dmin;
165     GList *pt0 = 0, *pt1 = 0;
166     while(1)
167     {
168         if(g_list_length(s) < 3)
169             break;
170         d0 = MAX_MIN_DIST*find_average_and_smallest_dist(s, &pt0, &pt1, &dmin);
171         if(dmin < d0)
172         {
173             GList *del;
174             if(pt0 == s)
175                 del = pt1;
176             else if(pt1 == g_list_last(s))
177                 del = pt0;
178             else
179             {
180                 GdkPoint *p0 = (GdkPoint*)pt0->data;
181                 GdkPoint *p01 = (GdkPoint*)g_list_previous(pt0)->data;
182                 GdkPoint *p1 = (GdkPoint*)pt1->data;
183                 GdkPoint *p11 = (GdkPoint*)g_list_next(pt1)->data;
184                 float d0 = dist(p0, p01);
185                 float d1 = dist(p1, p11);
186                 if(d0 < d1)
187                     del = pt0;
188                 else
189                     del = pt1;
190             }
191             s = g_list_delete_link(s, del);
192         }
193         else
194             break;
195     }
196     return g_list_first(s);
197 }
198
199 gchar *get_stroke_str(GList *s)
200 {
201     gchar *ret = calloc(g_list_length(s), sizeof(gchar));
202     gchar prev = -1;
203     gint len = 0;
204     for(;s != g_list_last(s); s = g_list_next(s))
205     {
206         GdkPoint *p0 = (GdkPoint*)s->data;
207         GdkPoint *p1 = (GdkPoint*)g_list_next(s)->data;
208         gchar ch = get_stroke_char(p0, p1);
209         if(!ch)
210             continue;
211         if(g_ascii_tolower(ch) != g_ascii_tolower(prev))
212         {
213             if(len)
214                 ch = g_ascii_tolower(ch);
215             ret[len++] = ch;
216         }
217         prev = ch;
218     }
219     return ret;
220 }
221
222 gchar* recognize_stroke(GList *stroke, GList **out)
223 {
224     *out = g_list_copy(stroke);
225     int len1, len2;
226     for(;;)
227     {
228         len1 = g_list_length(*out);
229         //g_printf("len_orig: %d\n", g_list_length(*out));
230         *out = filter_points_by_angle(*out);
231         //g_printf("len_angle_filtered: %d\n", g_list_length(*out));
232         *out = filter_points_by_dist(*out);
233         //g_printf("len_dist_filtered: %d\n", g_list_length(*out));
234         len2 = g_list_length(*out);
235         if(len1 != len2)
236             continue;
237         *out = filter_points_by_angle2(*out);
238         //g_printf("len_angle2_filtered: %d\n", g_list_length(*out));
239         len2 = g_list_length(*out);
240         if(len1 == len2)
241             break;
242     }
243     return get_stroke_str(*out);
244 }
245