← Back to team overview

multi-touch-dev team mailing list archive

Re: Touch analysis

 

On Fri, May 07, 2010 at 06:37:20PM +0200, Ara Pulido wrote:
> On 05/07/2010 09:02 AM, Bryce Harrington wrote:
> > On Sun, Apr 11, 2010 at 07:55:43PM -0400, Rafi Rubin wrote:
> >> Added crude mt finger painting.
> >>
> >> Same url: http://ofb.net/~rafi/ts_test.tgz
> >>
> >> For multitouch mode run: ./ts_test -mt_dev /dev/input/eventNN
> >>
> >> Its really nothing fancy (not meant to be), but for some of you, this may be the
> >> first time you actually mt do anything.
> > 
> > This is pretty darn sweet.
> > 
> >> Please excuse the sloppy code, I'm still experimenting to decide how to
> >> code up a testing framework.  In this case I just started from
> >> testsprite.py from the pygame examples.
> > 
> > I did some minor tidying (see attached).
> 
> Am I the only one not seeing anything attached? ;-)

Let's try that again...

#!/usr/bin/env python
# like the testsprite.c that comes with sdl, this pygame version shows 
#   lots of sprites moving around.

import pygame, sys, os
from pygame.locals import *
from time import time
from stats import mean, stdev
import code

##import FastRenderGroup as FRG
import pygame.sprite as FRG
from math import sin, cos, pi
from evdev import Device, Event

def usage():
    print '''Usage:  ts_test.py [-mt_dev MULTITOUCH_DEVICE]

The first test is a static noise test.  It measures how the cursor
moves when you hold your finger steady for 5 seconds.

The second test measures noise as a circle is traced.

If the -mt_dev arg is provided and a valid multi-touch input device
specified, it registers multi-finger input.    

Options:
	-h		This help
        -flip		Set the doublebuffer flag
        -sw		Use software surfaces
        -hw             Use hardware surfaces
        -mt_dev <dev>   Multitouch device (E.g. /dev/input/eventN)
'''

class Thingy(FRG.DirtySprite):
    images = None
    def __init__(self):
        FRG.DirtySprite.__init__(self)
        self.image = Thingy.images[0]
        self.rect = self.image.get_rect()
        self.dirty = 2


class Static(FRG.DirtySprite):
    images = None
    def __init__(self):
        FRG.DirtySprite.__init__(self)
        self.image = Static.images[0]
        self.rect = self.image.get_rect()
        self.rect.x = randint(0, 3*screen_dims[0]/4)
	self.rect.y = randint(0, 3*screen_dims[1]/4)

def refresh():
	if not update_rects:
		screen.fill([0,0,0])

	if update_rects:
		sprites.clear(screen, background)
	sprites.update()

	rects = sprites.draw(screen)

	if update_rects:
		pygame.display.update(rects)
	else:
		pygame.display.flip()


def single_pos_noise():
	global background
	points = []
	done = False

	background = pygame.Surface(screen.get_size())
	background = background.convert()
	screen.fill([0,0,0])
	background.fill([0,0,0])
	font = pygame.font.Font(None, 32)
	text = font.render("touch the green circle to begin",True,(255, 255, 255))
	textpos = text.get_rect(centerx=screen.get_width()/2)
	background.blit(text, textpos)
	screen.blit(text, textpos)

	screen_center = (screen.get_width()/2,screen.get_height()/2)

	pygame.draw.circle(screen, pygame.Color("green"),screen_center,32,0)
	pygame.draw.circle(background, pygame.Color("green"),screen_center,32,0)
	big_circ_pos = pygame.Rect(0,0,64,64)
	big_circ_pos.center = screen_center;

	pygame.display.flip()
	mouse_down = False

	start = -1

	# time to register a click 
	click_time = 0.5

	while not done:
		for event in pygame.event.get():
			if event.type in [KEYDOWN, QUIT, JOYBUTTONDOWN]:
				exit()
			if event.type == MOUSEMOTION: 
				sprity.rect.center = event.pos
			if event.type == MOUSEBUTTONDOWN: 
				mouse_down = True
				points = [complex(*event.pos)]
		refresh()

		# must click at the center for long enough to count
		if mouse_down and big_circ_pos.contains(sprity.rect):
			if start < 0:
				start = time()
			elif (time() - start) >= click_time:
				done = True
		else:
			start = -1



	screen.fill([0,0,0])
	background.fill([0,0,0])
	pygame.draw.circle(screen, pygame.Color("red"),screen_center,32,0)
	pygame.draw.circle(background, pygame.Color("red"),screen_center,32,0)

	text = font.render("Collecting: don't move your finger",True,pygame.Color("red"))
	textpos = text.get_rect(centerx=screen.get_width()/2,centery=screen.get_height()/4)
	background.blit(text, textpos)
	screen.blit(text, textpos)


	pygame.display.flip()

