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 gesturespro_test
/*is aliced*/;
21 import arsd
.simpledisplay
;
32 // ////////////////////////////////////////////////////////////////////////// //
36 // ////////////////////////////////////////////////////////////////////////// //
37 ulong msgHideTime
= 0;
45 if ((msgAlpha
+= 10) > 255) {
50 if (clockMilli() >= msgHideTime
) {
59 void showMessage (string msg
) {
60 if (msg
.length
== 0) return;
62 ovlMsg = new VLOverlay(msg.length*6+6, 8+6);
63 ovlMsg.fillRect(0, 0, ovlMsg.width, ovlMsg.height, rgb2col(25, 69, 247));
64 ovlMsg.rect(0, 0, ovlMsg.width, ovlMsg.height, rgb2col(255, 255, 255));
65 ovlMsg.rect(1, 1, ovlMsg.width-2, ovlMsg.height-2, rgb2col(0, 0, 0));
66 Color fg = rgb2col(255, 255, 255);
68 foreach (/+auto+/ ch
; msg
) {
71 case 1: fg = rgb2col(255, 255, 255); break;
72 case 2: fg = rgb2col(0, 255, 0); break;
73 case 3: fg = rgb2col(255, 255, 0); break;
74 case 4: fg = rgb2col(255, 127, 0); break;
77 if (ch < 32) continue;
79 ovlMsg
.drawChar(x
, 3, ch
, fg
);
84 msgHideTime
= clockMilli()+5000;
90 // ////////////////////////////////////////////////////////////////////////// //
93 DPPointCloud curPattern
;
99 void fixNameMaxLen () {
101 foreach (string name
; glib
.knownGestureNames
) if (name
.length
> nameMaxLen
) nameMaxLen
= cast(int)name
.length
;
105 // ////////////////////////////////////////////////////////////////////////// //
112 void registerGlyph () {
113 if (drawnGlyph
.length
> 5 && curGlyphName
.length
> 0) {
114 auto pc
= new DPPointCloud(curGlyphName
, drawnGlyph
);
115 glib
.appendGesture(pc
);
122 // ////////////////////////////////////////////////////////////////////////// //
124 enum CharHeight
= 10;
126 void frameChanged () {
127 if (sdwin
is null || sdwin
.closed
) return;
130 auto painter
= sdwin
.draw();
132 void drawText (int x
, int y
, const(char)[] str...) {
133 foreach (immutable char ch
; str) {
134 foreach (immutable int dy
; 0..CharHeight
) {
135 ushort v
= glConFont10
.ptr
[cast(ubyte)ch
*CharHeight
+dy
];
136 foreach (immutable int dx
; 0..CharWidth
) {
138 painter
.drawPixel(Point(x
+dx
, y
+dy
));
139 //painter.drawLine(Point(x+dx, y+dy), Point(x+dx+1, y+dy+1));
148 void helpOverlay () {
149 static immutable string
[] helpText
= [
153 " \2F1\1: toggle help",
154 " \2F2\1: save library to '\4strokes.dat\1'",
155 " \2F3\1: replace library with '\4strokes.dat\1'",
157 " \2DEL\1: delete selected stroke",
160 " \2LMB\1: select name or start drawing",
161 " \2RMB\1: register current stroke as template",
162 " \2Ctl\1: hold it to draw next segment",
165 static int stlen (string s
) {
167 foreach (immutable char ch
; s
) if (ch
>= 32) ++res
;
172 foreach (/*auto*/ s
; helpText
) {
174 if (ln
> maxlen
) maxlen
= ln
;
177 int wdt
= (maxlen
*CharWidth
+6);
178 int hgt
= (CharHeight
*cast(int)helpText
.length
+6);
179 int x0
= (sdwin
.width
-wdt
)/2;
180 int y0
= (sdwin
.height
-hgt
)/2;
182 painter
.outlineColor
= Color(25, 69, 247);
183 painter
.fillColor
= Color(25, 69, 247);
184 painter
.drawRectangle(Point(x0
, y0
), wdt
, hgt
);
185 painter
.fillColor
= Color
.transparent
;
186 painter
.outlineColor
= Color(255, 255, 255);
187 painter
.drawRectangle(Point(x0
, y0
), wdt
, hgt
);
188 painter
.outlineColor
= Color
.black
;
189 painter
.drawRectangle(Point(x0
+1, y0
+1), wdt
-2, hgt
-2);
191 foreach (/*auto*/ idx
, /*auto*/ s
; helpText
) {
192 if (s
.length
== 0) continue;
193 auto ln
= stlen(s
)*CharWidth
;
195 auto y
= idx
*CharHeight
+3;
197 if (s
[0] == '\x1f') {
202 Color fg
= Color(255, 255, 255);
203 foreach (/*auto*/ ch
; st
) {
205 case 1: fg
= Color(255, 255, 255); break;
206 case 2: fg
= Color(0, 255, 0); break;
207 case 3: fg
= Color(255, 255, 0); break;
208 case 4: fg
= Color(255, 127, 0); break;
211 if (ch
< 32) continue;
212 painter
.outlineColor
= fg
;
213 drawText(x0
+x
, y0
+y
, ch
);
219 void drawStroke (const(DPPoint
)[] stk
) {
220 painter
.outlineColor
= Color(255, 255, 0);
222 uint lastid
= uint.max
;
223 foreach (immutable pidx
, const ref DPPoint p
; stk
) {
227 } else if (p
.id
== lastid
) {
228 painter
.drawLine(Point(cast(int)stk
[pidx
-1].x
, cast(int)stk
[pidx
-1].y
), Point(cast(int)stk
[pidx
].x
, cast(int)stk
[pidx
].y
));
230 painter
.drawLine(Point(cast(int)stk
[pidx
-1].x
, cast(int)stk
[pidx
-1].y
), Point(cast(int)stk
[pidx
-1].x
+1, cast(int)stk
[pidx
-1].y
+1));
236 void drawTemplate (DPPointCloud stk
) {
237 if (stk
is null) return;
238 auto pts
= stk
.points
;
239 if (pts
.length
< 2) return;
241 uint lastid
= uint.max
;
242 double x0
, y0
, x1
, y1
;
243 foreach (uint idx
, const ref DPPoint p
; pts
) {
244 auto g
= cast(ubyte)(255*idx
/(pts
.length
-1));
245 auto b
= cast(ubyte)(255-(255*idx
/(pts
.length
-1)));
252 if (p
.id
== lastid
) {
258 x0
= x1
= pts
[idx
-1].x
;
259 y0
= y1
= pts
[idx
-1].y
;
263 //immutable p0 = stk.normPoint(idx-1), p1 = stk.normPoint(idx);
268 painter
.outlineColor
= Color(0, g
, b
);
269 painter
.drawLine(Point(cast(int)x0
, cast(int)y0
), Point(cast(int)x1
, cast(int)y1
));
273 void drawStrokeList (int curptr
) {
274 auto names
= glib
.knownGestureNames
; //WARNING: this allocates like crazy!
275 int wdt
= nameMaxLen
*CharWidth
+4;
276 int hgt
= cast(int)(names
.length
*CharHeight
+4);
277 painter
.outlineColor
= Color
.white
;
278 painter
.fillColor
= Color
.transparent
;
279 painter
.drawRectangle(Point(0, 0), wdt
, hgt
);
280 painter
.outlineColor
= Color
.black
;
281 painter
.drawRectangle(Point(1, 1), wdt
-2, hgt
-2);
282 painter
.fillColor
= Color
.white
;
283 painter
.drawRectangle(Point(2, 2), wdt
-4, hgt
-4);
284 painter
.fillColor
= Color
.transparent
;
285 foreach (immutable idx
, string name
; names
) {
287 if (name
== detectedGlyph
) {
289 col
= Color(255, 255, 255);
290 //bkcol = rgb2col(0, 0, 255);
291 bkcol
= Color(0, 100, 0);
293 col
= Color(255, 127, 0);
294 bkcol
= Color(0, 0, 127);
296 if (curptr
== idx
) bkcol
= Color(0, 127, 0);
297 foreach (uint gidx
; 0..uint.max
) {
298 auto fg
= glib
.findGesture(name
, gidx
);
299 if (fg
is null) break;
300 if (fg
is curPattern
) { col
= Color(255, 255, 0); break; }
302 painter
.outlineColor
= bkcol
;
303 painter
.fillColor
= bkcol
;
304 painter
.drawRectangle(Point(2, idx
*CharHeight
+2), wdt
-4, CharHeight
);
305 painter
.outlineColor
= col
;
306 painter
.fillColor
= Color
.transparent
;
307 drawText(2, idx
*CharHeight
+2, name
);
311 painter
.outlineColor
= Color
.black
;
312 painter
.fillColor
= Color
.black
;
313 painter
.drawRectangle(Point(0, 0), sdwin
.width
, sdwin
.height
);
315 if (curPattern
!is null) drawTemplate(curPattern
);
316 drawStrokeList(curGlyph
);
317 if (drawnGlyph
.length
) drawStroke(drawnGlyph
);
318 if (yesNoMessage
.length
> 0) {
319 painter
.outlineColor
= Color(128, 0, 0);
320 painter
.fillColor
= Color(128, 0, 0);
321 painter
.drawRectangle(Point(0, sdwin
.height
-CharHeight
), sdwin
.width
, CharHeight
);
322 painter
.outlineColor
= Color(255, 255, 0);
323 painter
.fillColor
= Color
.transparent
;
324 drawText(0, sdwin
.height
-CharHeight
, yesNoMessage
);
325 } else if (editingName
) {
326 painter
.outlineColor
= Color(0, 0, 190);
327 painter
.fillColor
= Color(0, 0, 190);
328 painter
.drawRectangle(Point(0, sdwin
.height
-CharHeight
), sdwin
.width
, CharHeight
);
329 painter
.outlineColor
= Color(255, 127, 0);
330 painter
.fillColor
= Color
.transparent
;
331 drawText(0, sdwin
.height
-CharHeight
, curGlyphName
);
332 painter
.outlineColor
= Color(255, 255, 0);
333 painter
.fillColor
= Color(255, 255, 0);
334 painter
.drawRectangle(Point(CharWidth
*cast(int)curGlyphName
.length
, sdwin
.height
-CharHeight
), CharWidth
, CharHeight
);
335 painter
.outlineColor
= Color(255, 127, 0);
336 painter
.fillColor
= Color
.transparent
;
338 if (msgAlpha
>= 0 && msgText
.length
) {
339 int y
= sdwin
.height
-CharHeight
;
340 painter
.outlineColor
= Color(60, 60, 90);
341 painter
.fillColor
= Color(60, 60, 90);
342 painter
.drawRectangle(Point(0, y
), sdwin
.width
, CharHeight
);
343 painter
.outlineColor
= Color(255, 255, 255);
344 painter
.fillColor
= Color
.transparent
;
345 drawText((sdwin
.width
-CharWidth
*cast(int)msgText
.length
)/2, y
, msgText
);
346 painter
.fillColor
= Color
.transparent
;
356 // ////////////////////////////////////////////////////////////////////////// //
360 int getSelectedGlyph (int x
, int y
) {
361 int wdt
= nameMaxLen
*CharWidth
+4;
362 int hgt
= cast(int)(glib
.knownGestureNames
.length
*CharHeight
+4);
363 if (x
>= 2 && y
>= 2 && x
< wdt
-4 && y
< hgt
-4) {
364 return cast(int)((y
-2)/CharHeight
);
371 // ////////////////////////////////////////////////////////////////////////// //
372 void main (string
[] args
) {
373 glib
= new DPGestureList();
374 glib
.load(VFile("zgest.gsl"));
376 writefln("%s strokes loaded", glib
.knownGestureNames
.length
);
377 //gstLibSave(File("strokes_new.dat", "w"), glib[]);
378 sdwin
= new SimpleWindow(800, 600, "Protractor Gesture Recognizer test");
386 delegate (MouseEvent event
) {
387 switch (event
.type
) {
388 case MouseEventType
.buttonPressed
:
389 if (yesNoMessage
.length
> 0 || editingName
) break;
391 if (event
.button
== MouseButton
.left
) {
392 auto ng
= getSelectedGlyph(event
.x
, event
.y
);
394 auto names
= glib
.knownGestureNames
;
395 if (ng
< names
.length
) {
396 curPattern
= glib
.findGesture(names
[ng
]);
402 if (event
.button
== MouseButton
.left || event
.button
== MouseButton
.right
) {
403 mdown
= (event
.button
== MouseButton
.left ?
1 : 2);
404 if (detectedGlyph
.length
== 0 && (event
.modifierState
&ModifierState
.ctrl
)) {
406 if (drawnGlyph
.length
> 0) {
407 drawnGlyph
~= DPPoint(event
.x
, event
.y
, drawnGlyph
[$-1].id
+1);
409 drawnGlyph
~= DPPoint(event
.x
, event
.y
, 1);
412 detectedGlyph
= null;
413 drawnGlyph
.length
= 0;
414 drawnGlyph
~= DPPoint(event
.x
, event
.y
, 1);
420 case MouseEventType
.buttonReleased
:
421 if (yesNoMessage
.length
> 0 || editingName
) return;
423 if (drawnGlyph
.length
> 0) {
425 if (event
.modifierState
&ModifierState
.ctrl
) {
428 auto res
= glib
.recognize(drawnGlyph
);
430 detectedGlyph
= res
.name
;
431 showMessage("glyph: '"~detectedGlyph
~"'");
432 writeln("glyph: '", detectedGlyph
, "'; score: ", res
.score
);
434 curGlyphName
= (curPattern
!is null ? curPattern
.name
: "");
446 case MouseEventType
.motion
:
447 if (yesNoMessage
.length
> 0 || editingName
) break;
449 auto ng
= getSelectedGlyph(event
.x
, event
.y
);
450 if (ng
!= curGlyph
) {
454 } else if (mdown
!= 0) {
455 if (drawnGlyph
.length
> 0) {
456 drawnGlyph
~= DPPoint(event
.x
, event
.y
, drawnGlyph
[$-1].id
);
465 delegate (KeyEvent event
) {
466 if (!event
.pressed
&& event
.key
== Key
.Ctrl
) {
467 if (drawnGlyph
.length
) {
468 auto res
= glib
.recognize(drawnGlyph
);
470 detectedGlyph
= res
.name
;
471 showMessage("glyph: '"~detectedGlyph
~"'");
472 writeln("glyph: '", detectedGlyph
, "'; score: ", res
.score
);
474 curGlyphName
= (curPattern
!is null ? curPattern
.name
: "");
480 if (!event
.pressed
) return;
482 if (event
== "Escape" || event
== "F1") {
488 if (yesNoMessage
.length
> 0) {
489 if (event
== "Escape") {
491 } else if (event
== "Enter") {
492 if (curPattern
!is null) glib
.removeGesture(curPattern
.name
);
493 detectedGlyph
= null;
500 if (event
== "C-Q") { sdwin
.close(); return; }
501 if (event
== "C-T") { addTestGestures(glib
); fixNameMaxLen(); frameChanged(); return; }
508 glib
.save(VFile("strokes.dat", "w"));
509 //writefln("%s strokes saved", glib.length);
514 glib
.load(VFile("strokes.dat"));
516 //writefln("%s strokes loaded", glib.length);
517 detectedGlyph
= null;
523 if (event
== "Delete") {
524 if (curPattern
!is null) {
525 yesNoMessage
= "Remove '"~curPattern
.name
~"'?";
532 delegate (dchar ch
) {
533 if (!editingName
) return;
534 if (ch
== 27) { editingName
= false; frameChanged(); return; }
536 if (curGlyphName
.length
> 0) curGlyphName
= curGlyphName
[0..$-1];
546 if (ch
== 10 || ch
== 13) {
551 if (ch
>= ' ' && ch
< 127) {
552 curGlyphName
~= cast(char)ch
;