From 6c79508558d09f288b8a01bad0a7c35cb489fb7b Mon Sep 17 00:00:00 2001 From: Thomas Perl Date: Sat, 23 May 2009 16:18:09 +0200 Subject: [PATCH] Support for multi-touch stroke drawing in tuioinput.py This is the first "multi-touch" drawing code for NP. No other events supported at the moment. Also fixed some memory allocation issues in Multitouch.cpp. --- Makefile.am | 3 +- Multitouch.cpp | 56 ++++++++++++++------------- PythonInput.cpp | 55 +++++++++++++------------- tuioinput.py | 117 ++++++++++++++++++++++++++++++++++++++++---------------- 4 files changed, 144 insertions(+), 87 deletions(-) diff --git a/Makefile.am b/Makefile.am index 0b7a439..75f4369 100644 --- a/Makefile.am +++ b/Makefile.am @@ -28,7 +28,8 @@ numptyphysics_SOURCES = \ OsHildon.cpp \ OsFreeDesktop.cpp \ OsWin32.cpp \ - PythonInput.cpp + PythonInput.cpp \ + Multitouch.cpp numptyphysics_CPPFLAGS = -IXX $(SDL_CFLAGS) $(HILDON_CFLAGS) $(PYTHON_CFLAGS) numptyphysics_LDADD = libbox2d.a $(SDL_LIBS) $(HILDON_LIBS) $(PYTHON_LIBS) diff --git a/Multitouch.cpp b/Multitouch.cpp index 82387c1..7ac2de4 100644 --- a/Multitouch.cpp +++ b/Multitouch.cpp @@ -2,6 +2,8 @@ #include #include +#define allocate(x) ((x*)malloc(sizeof(x))) + void queueDrawEvent(int type, int cursor_id, int x, int y); void queueDraggingEvent(int type, int cursor_id, int x, int y, int xdiff, int ydiff); @@ -37,12 +39,12 @@ void queueFinishRopeEvent(int cursor_id, int x, int y) void queueDrawEvent(int type, int cursor_id, int x, int y) { - DrawEvent de; - de.cursor_id = cursor_id; - de.x = x; - de.y = y; + DrawEvent* de = allocate(DrawEvent); + de->cursor_id = cursor_id; + de->x = x; + de->y = y; - queueUserEvent(type, &de, 0); + queueUserEvent(type, de, 0); } void queueStartDragEvent(int cursor_id, int x, int y, int xdiff, int ydiff) @@ -62,42 +64,42 @@ void queueEndDragEvent(int cursor_id, int x, int y, int xdiff, int ydiff) void queueDraggingEvent(int type, int cursor_id, int x, int y, int xdiff, int ydiff) { - DragEvent de; - de.cursor_id = cursor_id; - de.x = x; - de.y = y; - de.xdiff = xdiff; - de.ydiff = ydiff; - - queueUserEvent(type, &de, 0); + DragEvent* de = allocate(DragEvent); + de->cursor_id = cursor_id; + de->x = x; + de->y = y; + de->xdiff = xdiff; + de->ydiff = ydiff; + + queueUserEvent(type, de, 0); } void queuePanEvent(int xdiff, int ydiff) { - PanEvent pe; - pe.xdiff = xdiff; - pe.ydiff = ydiff; + PanEvent* pe = allocate(PanEvent); + pe->xdiff = xdiff; + pe->ydiff = ydiff; - queueUserEvent(SDL_NP_PAN, &pe, 0); + queueUserEvent(SDL_NP_PAN, pe, 0); } void queueZoomEvent(int x, int y, float zoomfactor) { - ZoomEvent ze; - ze.x = x; - ze.y = y; - ze.zoomfactor = zoomfactor; + ZoomEvent* ze = allocate(ZoomEvent); + ze->x = x; + ze->y = y; + ze->zoomfactor = zoomfactor; - queueUserEvent(SDL_NP_ZOOM, &ze, 0); + queueUserEvent(SDL_NP_ZOOM, ze, 0); } void queueDeleteEvent(int x, int y) { - DeleteEvent de; - de.x = x; - de.y = y; + DeleteEvent* de = allocate(DeleteEvent); + de->x = x; + de->y = y; - queueUserEvent(SDL_NP_DELETE, &de, 0); + queueUserEvent(SDL_NP_DELETE, de, 0); } void queueUserEvent(int type, void *data1, void *data2) @@ -107,6 +109,8 @@ void queueUserEvent(int type, void *data1, void *data2) event.user.data1 = data1; event.user.data2 = data2; + fprintf(stderr, "Posting event: %d (%p, %p)\n", type, data1, data2); + if (SDL_PushEvent(&event) != 0) { fprintf(stderr, "could not push SDL event onto queue"); diff --git a/PythonInput.cpp b/PythonInput.cpp index 7f0d788..e5c374f 100644 --- a/PythonInput.cpp +++ b/PythonInput.cpp @@ -16,6 +16,7 @@ */ #include "PythonInput.h" +#include "Multitouch.h" #ifdef HAVE_PYTHON @@ -31,14 +32,23 @@ PyObject* PythonInput::create_module() PyObject* module; module = Py_InitModule("numptyphysics", NumptyMethods); - PyModule_AddIntConstant(module, "BUTTONDOWN", SDL_MOUSEBUTTONDOWN); - PyModule_AddIntConstant(module, "BUTTONUP", SDL_MOUSEBUTTONUP); - PyModule_AddIntConstant(module, "MOTION", SDL_MOUSEMOTION); - PyModule_AddIntConstant(module, "BUTTONLEFT", SDL_BUTTON_LEFT); - PyModule_AddIntConstant(module, "BUTTONMIDDLE", SDL_BUTTON_MIDDLE); - PyModule_AddIntConstant(module, "BUTTONRIGHT", SDL_BUTTON_RIGHT); + + PyModule_AddIntConstant(module, "START_STROKE", SDL_NP_START_STROKE); + PyModule_AddIntConstant(module, "APPEND_STROKE", SDL_NP_APPEND_STROKE); + PyModule_AddIntConstant(module, "FINISH_STROKE", SDL_NP_FINISH_STROKE); + PyModule_AddIntConstant(module, "START_ROPE", SDL_NP_START_ROPE); + PyModule_AddIntConstant(module, "APPEND_ROPE", SDL_NP_APPEND_ROPE); + PyModule_AddIntConstant(module, "FINISH_ROPE", SDL_NP_FINISH_ROPE); + PyModule_AddIntConstant(module, "START_DRAG", SDL_NP_START_DRAG); + PyModule_AddIntConstant(module, "DRAG", SDL_NP_DRAG); + PyModule_AddIntConstant(module, "END_DRAG", SDL_NP_END_DRAG); + PyModule_AddIntConstant(module, "PAN", SDL_NP_PAN); + PyModule_AddIntConstant(module, "ZOOM", SDL_NP_ZOOM); + PyModule_AddIntConstant(module, "DELETE", SDL_NP_DELETE); + PyModule_AddIntConstant(module, "WIDTH", m_width); PyModule_AddIntConstant(module, "HEIGHT", m_height); + PyModule_AddIntConstant(module, "MAX_CURSORS", MT_MAX_CURSORS); return module; } @@ -47,7 +57,7 @@ PyObject* PythonInput::post_event(PyObject* self, PyObject* event) { int xpos = -1, ypos = -1; int type = -1; - int button = -1; + int cursor_id = -1; PyObject *o = NULL; SDL_Event e = {0}; @@ -67,7 +77,7 @@ PyObject* PythonInput::post_event(PyObject* self, PyObject* event) /* Get Y position */ o = PyObject_GetAttrString(event, "y"); if (PyInt_Check(o)) { - ypos = (int)PyInt_AsLong(o)-45; + ypos = (int)PyInt_AsLong(o); } else { fprintf(stderr, "y not a number: "); PyObject_Print(o, stderr, 0); @@ -84,30 +94,23 @@ PyObject* PythonInput::post_event(PyObject* self, PyObject* event) } Py_DECREF(o); - /* Get button */ - o = PyObject_GetAttrString(event, "button"); + /* Get cursor_id */ + o = PyObject_GetAttrString(event, "cursor_id"); if (PyInt_Check(o)) { - button = (int)PyInt_AsLong(o); + cursor_id = (int)PyInt_AsLong(o); } else { - fprintf(stderr, "button is not an int!\n"); + fprintf(stderr, "cursor_id is not an int!\n"); } Py_DECREF(o); -// fprintf(stderr, "blubba! (x=%d, y=%d, type=%d, button=%d)\n", xpos, ypos, type, button); - e.type = type; - if (type == SDL_MOUSEMOTION) { - /* mouse motion event */ - e.motion.x = (Uint16)xpos; - e.motion.y = (Uint16)ypos; - /* not setting state, xrel, yrel */ - SDL_PushEvent(&e); + if (type == SDL_NP_START_STROKE) { + queueStartStrokeEvent(cursor_id, xpos, ypos); + } else if (type == SDL_NP_FINISH_STROKE) { + queueFinishStrokeEvent(cursor_id, xpos, ypos); + } else if (type == SDL_NP_APPEND_STROKE) { + queueAppendStrokeEvent(cursor_id, xpos, ypos); } else { - /* mouse button event */ - e.button.button = (Uint8)button; - e.button.x = (Uint16)xpos; - e.button.y = (Uint16)ypos; - /* not setting state */ - SDL_PushEvent(&e); + fprintf(stderr, "Warning: unknown event type %d.\n"); } //return PyErr_Format(PyExc_TypeError, "This function needs a bot class to work"); diff --git a/tuioinput.py b/tuioinput.py index 05cb729..20f7bf0 100644 --- a/tuioinput.py +++ b/tuioinput.py @@ -19,6 +19,12 @@ import sys import threading import time +import collections +import os.path + +# Create the file "use-ipod" if you want to use an iPod Touch +# and its OSCemote application for TUIO input (rm it if you don't) +use_ipod = os.path.exists('use-ipod') try: import numptyphysics @@ -37,51 +43,94 @@ except ImportError, ioe: """ sys.exit(2) +class CursorTracker(object): + def __init__(self): + self._seen = {} + self._coords = {} + self._freeslots = collections.deque(range(numptyphysics.MAX_CURSORS)) + self.events = collections.deque() + + def update(self, cursors): + vanished = list(self._seen) + for cursor in cursors: + id, x, y = cursor.sessionid, cursor.xpos, cursor.ypos + if use_ipod: + x, y = y, 1.-x + x, y = self.convert_coords(x, y) + if id in self._seen: + # cursor still active + vanished.remove(id) + if self._coords[id] != (x, y): + self._coords[id] = (x, y) + self.push_event(id, numptyphysics.APPEND_STROKE) + else: + # found a new cursor + taken_slot = self.grabslot() + if taken_slot is None: + print 'ignoring excessive cursor' + continue + self._seen[id] = taken_slot + self._coords[id] = (x, y) + self.push_event(id, numptyphysics.START_STROKE) + + for id in vanished: + # the cursor vanished + self._coords[id] = (self._coords[id][0]+2, self._coords[id][1]+2) + self.push_event(id, numptyphysics.FINISH_STROKE) + self.freeslot(self._seen[id]) + del self._coords[id] + del self._seen[id] + + def grabslot(self): + try: + return self._freeslots.pop() + except IndexError: + return None + + def freeslot(self, slot): + self._freeslots.appendleft(slot) + + def push_event(self, id, type_): + x, y = self._coords[id] + cursor_id = self._seen[id] + self.events.appendleft(InputEvent(x, y, type_, cursor_id)) + + def convert_coords(self, x, y): + return (int(x*numptyphysics.WIDTH), int(y*numptyphysics.HEIGHT)) + + def get_events(self): + try: + while True: + yield self.events.pop() + except IndexError, ie: + raise StopIteration() class InputEvent(object): - def __init__(self, x, y, event_type, button=0): + def __init__(self, x, y, event_type, cursor_id=0): self.x = x self.y = y self.event_type = event_type - self.button = button + self.cursor_id = cursor_id class C(threading.Thread): def run(self): - tracking = tuio.Tracking() + tracking = tuio.Tracking('') + tracker = CursorTracker() oldcount = False x, y = None, None while True: - tracking.update() - cursor = None - count = 0 - for cur in sorted(tracking.cursors(), cmp=lambda a, b: a.sessionid-b.sessionid): - if cursor is None: - cursor = cur - count += 1 - - if cursor is not None: - cx, cy = int(cursor.xpos*numptyphysics.WIDTH), int(cursor.ypos*numptyphysics.HEIGHT) - - button = numptyphysics.BUTTONLEFT - if count == 2: - button = numptyphysics.BUTTONMIDDLE - elif count == 3: - button = numptyphysics.BUTTONRIGHT - - if oldcount != count: - x, y = cx, cy - if oldcount != 0: - numptyphysics.post_event(InputEvent(x, y, numptyphysics.BUTTONUP, button)) - if count != 0: - numptyphysics.post_event(InputEvent(x, y, numptyphysics.BUTTONDOWN, button)) - elif count: - # motion - if cx != x or cy != y: - x, y = cx, cy - numptyphysics.post_event(InputEvent(x, y, numptyphysics.MOTION)) - - oldcount = count - time.sleep(1./30.) + while tracking.update(): + # read the socket empty + pass + + tracker.update(tracking.cursors()) + + for event in tracker.get_events(): + numptyphysics.post_event(event) + + while not tracking.update(): + # wait for something to happen + time.sleep(1./30.) C().start() -- 2.11.4.GIT