#	text = font.render("Time: %d" % , int(time()-start),(255, 0, 0))
#	textpos = text.get_rect(centerx=screen.get_width()/2)
#	screen.blit(text, textpos)
#	for event in pygame.event.get():
#		if event.type in [KEYDOWN, QUIT, JOYBUTTONDOWN]:
#			done = True
#		if event.type == MOUSEMOTION: 
#			sprity.rect.center = event.pos
#		if event.type == MOUSEBUTTONDOWN:
#			inner = True
#			start = time()
	inner = True
	pygame.time.set_timer(1,500)
	collect_time = 5
	last_time = collect_time
	while inner:
		for event in pygame.event.get():
			if time() - collect_time > start:
				    inner = False
				    break
			if event.type == MOUSEMOTION: 
				sprity.rect.center = event.pos
				points.append(complex(*event.pos))
		cur_time = int(collect_time - (time() - start))
		if cur_time < last_time:
			last_time = cur_time
			text = font.render("%d" % cur_time,True,pygame.Color("red"))
			textpos = text.get_rect(centerx=screen.get_width()/2,centery=screen.get_height()/4-50)
			screen.fill([0,0,0],textpos)
			screen.blit(text, textpos)
		pygame.display.flip()

	pygame.time.set_timer(1,0)
#
#
	center = mean(points)
	npoints = map(lambda x: x-center,points)
##    print points
##    print npoints
	print "Static touch summary:"
	print "Collected "+str(points.__len__())+" points"
	if points.__len__() > 1:
		print "center      (" + str(int(center.real)) + "," + str(int(center.imag)) + ")"
		print "mean radius " + str(mean(map(complex.__abs__,npoints)))
		print "stdev       " + str(stdev(map(complex.__abs__,npoints)))

def circle_noise():
	global background
	points = []
	done = False

	background = pygame.Surface(screen.get_size())
	background = background.convert()
	screen.fill([0,0,0])
	background.fill([0,0,0])
	font = pygame.font.Font(None, 32)
	text = font.render("touch the green circle to begin",True,(255, 255, 255))
	textpos = text.get_rect(centerx=screen.get_width()/2)
	background.blit(text, textpos)
	screen.blit(text, textpos)

	screen_center = screen.get_rect().center

	big_circ_pos = pygame.Rect(0,0,36,36)
	big_circ_pos.center = screen_center;
	big_circ_pos.centery = screen.get_height()/2 - 180

	pygame.draw.circle(screen, pygame.Color("green"),big_circ_pos.center,16,0)
	pygame.draw.circle(background, pygame.Color("green"),big_circ_pos.center,16,0)
	pygame.draw.circle(screen, pygame.Color("white"),screen_center,180,1)
	pygame.draw.circle(background, pygame.Color("white"),screen_center,180,1)

	pygame.display.flip()
	mouse_down = False

	start = -1

	# time to register a click 
	click_time = 0.5

	while not done:
		for event in pygame.event.get():
			if event.type in [KEYDOWN, QUIT, JOYBUTTONDOWN]:
				exit()
			if event.type == MOUSEMOTION: 
				sprity.rect.center = event.pos
			if event.type == MOUSEBUTTONDOWN: 
				mouse_down = True
				points = [complex(*event.pos)]
		refresh()

		# must click at the center for long enough to count
		if mouse_down and big_circ_pos.contains(sprity.rect):
			if start < 0:
				start = time()
			elif (time() - start) >= click_time:
				done = True
		else:
			start = -1



	screen.fill([0,0,0])
	background.fill([0,0,0])
	pygame.draw.circle(screen, pygame.Color("red"),big_circ_pos.center,16,0)
	pygame.draw.circle(background, pygame.Color("red"),big_circ_pos.center,16,0)
	pygame.draw.circle(screen, pygame.Color("blue"),screen_center,180,1)
	pygame.draw.circle(background, pygame.Color("blue"),screen_center,180,1)

	text = font.render("Collecting: please trace the circle",True,pygame.Color("red"))
	textpos = text.get_rect(centerx=screen.get_width()/2,centery=screen.get_height()/4)
	background.blit(text, textpos)
	screen.blit(text, textpos)

	check_points = []
	for theta in map(lambda x:pi*x/6 + pi/2,range(1,12)):
		x = int(screen.get_rect().centerx + 180 * cos(theta))
		y = int(screen.get_rect().centery - 180 * sin(theta))
		pos = pygame.Rect(0,0,32,32)
		pos.center = (x,y)
		check_points.append(pos)
		pygame.draw.circle(screen, pygame.Color("red"),(x,y),6,0)
		pygame.draw.circle(background, pygame.Color("red"),(x,y),6,0)

	pygame.display.flip()
	refresh()

