Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
M
multitouch
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Taddeüs Kroes
multitouch
Commits
c8706a27
Commit
c8706a27
authored
Jun 17, 2012
by
Taddeüs Kroes
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added various fixes for transformations in the test application.
parent
f3801a8a
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
366 additions
and
30 deletions
+366
-30
src/__init__.py
src/__init__.py
+2
-1
src/areas.py
src/areas.py
+1
-15
src/geometry.py
src/geometry.py
+9
-1
src/trackers/transform.py
src/trackers/transform.py
+43
-7
test.py
test.py
+3
-3
tests/draw.py
tests/draw.py
+2
-2
tests/parse_arguments.py
tests/parse_arguments.py
+2
-1
tests/testapp.py
tests/testapp.py
+269
-0
tests/utils.py
tests/utils.py
+35
-0
No files found.
src/__init__.py
View file @
c8706a27
from
logger
import
Logger
from
tracker
import
GestureTracker
,
Gesture
from
drivers
import
create_driver
from
tracker
import
GestureTracker
,
Gesture
from
geometry
import
Positionable
from
areas
import
*
src/areas.py
View file @
c8706a27
...
...
@@ -31,21 +31,7 @@ class RectangularArea(Area):
and
self
.
y
<=
y
<=
self
.
y
+
self
.
height
def
contains_event
(
self
,
event
):
if
self
.
contains
(
*
event
.
get_position
()):
return
True
if
isinstance
(
event
,
PointMoveEvent
):
px
,
py
=
event
.
point
.
get_previous_position
()
if
self
.
parent
:
x
,
y
=
self
.
parent
.
get_screen_offset
()
else
:
x
=
y
=
0
if
self
.
contains
(
px
-
x
,
py
-
y
):
self
.
handle_event
(
PointUpEvent
(
event
.
point
))
return
False
return
self
.
contains
(
*
event
.
get_position
())
class
CircularArea
(
Area
):
...
...
src/geometry.py
View file @
c8706a27
from
__future__
import
division
from
math
import
atan2
,
pi
from
math
import
atan2
,
pi
,
sin
,
cos
import
time
...
...
@@ -60,6 +60,14 @@ class Positionable(object):
def
translate
(
self
,
x
,
y
):
self
.
set_position
(
self
.
x
+
x
,
self
.
y
+
y
)
def
scale
(
self
,
scale
):
self
.
set_position
(
self
.
x
*
scale
,
self
.
y
*
scale
)
def
rotate
(
self
,
angle
):
c
=
cos
(
angle
)
s
=
sin
(
angle
)
self
.
set_position
(
c
*
self
.
x
-
s
*
self
.
y
,
s
*
self
.
x
+
c
*
self
.
y
)
class
MovingPositionable
(
Positionable
):
"""
...
...
src/trackers/transform.py
View file @
c8706a27
...
...
@@ -21,7 +21,7 @@ class RotationGesture(Gesture, Positionable):
%
(
self
.
x
,
self
.
y
,
self
.
angle
)
def
get_angle
(
self
):
return
self
.
angle
return
-
self
.
angle
class
PinchGesture
(
Gesture
,
Positionable
):
...
...
@@ -84,6 +84,8 @@ class TransformationTracker(GestureTracker):
# Current and previous centroid of all touch points
self
.
centroid
=
None
self
.
deleted
=
[]
def
update_centroid
(
self
):
if
not
self
.
points
:
self
.
centroid
=
None
...
...
@@ -105,18 +107,28 @@ class TransformationTracker(GestureTracker):
def
on_point_down
(
self
,
event
):
self
.
points
.
append
(
event
.
point
)
self
.
update_centroid
()
event
.
stop_propagation
()
def
on_point_move
(
self
,
event
):
point
=
event
.
point
if
point
not
in
self
.
points
:
return
pid
=
point
.
get_id
()
if
pid
not
in
self
.
deleted
:
return
self
.
debug
(
'recovered %s'
%
point
)
self
.
deleted
.
remove
(
pid
)
self
.
points
.
append
(
point
)
self
.
update_centroid
()
event
.
stop_propagation
()
self
.
invalidate_points
()
l
=
len
(
self
.
points
)
if
l
>
1
:
offset_centroid
=
self
.
centroid
-
self
.
area
.
get_screen_offset
()
print
self
.
centroid
,
self
.
area
,
offset_centroid
# Rotation (around the previous centroid)
rotation
=
point
.
rotation_around
(
self
.
centroid
)
/
l
...
...
@@ -137,8 +149,32 @@ class TransformationTracker(GestureTracker):
self
.
centroid
.
translation
(),
l
))
def
on_point_up
(
self
,
event
):
if
event
.
point
not
in
self
.
points
:
return
if
event
.
point
in
self
.
points
:
self
.
points
.
remove
(
event
.
point
)
self
.
update_centroid
()
event
.
stop_propagation
()
def
invalidate_points
(
self
):
"""
Check if all points are still in the corresponding area, and remove
those which are not.
"""
delete
=
[]
if
self
.
area
.
parent
:
ox
,
oy
=
self
.
area
.
parent
.
get_screen_offset
()
else
:
ox
=
oy
=
0
self
.
points
.
remove
(
event
.
point
)
self
.
update_centroid
()
for
i
,
p
in
enumerate
(
self
.
points
):
x
,
y
=
p
.
get_position
()
if
not
self
.
area
.
contains
(
x
-
ox
,
y
-
oy
):
self
.
debug
(
'deleted %s'
%
p
)
delete
.
append
(
i
)
self
.
deleted
.
append
(
p
.
get_id
())
if
delete
:
self
.
points
=
[
p
for
i
,
p
in
enumerate
(
self
.
points
)
if
i
not
in
delete
]
self
.
update_centroid
()
test.py
View file @
c8706a27
#!/usr/bin/env python
import
os
src_path
=
os
.
path
.
realpath
(
'src'
)
import
sys
sys
.
path
.
insert
(
0
,
src_path
)
sys
.
path
.
insert
(
0
,
os
.
path
.
realpath
(
'src'
))
sys
.
path
.
insert
(
0
,
os
.
path
.
realpath
(
'tests'
))
del
sys
.
argv
[
0
]
execfile
(
sys
.
argv
[
0
])
tests/draw.py
View file @
c8706a27
#!/usr/bin/env python
lambda
g
:
#!/usr/bin/env python
from
__future__
import
division
import
pygame
from
threading
import
Thread
from
math
import
degrees
from
tests.parse_arguments
import
create_parser
,
parse_args
from
src
import
FullscreenArea
,
create_driver
from
src.screen
import
screen_size
from
parse_arguments
import
create_parser
,
parse_args
# Parse arguments
parser
=
create_parser
()
...
...
tests/parse_arguments.py
View file @
c8706a27
import
argparse
import
logging
from
src
.logger
import
Logger
from
src
import
Logger
# Parse arguments
...
...
@@ -18,6 +18,7 @@ def create_parser():
def
parse_args
(
parser
):
print
'here:'
,
parser
.
format_usage
()
args
=
parser
.
parse_args
()
# Configure logger
...
...
tests/
cairotest
.py
→
tests/
testapp
.py
View file @
c8706a27
#!/usr/bin/env python
from
__future__
import
division
import
gtk
import
cairo
from
math
import
radians
from
threading
import
Thread
from
math
import
pi
,
tan
import
src
as
mt
from
utils
import
BoundingBoxArea
RED
=
1
,
0
,
0
GREEN
=
0
,
1
,
0
BLUE
=
0
,
0
,
1
WHITE
=
1
,
1
,
1
BLACK
=
0
,
0
,
0
class
Rectangle
(
mt
.
RectangularArea
):
def
__init__
(
self
,
x
,
y
,
width
,
height
,
color
=
(
1
,
0
,
0
)):
super
(
Rectangle
,
self
).
__init__
(
x
,
y
,
width
,
height
)
self
.
w
=
width
self
.
h
=
height
self
.
scale
=
1
self
.
angle
=
0
self
.
color
=
color
self
.
t
=
cairo
.
Matrix
()
self
.
t
.
translate
(
x
,
y
)
self
.
on_drag
(
self
.
handle_drag
)
self
.
on_drag
(
self
.
move
)
self
.
on_pinch
(
self
.
resize
)
#self.on_rotate(self.rotate)
def
move
(
self
,
g
):
print
'move:'
,
g
self
.
translate
(
*
g
.
get_translation
())
self
.
ttrans
(
*
g
.
get_translation
())
def
handle_drag
(
self
,
g
):
tx
,
ty
=
g
.
get_translation
()
self
.
translate
(
tx
,
ty
)
refresh
()
def
resize
(
self
,
g
):
print
'resize:'
,
g
def
draw
(
self
,
cr
):
cr
.
rectangle
(
self
.
x
,
self
.
y
,
self
.
width
,
self
.
height
)
cr
.
set_source_rgb
(
*
self
.
color
)
cr
.
fill
()
x
,
y
=
g
.
get_position
()
scale
=
g
.
get_scale
()
self
.
ttrans
(
x
,
y
)
self
.
tscale
(
scale
)
self
.
ttrans
(
-
x
,
-
y
)
self
.
translate
(
x
-
x
*
scale
,
y
-
y
*
scale
)
class
Polygon
(
BoundingBoxArea
):
def
__init__
(
self
,
x
,
y
,
points
,
color
=
BLUE
,
border_color
=
RED
):
super
(
Polygon
,
self
).
__init__
(
x
,
y
,
points
)
self
.
fill_color
=
color
self
.
border_color
=
border_color
self
.
width
*=
scale
self
.
height
*=
scale
self
.
on_drag
(
self
.
handle_drag
)
self
.
on_pinch
(
self
.
handle_pinch
)
self
.
on_rotate
(
self
.
handle_rotate
)
def
handle_drag
(
self
,
g
):
tx
,
ty
=
g
.
get_translation
()
self
.
translate
(
tx
,
ty
)
refresh
()
def
rotate
(
self
,
g
):
print
'rotate:'
,
g
x
,
y
=
g
.
get_position
()
self
.
ttrans
(
x
,
y
)
self
.
trot
(
-
g
.
get_angle
())
self
.
ttrans
(
-
x
,
-
y
)
def
handle_pinch
(
self
,
g
):
cx
,
cy
=
g
.
get_position
()
self
.
scale_points
(
g
.
get_scale
(),
cx
,
cy
)
self
.
update_bounds
()
refresh
()
def
handle_rotate
(
self
,
g
):
cx
,
cy
=
g
.
get_position
()
self
.
rotate_points
(
g
.
get_angle
(),
cx
,
cy
)
self
.
update_bounds
()
refresh
()
def
ttrans
(
self
,
tx
,
ty
):
t
=
cairo
.
Matrix
()
t
.
translate
(
tx
,
ty
)
self
.
t
=
t
*
self
.
t
def
draw
(
self
,
cr
):
# Draw bounding box
if
draw_bounding_boxes
:
cr
.
rectangle
(
self
.
x
,
self
.
y
,
self
.
width
,
self
.
height
)
cr
.
set_source_rgb
(
*
self
.
border_color
)
cr
.
set_line_width
(
3
)
cr
.
stroke
()
def
tscale
(
self
,
s
):
t
=
cairo
.
Matrix
()
t
.
scale
(
s
,
s
)
self
.
t
=
t
*
self
.
t
# Fill polygon
cr
.
translate
(
self
.
x
,
self
.
y
)
cr
.
new_path
()
def
trot
(
self
,
a
):
t
=
cairo
.
Matrix
()
t
.
rotate
(
a
)
self
.
t
=
t
*
self
.
t
for
x
,
y
in
zip
(
*
self
.
points
):
cr
.
line_to
(
x
,
y
)
def
draw
(
self
,
cr
):
cr
.
transform
(
self
.
t
)
cr
.
rectangle
(
0
,
0
,
self
.
w
,
self
.
h
)
cr
.
set_source_rgb
(
*
self
.
color
)
cr
.
set_source_rgb
(
*
self
.
fill_color
)
cr
.
fill
()
fullscreen
=
False
draw_bounding_boxes
=
draw_touch_points
=
True
W
,
H
=
mt
.
screen
.
screen_size
...
...
@@ -92,10 +94,12 @@ def create_context_window(w, h, callback):
"""Synchronize root multi-touch area with GTK window."""
root
.
set_position
(
*
event
.
get_coords
())
root
.
set_size
(
event
.
width
,
event
.
height
)
overlay
.
set_size
(
event
.
width
,
event
.
height
)
draw
()
def
handle_key
(
win
,
event
):
"""Handle key event. 'f' toggles fullscreen, 'q' exits the program."""
"""Handle key event. 'f' toggles fullscreen, 'q' exits the program, 'b'
toggles bounding boxes, 'p' toggles touch points."""
if
event
.
keyval
>=
256
:
return
...
...
@@ -107,10 +111,19 @@ def create_context_window(w, h, callback):
fullscreen
=
not
fullscreen
elif
key
==
'q'
:
quit
()
elif
key
==
'b'
:
global
draw_bounding_boxes
draw_bounding_boxes
=
not
draw_bounding_boxes
refresh
()
elif
key
==
'p'
:
global
draw_touch_points
draw_touch_points
=
not
draw_touch_points
refresh
()
# Root area (will be synchronized with GTK window)
global
root
global
root
,
overlay
root
=
mt
.
RectangularArea
(
0
,
0
,
w
,
h
)
overlay
=
mt
.
RectangularArea
(
0
,
0
,
w
,
h
)
# GTK window
global
window
...
...
@@ -121,6 +134,9 @@ def create_context_window(w, h, callback):
window
.
connect
(
'configure-event'
,
move_window
)
window
.
connect
(
'show'
,
callback
)
if
fullscreen
:
window
.
fullscreen
()
# Drawing area, needed by cairo context for drawing
area
=
gtk
.
DrawingArea
()
area
.
set_size_request
(
w
,
h
)
...
...
@@ -137,7 +153,7 @@ def draw(*args):
# Background
cr
.
rectangle
(
0
,
0
,
*
root
.
get_size
())
cr
.
set_source_rgb
(
0
,
1
,
0
)
cr
.
set_source_rgb
(
*
BLACK
)
cr
.
fill
()
# Drawable objects (use save and restore to allow transformations)
...
...
@@ -146,6 +162,24 @@ def draw(*args):
obj
.
draw
(
cr
)
cr
.
restore
()
if
draw_touch_points
:
ox
,
oy
=
root
.
get_position
()
cr
.
set_source_rgb
(
*
WHITE
)
for
x
,
y
in
touch_points
.
itervalues
():
x
-=
ox
y
-=
oy
cr
.
set_line_width
(
3
)
cr
.
arc
(
x
,
y
,
20
,
0
,
2
*
pi
)
cr
.
stroke
()
cr
.
set_line_width
(
1
)
cr
.
move_to
(
x
-
8
,
y
)
cr
.
line_to
(
x
+
8
,
y
)
cr
.
move_to
(
x
,
y
-
8
)
cr
.
line_to
(
x
,
y
+
8
)
cr
.
stroke
()
def
refresh
():
window
.
queue_draw
()
...
...
@@ -156,8 +190,13 @@ def quit(*args):
# Initialization
window
=
cr
=
root
=
None
window
=
cr
=
root
=
overlay
=
None
draw_objects
=
[]
touch_points
=
{}
def
triangle_height
(
width
):
return
abs
(.
5
*
width
*
tan
(
2
/
3
*
pi
))
def
on_show
(
window
):
...
...
@@ -165,18 +204,53 @@ def on_show(window):
root
.
on_double_tap
(
root_dtap
)
# Create blue rectangle
rect
=
Rectangle
(
300
,
200
,
250
,
150
,
color
=
(
0
,
0
,
1
))
x
,
y
,
w
,
h
=
0
,
0
,
250
,
150
rect
=
Polygon
(
x
,
y
,
[(
0
,
0
),
(
0
,
h
),
(
w
,
h
),
(
w
,
0
)])
draw_objects
.
append
(
rect
)
root
.
add_area
(
rect
)
def
rect_tap
(
g
):
print
'tapped on rectangle'
rect
.
on_tap
(
rect_tap
,
propagate_up_event
=
False
)
# Create green triangle
x
,
y
,
w
=
400
,
400
,
200
h
=
triangle_height
(
w
)
triangle
=
Polygon
(
x
,
y
,
[(
0
,
h
),
(
w
,
h
),
(
w
/
2
,
0
)],
color
=
GREEN
)
draw_objects
.
append
(
triangle
)
root
.
add_area
(
triangle
)
# Overlay catches basic events
def
handle_down
(
gesture
):
point
=
gesture
.
get_event
().
get_touch_object
()
touch_points
[
point
.
get_id
()]
=
point
.
get_position
()
if
draw_touch_points
:
refresh
()
def
handle_up
(
gesture
):
point
=
gesture
.
get_event
().
get_touch_object
()
del
touch_points
[
point
.
get_id
()]
if
draw_touch_points
:
refresh
()
overlay
.
on_point_down
(
handle_down
)
overlay
.
on_point_move
(
handle_down
)
overlay
.
on_point_up
(
handle_up
)
root
.
add_area
(
overlay
)
if
__name__
==
'__main__'
:
from
parse_arguments
import
create_parser
,
parse_args
# Parse arguments
from
tests.parse_arguments
import
create_parser
,
parse_args
parse_args
(
create_parser
())
parser
=
create_parser
()
parser
.
add_argument
(
'-f'
,
'--fullscreen'
,
action
=
'store_true'
,
default
=
False
,
help
=
'run in fullscreen initially'
)
args
=
parse_args
(
parser
)
fullscreen
=
args
.
fullscreen
# Create a window with a Cairo context in it and a multi-touch area
# syncronized with it
...
...
tests/utils.py
0 → 100644
View file @
c8706a27
from
numpy
import
array
,
diag
,
dot
,
cos
,
sin
from
src
import
RectangularArea
class
BoundingBoxArea
(
RectangularArea
):
def
__init__
(
self
,
x
,
y
,
points
):
super
(
BoundingBoxArea
,
self
).
__init__
(
x
,
y
,
0
,
0
)
self
.
points
=
array
(
points
).
T
self
.
update_bounds
()
def
translate_points
(
self
,
tx
,
ty
):
self
.
points
+=
[[
tx
],
[
ty
]]
def
scale_points
(
self
,
scale
,
cx
,
cy
):
self
.
translate_points
(
-
cx
,
-
cy
)
self
.
points
=
dot
(
diag
([
scale
,
scale
]),
self
.
points
)
self
.
translate_points
(
cx
,
cy
)
def
rotate_points
(
self
,
angle
,
cx
,
cy
):
cosa
=
cos
(
angle
)
sina
=
sin
(
angle
)
mat
=
array
([[
cosa
,
-
sina
],
[
sina
,
cosa
]])
self
.
translate_points
(
-
cx
,
-
cy
)
self
.
points
=
dot
(
mat
,
self
.
points
)
self
.
translate_points
(
cx
,
cy
)
def
update_bounds
(
self
):
min_x
,
min_y
=
self
.
points
.
min
(
1
)
max_x
,
max_y
=
self
.
points
.
max
(
1
)
self
.
set_size
(
max_x
-
min_x
,
max_y
-
min_y
)
if
min_x
or
min_y
:
self
.
translate
(
min_x
,
min_y
)
self
.
translate_points
(
-
min_x
,
-
min_y
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment