egra: some agg mini optimisations (rendering, hittest)
[iv.d.git] / dtw_test / gestures.d
blob835960683211cf57dc651016866280b282a7c4f0
1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module gesturesdtw_test /*is aliced*/;
18 private:
20 import arsd.color;
21 import arsd.simpledisplay;
23 import iv.alice;
24 import iv.cmdcongl;
25 import iv.gengdtw;
26 import iv.pxclock;
27 import iv.vfs.io;
30 // ////////////////////////////////////////////////////////////////////////// //
31 SimpleWindow sdwin;
34 // ////////////////////////////////////////////////////////////////////////// //
35 ulong msgHideTime = 0;
36 int msgAlpha = -1;
37 string msgText;
40 void updateMsg () {
41 if (msgAlpha >= 0) {
42 if (msgAlpha > 0) {
43 if ((msgAlpha += 10) > 255) {
44 msgAlpha = -1;
45 msgText = null;
47 } else {
48 if (clockMilli() >= msgHideTime) {
49 msgAlpha = 1;
52 frameChanged();
57 void showMessage (string msg) {
58 if (msg.length == 0) return;
59 msgText = msg;
60 msgHideTime = clockMilli()+5000;
61 msgAlpha = 0;
65 // ////////////////////////////////////////////////////////////////////////// //
66 DTWGlyph[] glib;
67 int nameMaxLen = 0;
68 int curPattern = -1;
69 DTWGlyph drawnGlyph;
70 DTWGlyph detectedGlyph;
71 bool helpVisible;
74 void fixNameMaxLen () {
75 nameMaxLen = 0;
76 foreach (/*auto*/ g; glib) if (g.name.length > nameMaxLen) nameMaxLen = cast(int)g.name.length;
80 // ////////////////////////////////////////////////////////////////////////// //
81 int curGlyph = -1;
82 string curGlyphName;
83 bool editingName;
84 string yesNoMessage;
87 void registerGlyph () {
88 if (drawnGlyph !is null && drawnGlyph.valid && curGlyphName.length > 0) {
89 auto gg = drawnGlyph.clone;
90 gg.finish;
91 gg.name = curGlyphName;
92 usize gpos = usize.max;
93 foreach (/*auto*/ idx, /*auto*/ g; glib) if (g.name == curGlyphName) { gpos = idx; break; }
94 if (gpos != usize.max) {
95 glib[gpos] = gg;
96 } else {
97 gpos = glib.length;
98 glib ~= gg;
100 fixNameMaxLen();
101 curPattern = cast(int)gpos;
106 enum CharWidth = 10;
107 enum CharHeight = 10;
109 void frameChanged () {
110 if (sdwin is null || sdwin.closed) return;
113 auto painter = sdwin.draw();
115 void drawText (int x, int y, const(char)[] str...) {
116 foreach (immutable char ch; str) {
117 foreach (immutable int dy; 0..CharHeight) {
118 ushort v = glConFont10.ptr[cast(ubyte)ch*CharHeight+dy];
119 foreach (immutable int dx; 0..CharWidth) {
120 if (v&0x8000) painter.drawPixel(Point(x+dx, y+dy));
121 v <<= 1;
124 x += CharWidth;
128 void fillRect (int x, int y, int w, int h, Color clr) {
129 painter.outlineColor = clr;
130 painter.fillColor = clr;
131 painter.drawRectangle(Point(x, y), w, h);
132 painter.fillColor = Color.transparent;
135 void drawRect (int x, int y, int w, int h, Color clr) {
136 painter.outlineColor = clr;
137 painter.fillColor = Color.transparent;
138 painter.drawRectangle(Point(x, y), w, h);
141 void drawHelp () {
142 static immutable string[] helpText = [
143 "\x1fDemo actions",
144 "\x1f------------",
145 "\3keyboard:\1",
146 " \2F1\1: toggle help",
147 " \2F2\1: save library to '\4strokes.dat\1'",
148 " \2F3\1: replace library with '\4strokes.dat\1'",
149 " \2ESC\1: quit",
150 " \2DEL\1: delete selected stroke",
152 "\3mouse:\1",
153 " \2LMB\1: select name or start drawing",
154 " \2RMB\1: register current stroke as template",
157 static int stlen (string s) {
158 int res = 0;
159 foreach (immutable char ch; s) if (ch >= 32) ++res;
160 return res;
163 int maxlen = 0;
164 foreach (/*auto*/ s; helpText) {
165 auto ln = stlen(s);
166 if (ln > maxlen) maxlen = ln;
169 int wdt = (maxlen*CharWidth+6);
170 int hgt = (CharHeight*cast(int)helpText.length+6);
171 int x0 = (sdwin.width-wdt)/2;
172 int y0 = (sdwin.height-hgt)/2;
174 fillRect(x0, y0, wdt, hgt, Color(25, 69, 247));
175 drawRect(x0, y0, wdt, hgt, Color(255, 255, 255));
176 drawRect(x0+1, y0+1, wdt-2, hgt-2, Color.black);
178 foreach (/*auto*/ idx, /*auto*/ s; helpText) {
179 if (s.length == 0) continue;
180 auto ln = stlen(s)*CharWidth;
181 auto x = (wdt-ln)/2;
182 auto y = idx*CharHeight+3;
183 string st = s;
184 if (s[0] == '\x1f') {
185 st = s[1..$];
186 } else {
187 x = 3;
189 Color fg = Color(255, 255, 255);
190 foreach (/*auto*/ ch; st) {
191 switch (ch) {
192 case 1: fg = Color(255, 255, 255); break;
193 case 2: fg = Color(0, 255, 0); break;
194 case 3: fg = Color(255, 255, 0); break;
195 case 4: fg = Color(255, 127, 0); break;
196 default: break;
198 if (ch < 32) continue;
199 painter.outlineColor = fg;
200 drawText(x0+x, y0+y, ch);
201 x += CharWidth;
206 bool drawYesNoMessage () {
207 if (yesNoMessage.length > 0) {
208 fillRect(0, sdwin.height-CharHeight, sdwin.width, CharHeight, Color(128, 0, 0));
209 painter.outlineColor = Color(255, 255, 0);
210 drawText(0, sdwin.height-CharHeight, yesNoMessage);
211 return true;
213 return false;
216 bool drawEditor () {
217 if (editingName) {
218 fillRect(0, sdwin.height-CharHeight, sdwin.width, CharHeight, Color(0, 0, 190));
219 painter.outlineColor = Color(255, 127, 0);
220 drawText(0, sdwin.height-CharHeight, curGlyphName);
221 fillRect(CharWidth*cast(int)curGlyphName.length, sdwin.height-CharHeight, CharWidth, CharHeight, Color(255, 255, 0));
222 return true;
224 return false;
227 bool drawMessage () {
228 if (msgAlpha >= 0 && msgText.length) {
229 int y = sdwin.height-CharHeight;
230 fillRect(0, y, sdwin.width, CharHeight, Color(60, 60, 90));
231 painter.outlineColor = Color(255, 255, 255);
232 drawText((sdwin.width-CharWidth*cast(int)msgText.length)/2, y, msgText);
233 return true;
235 return false;
238 void drawStroke (const(DTWGlyph) stk) {
239 painter.outlineColor = Color(255, 255, 0);
240 foreach (uint idx; 1..stk.length) {
241 immutable p0 = stk[idx-1], p1 = stk[idx];
242 double x0 = p0.x, y0 = p0.y;
243 double x1 = p1.x, y1 = p1.y;
244 painter.drawLine(Point(cast(int)x0, cast(int)y0), Point(cast(int)x1, cast(int)y1));
248 void drawTemplate (const(DTWGlyph) stk) {
249 foreach (uint idx; 1..stk.length) {
250 auto g = cast(ubyte)(255*idx/(stk.length-1));
251 auto b = cast(ubyte)(255-(255*idx/(stk.length-1)));
252 immutable p0 = stk[idx-1], p1 = stk[idx];
253 double x0 = p0.x, y0 = p0.y;
254 double x1 = p1.x, y1 = p1.y;
255 x0 = x0*200+400;
256 y0 = y0*200+300;
257 x1 = x1*200+400;
258 y1 = y1*200+300;
259 painter.outlineColor = Color(0, g, b);
260 painter.drawLine(Point(cast(int)x0, cast(int)y0), Point(cast(int)x1, cast(int)y1));
264 void drawStrokeList (int curptr) {
265 int wdt = nameMaxLen*CharWidth+4;
266 int hgt = cast(int)(glib.length*CharHeight+4);
267 drawRect(0, 0, wdt, hgt, Color.white);
268 drawRect(1, 1, wdt-2, hgt-2, Color.black);
269 foreach (/*auto*/ idx, /*auto*/ g; glib) {
270 Color col, bkcol;
271 if (g is detectedGlyph) {
272 // highlighted
273 col = Color(255, 255, 255);
274 //bkcol = rgb2col(0, 0, 255);
275 bkcol = Color(0, 100, 0);
276 } else {
277 col = Color(255, 127, 0);
278 bkcol = Color(0, 0, 127);
280 if (curptr == idx) bkcol = Color(0, 127, 0);
281 if (idx == curPattern) col = Color(255, 255, 0);
282 fillRect(2, idx*CharHeight+2, wdt-4, CharHeight, bkcol);
283 painter.outlineColor = col;
284 drawText(2, idx*CharHeight+2, g.name);
288 fillRect(0, 0, sdwin.width, sdwin.height, Color.black); // cls
290 if (curPattern >= 0 && curPattern < cast(int)glib.length) drawTemplate(glib[curPattern]);
291 drawStrokeList(curGlyph);
292 if (drawnGlyph !is null && drawnGlyph.valid) drawStroke(drawnGlyph);
294 if (!drawYesNoMessage()) drawEditor();
295 drawMessage();
296 if (helpVisible) drawHelp();
298 flushGui();
302 // ////////////////////////////////////////////////////////////////////////// //
303 int mdown = 0;
306 int getSelectedGlyph (int x, int y) {
307 int wdt = nameMaxLen*CharWidth+4;
308 int hgt = cast(int)(glib.length*CharHeight+4);
309 if (x >= 2 && y >= 2 && x < wdt-4 && y < hgt-4) {
310 return cast(int)((y-2)/CharHeight);
311 } else {
312 return -1;
317 // ////////////////////////////////////////////////////////////////////////// //
318 void main (string[] args) {
319 glib = gstLibLoad(VFile("strokes.dat"));
320 fixNameMaxLen();
321 writefln("%s strokes loaded", glib.length);
322 //gstLibSave(File("strokes_new.dat", "w"), glib[]);
323 sdwin = new SimpleWindow(800, 600, "DTW Gesture Recognizer test");
324 frameChanged();
325 sdwin.eventLoop(100,
326 // pulse timer
327 delegate () {
328 updateMsg();
330 // mouse events
331 delegate (MouseEvent event) {
332 switch (event.type) {
333 case MouseEventType.buttonPressed:
334 if (yesNoMessage.length > 0 || editingName) break;
335 if (mdown == 0) {
336 if (event.button == MouseButton.left) {
337 auto ng = getSelectedGlyph(event.x, event.y);
338 if (ng >= 0) {
339 curPattern = ng;
340 frameChanged();
341 return;
344 if (event.button == MouseButton.left || event.button == MouseButton.right) {
345 mdown = (event.button == MouseButton.left ? 1 : 2);
346 detectedGlyph = null;
347 drawnGlyph = new DTWGlyph();
348 drawnGlyph.appendPoint(event.x, event.y);
349 frameChanged();
352 break;
353 case MouseEventType.buttonReleased:
354 if (yesNoMessage.length > 0 || editingName) return;
355 if (mdown != 0) {
356 if (drawnGlyph.valid) {
357 if (mdown == 1) {
358 detectedGlyph = cast(DTWGlyph)drawnGlyph.findMatch(glib[]); // sorry
359 if (detectedGlyph !is null && detectedGlyph.name.length > 0) {
360 showMessage("glyph: '"~detectedGlyph.name~"'");
362 } else {
363 curGlyphName = (curPattern >= 0 ? glib[curPattern].name : "");
364 editingName = true;
366 } else {
367 drawnGlyph = null;
369 frameChanged();
371 mdown = 0;
372 break;
373 case MouseEventType.motion:
374 if (yesNoMessage.length > 0 || editingName) break;
375 if (mdown == 0) {
376 auto ng = getSelectedGlyph(event.x, event.y);
377 if (ng != curGlyph) {
378 curGlyph = ng;
379 frameChanged();
381 } else if (mdown != 0) {
382 drawnGlyph.appendPoint(event.x, event.y);
383 frameChanged();
385 break;
386 default:
389 // keyboard events
390 delegate (KeyEvent event) {
391 if (!event.pressed) return;
392 if (helpVisible) {
393 if (event == "Escape" || event == "F1") {
394 helpVisible = false;
395 frameChanged();
397 return;
399 if (yesNoMessage.length > 0) {
400 if (event == "Escape") {
401 yesNoMessage = null;
402 } else if (event == "Enter") {
403 glib = glib[0..curPattern]~glib[curPattern+1..$];
404 detectedGlyph = null;
405 curPattern = -1;
406 yesNoMessage = null;
408 frameChanged();
409 return;
412 if (editingName) {
413 if (event == "Escape")
414 switch (event.keysym.sym) {
415 case SDLK_ESCAPE: editingName = false; break;
416 case SDLK_BACKSPACE:
417 if (curGlyphName.length > 0) curGlyphName = curGlyphName[0..$-1];
418 break;
419 case SDLK_RETURN: registerGlyph(); editingName = false; break;
420 default: break;
422 if (!editingName) stopTextInput();
423 frameChanged();
424 return;
427 if (event == "C-Q") { sdwin.close(); return; }
428 if (event == "F1") {
429 helpVisible = true;
430 frameChanged();
431 return;
433 if (event == "F2") {
434 gstLibSave(VFile("strokes.dat", "w"), glib[]);
435 writefln("%s strokes saved", glib.length);
436 frameChanged();
437 return;
439 if (event == "F3") {
440 glib = gstLibLoad(VFile("strokes.dat"));
441 fixNameMaxLen();
442 writefln("%s strokes loaded", glib.length);
443 detectedGlyph = null;
444 curPattern = -1;
445 drawnGlyph = null;
446 frameChanged();
447 return;
449 if (event == "Delete") {
450 if (curPattern >= 0) {
451 yesNoMessage = "Remove '"~glib[curPattern].name~"'?";
452 frameChanged();
453 return;
457 // characters
458 delegate (dchar ch) {
459 if (!editingName) return;
460 if (ch == 27) { editingName = false; frameChanged(); return; }
461 if (ch == 8) {
462 if (curGlyphName.length > 0) curGlyphName = curGlyphName[0..$-1];
463 frameChanged();
464 return;
466 if (ch == 25) {
467 // C-Y
468 curGlyphName = null;
469 frameChanged();
470 return;
472 if (ch == 10 || ch == 13) {
473 registerGlyph();
474 editingName = false;
475 return;
477 if (ch >= ' ' && ch < 127) {
478 curGlyphName ~= cast(char)ch;
479 frameChanged();
480 return;