#	text = font.render("Time: %d" % , int(time()-start),(255, 0, 0))
#	textpos = text.get_rect(centerx=screen.get_width()/2)
#	screen.blit(text, textpos)
#	for event in pygame.event.get():
#		if event.type in [KEYDOWN, QUIT, JOYBUTTONDOWN]:
#			done = True
#		if event.type == MOUSEMOTION: 
#			sprity.rect.center = event.pos
#		if event.type == MOUSEBUTTONDOWN:
#			inner = True
#			start = time()

	dot_color = pygame.Color("purple")


	inner = True
	while inner:
		for event in pygame.event.get():
			if event.type in [KEYDOWN, QUIT, JOYBUTTONDOWN]:
				exit()
			if check_points.__len__() == 0 and big_circ_pos.contains(sprity.rect):
				    inner = False
				    break
			if event.type == MOUSEMOTION: 
				sprity.rect.center = event.pos
				points.append(complex(*event.pos))
				pygame.draw.rect(background,dot_color,pygame.Rect(event.pos[0],event.pos[1],0,0),1)
				for hit in filter(lambda p: p.collidepoint(event.pos), check_points):
					pygame.draw.circle(background, pygame.Color("green"),hit.center,6,0)
					check_points.remove(hit)

		refresh()
#
#
	center = mean(points)
	npoints = map(lambda x: x-center,points)
##    print points
##    print npoints
	print "Static touch summary:"
	print "Collected "+str(points.__len__())+" points"
	if points.__len__() > 0:
		print "center      (" + str(int(center.real)) + "," + str(int(center.imag)) + ")"
		print "mean radius " + str(mean(map(complex.__abs__,npoints)))
		print "stdev       " + str(stdev(map(complex.__abs__,npoints)))

dot_color = pygame.Color("purple")
mt_scale_x = 0
mt_scale_y = 0

mt_x = -1
mt_y = -1
mt_maj = 1
mt_min = 1
mt_orient = 1

def draw_point(x,y):
	global background, screen
	#print "drawing "+str(x)+" "+str(y)
	pygame.draw.rect(background,dot_color,pygame.Rect(x,y,0,0),1)
	pygame.draw.rect(screen,dot_color,pygame.Rect(x,y,0,0),1)

def draw_ellipse(rect):
	global background, screen
	#print "drawing "+str(x)+" "+str(y)
	pygame.draw.ellipse(screen,dot_color,rect)

def mt_event(event):
	global mt_x, mt_y, mt_scale_x, mt_scale_y, mt_maj, mt_min, mt_orient
	if event.type == 'EV_ABS':
		if event.code == 'ABS_MT_POSITION_X':
			mt_x = mt_scale_x * event.value
		elif event.code == 'ABS_MT_POSITION_Y':
			mt_y = mt_scale_y * event.value
		elif event.code == 'ABS_MT_TOUCH_MAJOR':
			mt_maj = event.value
		elif event.code == 'ABS_MT_TOUCH_MINOR':
			mt_min = event.value
		elif event.code == 'ABS_MT_ORIENTATION':
			mt_orient = event.value
	elif event.type == 'EV_SYN':
		if event.code == 'SYN_MT_REPORT':
			if mt_orient == 0:
				(mt_maj, mt_min) = (mt_min, mt_maj)
			if mt_maj > 1:
				mt_maj *= mt_scale_x*0.7
			if mt_min > 1:
				mt_min *= mt_scale_y*0.7
			#draw_point(mt_x,mt_y)
			draw_ellipse(Rect(mt_x-mt_maj/2,mt_y-mt_min/2,mt_maj,mt_min))
		elif event.code == 'SYN_REPORT':
			pygame.display.flip()
			mt_x = mt_y = -1

def mt_draw(mt_dev):
	global mt_x, mt_y, mt_scale_x, mt_scale_y, background
	points = []
	done = False
	
	try:
		info = mt_dev.absAxisInfo["ABS_MT_POSITION_X"]
		mt_scale_x = float(screen.get_rect().width)/(info['max']-info['min'])
		print "ABS_MT_POSITION_X range: " +str(info['min'])+":"+str(info['max'])
	except KeyError:
		return
	try:
		info = mt_dev.absAxisInfo["ABS_MT_POSITION_Y"]
		mt_scale_y = float(screen.get_rect().height)/(info['max']-info['min'])
		print "ABS_MT_POSITION_Y range: " +str(info['min'])+":"+str(info['max'])
	except KeyError:
		return


	background = pygame.Surface(screen.get_size())
	background = background.convert()
	screen.fill([0,0,0])
	background.fill([0,0,0])
	font = pygame.font.Font(None, 32)
	text = font.render("DRAW!!! (hit any key to quit)",True,(255, 255, 255))
	textpos = text.get_rect(centerx=screen.get_width()/2)
	background.blit(text, textpos)
	screen.blit(text, textpos)
	pygame.display.flip()
	import thread
	thread.start_new(mt_dev.listen,(mt_event,))


	while not done:
		ev_count = 0
		for event in pygame.event.get():
			ev_count+=1
			if event.type in [KEYDOWN, QUIT, JOYBUTTONDOWN]:
				exit()
#			if event.type == MOUSEMOTION: 
#				sprity.rect.center = event.pos
#			if event.type == MOUSEBUTTONDOWN: 
#				mouse_down = True

		if ev_count == 0:
			pygame.time.delay(100)
#		refresh()

		# must click at the center for long enough to count

def main():
    global update_rects, flags, screen, sprites, background, sprity

    # use this to use update rects or not.
    #  If the screen is mostly full, then update rects are not useful.
    update_rects = True
    use_FastRenderGroup = False

    use_rle = True
    mt_mode = False

    screen_dims = [0, 0]
    surfaces = []

    flags = 0
    flags ^= FULLSCREEN

    if "-h" in sys.argv:
        usage()
        sys.exit(0)

    if "-flip" in sys.argv:
        flags ^= DOUBLEBUF

    if "-sw" in sys.argv:
        flags ^= SWSURFACE

    if "-hw" in sys.argv:
        flags ^= HWSURFACE
        use_rle = False

    if "-mt_dev" in sys.argv:
	mt_mode = True
	mt_dev  = Device(sys.argv[sys.argv.index("-mt_dev")+1])

    sprite_surface = pygame.image.load("asprite.png")
    surfaces.append(sprite_surface)

    pygame.display.init()
    pygame.font.init()
    screen = pygame.display.set_mode(screen_dims, flags)
    screen.fill([0,0,0])
    pygame.display.flip()

    if use_rle:
	map(lambda x: x.set_colorkey([0xFF, 0xFF, 0xFF], SRCCOLORKEY|RLEACCEL), surfaces)
    else:
	map(lambda x: x.set_colorkey([0xFF, 0xFF, 0xFF], SRCCOLORKEY), surfaces)

    sprite_surface = sprite_surface.convert_alpha()
    Thingy.images = [sprite_surface]

    numsprites = 1
    sprites = None
    if use_FastRenderGroup:
        sprites = FRG.LayeredDirty()
    else:
        if update_rects:
            sprites = pygame.sprite.RenderUpdates()
        else:
            sprites = pygame.sprite.Group()
    sprity=Thingy()
    sprites.add(sprity)

    background = pygame.Surface(screen.get_size())
    background = background.convert()
    background.fill([0,0,0])
    try:
	    if mt_mode:
		    mt_draw(mt_dev)
		    return

	    single_pos_noise()
	    circle_noise()
    except KeyboardInterrupt:
	    return


if __name__ == "__main__":
    main()

References