CAPITOLO 3: SVILUPPO E APPLICAZIONE DEL NUOVO “FAN-LIKE”
E. Script di Matlab per la calibrazione del sensore
F1. Sani convergenti F2. Sani divergenti F3. Post-ictus convergenti F4. Post-ictus divergenti F5. Cerebellari convergenti F6. Cerebellari divergenti
G. Script di Matlab per la valutazione dell’FDE G1. Sani convergenti G2. Sani divergenti G3. Post-ictus convergenti G4. Post-ictus divergenti G5. Cerebellari convergenti G6. Cerebellari divergenti
A. Scala di Ashworth Modificata
0 Nessuna spasticità
1 Moderata spasticità alla fine del movimento
1+ Moderata e continua spasticità durante meno della metà del movimento 2 Moderata spasticità durante tutto il movimento
3 Spasticità importante con limitazione severa del movimento 4 Spasticità irriducibile
114 B. Scala Fugl-Meyer per l’arto superiore
B1. Motricità
I Attività riflessa Flessori 0 1 2
Estensori 0 1 2
II
Movimenti nelle sinergie patologiche
Flessoria Scapola Retrazione 0 1 2 Elevazione 0 1 2 Spalla Abduzione 0 1 2 Rotazione esterna 0 1 2 Gomito Flessione 0 1 2 Avambraccio Supinazione 0 1 2 Estensoria Spalla Rotazione
interna
0 1 2
Adduzione 0 1 2 Gomito Estensione 0 1 2 Avambraccio Pronazione 0 1 2
III Iniziale indipendenza dalle sinergie patologiche
Mano alla regione sacrale 0 1 2
Flessione spalla 90° 0 1 2
Pronosupinazione a gomito flesso 90° 0 1 2
IV Relativa indipendenza dalle sinergie patologiche
Abduzione spalla a 90° 0 1 2 Flessione spalla a 180° 0 1 2 Pronosupinazione a gomito esteso 0 1 2
V Attività riflessa normale 0 1 2
Coordinazione-velocità Tremore 0 1 2 Velocità 0 1 2 Dismetria 0 1 2 Polso Stabilizzazione gomito a 90° 0 1 2 Stabilizzazione gomito esteso 0 1 2 Flesso-estensione gomito a 90° 0 1 2 Flesso-estensione gomito esteso 0 1 2
Circonduzione 0 1 2 Mano Flessione globale 0 1 2 Estensione globale 0 1 2 Presa a gancio 0 1 2 Presa laterale 0 1 2 Presa palmare 0 1 2 Presa cilindrica 0 1 2 Presa sferica 0 1 2 Totale /66
115 B2. Sensibilità Tattile Braccio 0 1 2 Palmo 0 1 2 Propriocettiva Spalla 0 1 2 Gomito 0 1 2 Polso 0 1 2 Pollice 0 1 2 Totale /12 B3. ROM-Dolore Spalla Flessione 0 1 2 0 1 2 Abduzione 0 1 2 0 1 2 Rotazione interna 0 1 2 0 1 2 Rotazione esterna 0 1 2 0 1 2 Gomito Flessione 0 1 2 0 1 2 Estensione 0 1 2 0 1 2 Supinazione 0 1 2 0 1 2 Pronazione 0 1 2 0 1 2
Polso e dita Flessione del polso 0 1 2 0 1 2
Estensione del polso 0 1 2 0 1 2
Flessione delle dita 0 1 2 0 1 2
Estensione delle dita 0 1 2 0 1 2
116 C. Mini Mental State Examination
AREE INDAGATE PUNTEGGIO
CONSEGUITO
PUNTEGGIO MASSIMO orientamento
data,giorno mese stagione anno 5
nazione, regione, città, luogo, reparto 5
memoria recente
nomi di tre soggetti 3
attenzione e calcolo
numerazione al contrario (100, 93,86) oppure sillabare al contrario (mondo=odnom)
5
richiamo alla memoria
ricordare i nomi di tre soggetti prima menzionati 3
linguaggio
identificazione della matita e dell’orologio 2
ripetere “sopra la panca la capra campa” 1
ordine: “prenda il foglio, lo pieghi a metà e lo metta sul pavimento”
3
esecuzione comando scritto “chiuda gli occhi” 1
scrittura di una frase di senso compiuto 1
copia del disegno (pentagoni intersecati) 1
117 D. Script del nuovo “fan-like”
#! /usr/bin/wish
# Copyright 2000-2004 Interactive Motion Technologies, Inc # trb 9/2000
# original comments:
# user moves cursor to touch blinking ball, # then blinking ball changes.
# alternating between center ball and edge balls.
# to make the game challenging, you may enter a threshold time # in milliseconds, and if you keep a floating average (over the last # 10 hits) below that number, the face stays green, or else it changes # to red.
# the number of spots on the perimeter may be specified (default 8). # the travel of the ball may be set to different increments, or
# to random.
# new comments, rtlinux port, 1/2004 # japanese i18n 3/2004
# more cleanup 7/2004
# This clock game is the basis of a battery of therapy and
# evaluation games. These games are all variations on the clock game, # with different settings specified in the games/ directory.
# this file contains a bunch of procs, and one call to main. # Tk GUI library
package require Tk package require counter global ob
if {[info exists env(CROB_HOME)]} { set ob(crobhome) $env(CROB_HOME) } else {
set ob(crobhome) /home/imt/crob }
#source $ob(crobhome)/shm.tcl
source $env(IMT_HOME)/crob/shm.tcl
# print a string, with a difference in ms since the last dtime set ob(dtimetime) [clock clicks -m]
118 global ob
set time [clock clicks -m]
set dtime [expr {$time - $ob(dtimetime)}] puts "dtime: $dtime - $str"
set ob(dtimetime) $time }
# print a stack trace
proc call_trace {{file stdout}} { puts $file "Tcl Call Trace"
for {set x [expr {[info level] -1}]} {$x > 0} {incr x -1} { puts $file "$x: [info level $x]"
} }
# print info on all current pending after events
proc after_info {} {foreach i [after info] {puts "$i [after info $i]"}} # counter aliases
interp alias {} ctadd {} ::counter::count interp alias {} ctget {} ::counter::get interp alias {} ctinit {} ::counter::init interp alias {} ctreset {} ::counter::reset # main
# this proc is called once (at the bottom of this file) # when the program is first run.
proc main {} { global ob argc argv
# game name and patient name
# they come in as command line args, usually from the # cons "game console" program
# in a HIPAA setting, the patient name will be a numeric ID. set ob(gamename) games/eval/test_log_frc
set ob(patname) dfltpat if {$argc >= 1} {
set ob(gamename) [lindex $argv 0] }
if {$argc >= 2} {
set ob(patname) [lindex $argv 1] }
# put the game and patient name in the title bar wm title . "$ob(gamename) $ob(patname)" # read in game support procs
119 source ../common/menu.tcl
source /home/ospedale/imt/i18n/i18n.tcl # is this planar or wrist?
localize_robot
set ob(programname) clock-$ob(gamename) font create default -family Times -size -18
# if you want to run with no arm and mouse cursor, # uncomment this following line:
# no_arm
# this initializes a bunch of variables, called once. onceg1
# this starts the game, may be called many times. # it is also bound to the n (new game) key. restartg1
}
# for init files
# the s command means "set." for instance, there is a variable called # "log" to turn on logging. if the game init file has the line:
# s log yes # then this proc will: # set ob(log) yes proc s {name val} { global ob
set ob($name) $val }
# turn beeping on and off, in response to a typed "b" proc toggle_nbeep {} {
global ob
if {$ob(dobeep)} { # was yes.
rename nbeep real_nbeep proc nbeep {args} {} set ob(dobeep) no } else {
# was no
rename nbeep {}
rename real_nbeep nbeep set ob(dobeep) yes }
120 # read the init file for the game specified.
# these flies consist of s (set) commands, and source commands, which # read other init files. this allows the differences between similar # games to be described in a simple way.
proc read_game_init {gamefilename} { set odir [pwd]
# normalize isn't available until tcl 8.4. not that important.
# set dir [file normalize [file join [pwd] [file dirname [info script] ... ]]] set dir [file join [pwd] [file dirname [info script]]\
[file dirname $gamefilename] ] set tail [file tail $gamefilename] if {[file isdirectory $dir]} {
cd $dir
# puts "cd $dir" } else {
error "read_game_init: can't source $dir $tail" }
if {[file exists $tail]} { source $tail
# puts "source $gamefilename" } else {
error "read_game_init: can't source $dir $tail" }
cd $odir }
# after the game init variables are set in the GUI program, some of # the values need to be sent to the control loop in the Linux Kernel # Module (using wshm - write shared memory), and some other values # need to be calculated. proc apply_init_vars {} { global ob wshm offset_x $ob(centerx) wshm offset_y $ob(centery) #wshm safety_pos 1.0 wshm safety_pos 0.5 if {$ob(wrist)} { wshm wrist_diff_damp $ob(wrist_diff_damp) wshm wrist_ps_damp $ob(wrist_ps_damp) wshm wrist_diff_stiff $ob(wrist_diff_stiff) wshm wrist_ps_stiff $ob(wrist_ps_stiff) } wshm curl $ob(curl) wshm restart_stiff $ob(stiff) wshm restart_damp $ob(damp)
121 wshm restart_Hz $ob(Hz)
wshm restart_go 1
wshm logfnid $ob(logfnid) # wait before trip to edge (ms) # k here means thousands
set ob(kpre_wait) [expr {int($ob(pre_wait) * 1000)}] set ob(kvlim_wait) [expr {int($ob(vlim_wait) * 1000)}] # per slot implies log
if {$ob(logperslot)} {set ob(log) "yes"} }
# initialize the compass directions, happens when game is re/started # needs to be tweaked for random, to redo each circuit.
proc dodirlist {} { global ob
# the center is initally tagged as nextball, # this tag moves when a ball is touched. set ndirs [llength $ob(dirs)]
set nsets [expr {$ob(nslots) / $ndirs}] set nslots2 [expr {$ob(nslots) / 2 - 1}]
set idirlist {}
for {set i 0} {$i < $nsets} {incr i} { lappend idirlist $ob(dirs) }
set idirlist [join $idirlist]
# randomize the list in two halves if {$ob(random)} {
set id1 [lrange $idirlist 0 $nslots2] incr nslots2
set id2 [lrange $idirlist $nslots2 end] set idirlist {}
lappend idirlist [shuffle $id1] [shuffle $id2] set idirlist [join $idirlist]
} set ob(dirlist) {} foreach j $idirlist { lappend ob(dirlist) C $j } } # do once at init.
122 proc onceg1 {} {
global ob mob
# blink rate for target ball set ob(blinkrate) 0.5
# set defaults, many of these will be overridden by the game_inits set ob(cur,x) 0.1 set ob(cur,y) 0.1 set ob(curcan,x) 0.1 set ob(curcan,y) 0.1 # control variables set ob(centerx) 0.0 set ob(centery) -0.72 set ob(damp) 5.0 # set ob(stiff) 200.0 set ob(Hz) 200 set ob(curl) 0.0
# which controller ID to use in movebox # planar set ob(controller) 0 if {$ob(wrist)} { set ob(controller) 7 } set ob(edge) 0 set ob(edgedir) t set ob(slotpairnum) 1 # default game type set ob(gametype) eval
# how close to the centers do balls need to be for a hit? in meters. # the on-screen balls are .025 cm in world space, so this is a bit # less than half-overlapped.
set ob(hitradius) 0.010 set ob(ball_size) .0125 if {$ob(wrist)} {
set ob(ball_size) [expr {$ob(ball_size) / $ob(wrist_scale)}] set ob(hitradius) [expr {$ob(hitradius) / $ob(wrist_scale)}] }
123 set ob(cur_ball_size) [expr {$ob(ball_size) / 2.}]
# is this a "static" game? (user holds handle in center) set ob(static) no
# is this a "static" game? (program holds handle in center) set ob(dynamic) no
# if noballhit is yes, then no events on ball hit set ob(noballhit) no
# do processing for adaptive games set ob(adaptive) no
# hide the score display set ob(hidescore) no
# see note above enter_target_do_adaptive set ob(just_ran_pm_display) no
# game description variables # are we logging?
set ob(log) no
# how many vars to log during each sample # time x y vx vy
# fx fy fz grasp set ob(logvars) 9
# base directory for log files
set ob(logdirbase) /home/ospedale/imt/experiment/ventaglio2/ventaglio_dx # default log function
set ob(logfnid) 0
# shall we log each slot in its own file? set ob(logperslot) no
# are we applying motor forces? set ob(motorforces) no
# how many target balls on the clock edge? # (not the center or the cursor)
set ob(nballs) 8
# how many slot paths before we finish? set ob(nslots) 16
# should the targets be chosen randomly (or sequentially) set ob(random) no
# show cursor?
set ob(showcursor) yes
# distance in meters from center ball to edge ball (center to center) set ob(slotlength) 0.21
# how long the movement should take, in seconds and ticks set ob(slottime) 1.4
# slotticks in samples, fed to movebox
set ob(slotticks) [expr {int($ob(slottime) * $ob(Hz))}] # how long to wait in seconds before "timing out" on a slot # time out sets to white-ball "paused" mode
124 set ob(slottimeout) 0.0
# should we pause when a slot times out? set ob(timeoutpause) no
# for circle eval games set ob(smallercircle) no # for shouler eval games set ob(shoulderarrow) no set ob(shoulderarrowdir) up
# center the arm with motor force, unless we ask not to. set ob(nocenterarm) no
set ob(pre_wait) 0.0 set ob(vlim_wait) 0.0 set ob(dobeep) yes toggle_nbeep
# file names built from current time. current time is taken once # here, so that all files for a run have the same time, which is # convenient for grouping them.
set curtime [clock seconds]
set ob(datestamp) [clock format $curtime -format "%Y%m%d_%a"] set ob(timestamp) [clock format $curtime -format "%H%M%S"] # set ob(dirname) [file join $ob(logdirbase) $ob(patname)
# $ob(gametype) $ob(datestamp) ] # blinking ball colors
set ob(ball,col,1) red set ob(ball,col,2) orange # compass directions
set ob(dirs) {E ENE NE NNE NNW NW WNW W} # this may be overrridden by read_game_init
# set ob(dirnums) {-3 -2 -1 0 1 2 3 4 } con questa istruzione si parte da sinistra set ob(dirnums) {4 3 2 1 0 -1 -2 -3 }
# which you hit the "Alt-m" key, you get a menu with various info # may be useful in a clinical setting, or not.
# off by default. toggles on and off. set m [menu_init .menu]
# build the menu.
# the menu building procs are: # menu_t label text
# menu_cb checkbox # menu_v variable entry
125 # menu_b button
menu_t $m hits Hits menu_t $m hittime Time menu_t $m avgtime Avg Time menu_t $m blank2 "" ""
# menu_cb $m random Random set mob(random) no
menu_t $m blank3 "" ""
menu_v $m increment Increment # menu_v $m nballs Spots menu_v $m thresh Threshold menu_t $m blank4 "" ""
menu_b $m restart "New Game (n)" restartg1 menu_t $m menu "Hide Menu (Alt-m)" "" menu_b $m quit "Quit (q)" {ventaglio_exit} # display scale
# 2000.0 means 1 meter on the table == 2000 pixels on the screen. # .14m == 280 pixels # for planar # set ob(scale) 1800.0 # for wrist, 1800 * 14 / 26 set ob(scale) 1800. if {$ob(wrist)} {
set ob(scale) [expr {1800. * $ob(wrist_scale)}] }
# array size for rolling average set ob(avn) 10
# not blinking yet. set ob(blinking) no
# bind . binds the big window # new window, quit
# bind . <n> restartg1 bind . <q> ventaglio_exit bind . <Escape> ventaglio_exit bind . <b> toggle_nbeep
# toggle start/stop. target blinks when started. # white when stopped.
bind . <space> ventaglio_space
# dump game variables to stdout, for debugging. bind . <o> dump_ob
# bind .menu binds the menu window # bind .menu <n> restartg1
bind .menu <q> ventaglio_exit bind .menu <Escape> ventaglio_exit # bind .menu <space> ventaglio_space
126 # useful for measurement/debugging.
# note that cur,x/y will not be updated when "no_arm" is called. # 1st two are ball cursor in world space,
# 2nd two are X cursor in screen space
# bind . <Motion> [list wm title . "cursor pos $ob(cur,x) $ob(cur,y) $ob(curcan,x) $ob(curcan,y) %x %y"]
# this is useful for debugging measurement and cursor placement start_lkm
start_shm
### read game control file here
### to override defaults, and apply some vars to shared memory read_game_init $ob(gamename)
apply_init_vars #wshm safety_pos 1.0 wshm safety_pos 0.5
set ob(dirname) [file join $ob(logdirbase) $ob(patname) \ $ob(gametype) $ob(datestamp) ]
# settable text variables, these are deprecated. set mob(increment) 1
set mob(random) $ob(random) set mob(nballs) 8
# this is the green/red threshold in ms. 0 is off. set mob(thresh) 0 # counters set mob(hits) 0 set ob(slotnum) 1 set mob(hittime) 0 set mob(avgtime) 0 if {$ob(adaptive)} { init_adap }
# unpause the robot control loop LKM start_loop
after 100 }
# restart the game
# reset vars, calculate some positions proc restartg1 {} {
127 global ob mob
# not first time through. if {[winfo exists .fl]} {
destroy .fl }
# if there are any slots running, stop them stop_movebox 0
set ob(random) $mob(random) set ob(increment) $mob(increment) set mob(hits) 0
set ob(slotnum) 1 set mob(hittime) 0 set mob(avgtime) 0 set ob(t0) [clockms] set ob(avi) 0
set ob(avgtime) 0
for {set i 0} {$i < $ob(avn)} {incr i} { set ob(avg,$i) 0
}
# found empirically on 1024x768 screen under gnome # the size of the window on screen
# wm geom . 1009x738+0+0
# the size of the canvas in the window set ob(can,x) 1000
set ob(can,y) 650
# center of window in pixels
set ob(half,x) [expr {$ob(can,x) / 2.}] set ob(half,y) [expr {$ob(can,y) / 2.}]
# inner radius world space (with the balls centered on it). set ob(irad) $ob(slotlength)
# outer radius (the enclosing circle)
set ob(orad) [expr {16./14. * $ob(slotlength)}] # set up direction list
dodirlist
# .fl - originally the left frame, when the menu was the right frame. # has the status line .fl.status on the bottom,
# and setupg1 sets up the big clock canvas in .fl too. frame .fl
128 label .fl.status -textvariable ob(status) -font default\
-background gray20 -foreground gray50 pack .fl.status -fill x -expand true -side bottom status_mes [imes "Press Space Bar to Start"] set w [setupg1 .fl.c]
# grid $w pack $w
set ob(bigcan) $w # experiment...
if {[winfo exists .disp]} { destroy .disp
}
set ob(score) "$mob(hits)/$ob(nslots)"
label .disp -textvariable ob(score) -font $ob(scorefont) -bg gray25 -fg yellow place .disp -in . -relx 1.0 -rely 0.0 -anchor ne
# puts "window size $ob(can,x) $ob(can,y)"
# do final initialization of game vars gameinit $w
# event loop to handle things that move and blink xyinit $w
}
# status message on the bottom line proc status_mes {mes} {
global ob
set ob(status) $mes }
# do the actual canvas munging proc setupg1 {w} {
global ob
# create a canvas, $w this will be ob(bigcan)
set w [canvas $w -width $ob(can,x) -height $ob(can,y) -bg gray25] $w config -highlightthickness 0
# the window outside the canvas . configure -background gray25
# the edge highlight of the canvas should be 0 width
# this is important for the math that converts robot x/y to screen x/y $w configure -highlightthickness 0
# make the center be 0,0 - translate by "scrolling"
129 $ob(half,y)]
# draw big filled circle (questa riga in clock era attiva solo che io ora nn devo disegnare il cerchio
#$w create oval [centxy 0 0 $ob(orad)] -fill gray25 -width 3 -tag bigcircle # draw eight (or whatever) nballs, in a loop.
# nballs should now be fixed to 8. set col1 navyblue
set col2 blue
#set pi [expr {acos(0.) * 2.}] istruzione vecchia
# nel clock il valore di pi corrisponde alla costante pi greco; in questo caso e' un valore # calcolato in maniera empirica per posizionare i target in modo appropriato.
set pi [expr {acos(0.35) * 2.88}]
# if there are 8 slices, each slice is 180/9 degrees. set extdeg [expr {185 /9}]
for {set i 0} {$i < $ob(nballs)} {incr i} { set k [lindex $ob(dirnums) $i]
set ideg [expr {$k * 177 / 8}]
set i2 [expr {($ideg - $extdeg / 2)-280}] # outer circle
eval $w create arc [centxy 0 -0.07 $ob(orad)] -start $i2 -extent $extdeg\ -style pieslice -fill $col1 -width 3 -tag outer
# inner circle
eval $w create arc [centxy 0 -0.07 $ob(irad)] -start $i2 -extent $extdeg\ -style pieslice -fill $col2 -width 3 -tag inner
}
# draw a ball in the center, then balls on edge. # call it C for center.
set ob(ball,C,dir) W
# world position of center of the ball for movebox, x,y set ob(ball,C,center) {0.0 -0.07}
# canvas object id, returned by canvas create
set ob(ball,C,id) [$w create oval [centxy 0.0 -0.07 $ob(ball_size)] \ -fill black -tag [list ball W]]
# ball dir from id, N, NE, etc, and C for center. set ob(ball,$ob(ball,C,id),dir) $ob(ball,C,dir) # draw balls at circumference, all tagged as balls. # starting at top, going clockwise
130 # irad is radius of inner circle
set rad $ob(irad)
for {set i 0} {$i < $ob(nballs)} {incr i} { set k [lindex $ob(dirnums) $i] # use trig to calculate positions
set sx [expr {sin(($k * $pi / 9)-0.2)}] set sy [expr {cos(($k * $pi / 9)-0.2)}] set cx [expr {($rad * $sx)}]
set cy [expr {($rad * $sy)-0.07}] # see comments for center ball above set dir [lindex $ob(dirs) $i]
set ob(ball,$dir,center) [list $cx $cy]
set ob(ball,$dir,id) [$w create oval [centxy $cx $cy $ob(ball_size)] \ -fill black -tag [list ball $dir]]
set ob(ball,$ob(ball,$dir,id),dir) $dir
}
# create cursor off center
# will soon be at actual cursor position after pointer motion
set ob(cursor,id) [$w create oval [centxy .1 .1 $ob(cur_ball_size)] -tag cursor\ -fill yellow]
# to hide mouse pointer:
# $w itemconfig cursor -state hidden # scale the canvas, and flip y
$w scale all 0 0 $ob(scale) -$ob(scale) # return the widget ID of the canvas return $w
}
# show indictator arrows for circle games proc circlearrow {w} {
global ob mob
set smallrad [expr {$ob(orad) / 2.0}] switch -- $ob(circlestart) {
9cw {
set x1 [expr {0.0 - $smallrad}] set y1 0.0
131 set y2 .04
} 9ccw {
set x1 [expr {0.0 - $smallrad}] set y1 0.0
set x2 [expr {0.0 - $smallrad + .01}] set y2 -.04
} 3cw {
set x1 [expr {$smallrad}] set y1 0.0
set x2 [expr {$smallrad - .01}] set y2 -.04
} 3ccw {
set x1 [expr {$smallrad}] set y1 0.0
set x2 [expr {$smallrad - .01}] set y2 .04
}
default return }
$w create line $x1 $y1 $x2 $y2 \
-arrow last -arrowshape {15 20 10} \ -width 10 -fill orange -tag dirarrow $w scale dirarrow 0 0 $ob(scale) -$ob(scale) $w raise cursor
}
# show indicator arrows for shoulder games proc shoulderarrow {w} {
global ob
set smallrad [expr {0 - $ob(orad) / 2.0}] set x1 $smallrad
set y1 0.0 set x2 $smallrad
if {[string equal $ob(shoulderarrowdir) "up"]} { set y2 0.04
} else {
set y2 -0.04 }
$w create line $x1 $y1 $x2 $y2 \
-arrow last -arrowshape {15 20 10} \ -width 10 -fill orange -tag dirarrow
132 $w scale dirarrow 0 0 $ob(scale) -$ob(scale)
$w raise cursor }
# final init before starting game proc gameinit {w} {
global ob mob
if {$ob(smallercircle)} {
set smallrad [expr {$ob(orad) / 2.0}] $w coords bigcircle [centxy 0 0 $smallrad] $w scale bigcircle 0 0 $ob(scale) -$ob(scale) circlearrow $w } if {$ob(shoulderarrow)} { shoulderarrow $w } # blink counter set ob(blinki) 0
# if a blink loop already exists, cancel it. after cancel blinkloop $w
if {!$ob(nocenterarm)} {
# move the arm from its current position to world origin center_arm 0.0 -0.07
}
# if you want to hide the score display if {$ob(hidescore)} { lower .disp } set mob(hits) 0 set ob(slotnum) 1 init_target }
# cancel blink loop and stop blinking # cancel slot timeout too
proc blinkstop {w} { global ob
after cancel blinkloop $w if {!$ob(dynamic)} {
133 # after_info
if {[info exists ob(mb2_after_id)]} { after cancel $ob(mb2_after_id) unset ob(mb2_after_id)
} }
set ob(blinking) no }
# the real action happens in xyloop on ball cursor hits. # blink target ball every half second, or ob(blinkrate) proc blinkloop {w} {
global ob
if {$ob(in_ventaglio_exit)} {return} # blink alternate colors
if {$ob(blinki) % 2} { set color $ob(ball,col,1) } else {
set color $ob(ball,col,2) }
# the current target ball
$w itemconfigure $ob(ball,next,id) -fill $color set blinkrate [expr {int(1000.0 * $ob(blinkrate))}] after $blinkrate blinkloop $w
incr ob(blinki) }
# start the cursor motion loop proc xyinit {w} {
# give the lkm time to unpause, or rshm will return 0's after 100 xyloop $w
}
# the cursor motion loop, runs 20x/sec proc xyloop {w} {
global ob mob
if {$ob(in_ventaglio_exit)} {return} # 20x / sec
after 50 xyloop $w # get world space coords,
# for planar, meters, for wrist, radians set x [getptr x]
134 if {$ob(wrist)} {
foreach {x y} [wrist_ptr_scale $x $y] break }
set ob(cur,x) $x set ob(cur,y) $y
# move the yellow cursor ball, scale, and flip its y $w coords cursor [centxy $x $y $ob(cur_ball_size)] set ob(curcan,x) [expr {$x * $ob(scale) + $ob(half,x)}] set ob(curcan,y) [expr {-$y * $ob(scale) + $ob(half,y)}] $w scale cursor 0 0 $ob(scale) [expr {-$ob(scale)}]
# status_mes "x $x y $y cx $ob(curcan,x) y $ob(curcan,y)" # no check for ball hit if target not blinking.
# or static or no edge balls. if {!$ob(blinking)} { return } if {$ob(static)} { return } if {$ob(nballs) == 0} { return } if {$ob(noballhit)} { return }
# if adaptive, check the velocity magnitude.
# if the patient's hand has moved enough, start the slot now. if {$ob(adaptive)} {
# if there is a movebox event pending... if {[info exists ob(mb2_after_id)]} {
if {$ob(slottime) <= 0.0} { set slot_time 1.0 } # .20 controls the sensitivity of the velocity limit. # lower is more sensitive
set ob(vellim) [expr {.20 * 1.875 * $ob(slotlength) / $ob(slottime)}] set ob(velmag) [rshm velmag]
set ob(adap_patient_moved) "no" if {$ob(velmag) > $ob(vellim)} {
set ob(adap_patient_moved) "yes" # the patient moved the handle ctadd initiate
# puts "velmag $ob(velmag) > vellim $ob(vellim) cancel and start movebox" # execute the command immediately.
# mb_command will cancel the after.
set mb_command [lindex [after info $ob(mb2_after_id)] 0] nbeep 1
eval $mb_command }
} }
# did the cursor enter the next target ball? # see if the cursor is close enough to ball,next set curx $x
set cury $y
set dir $ob(ball,next,dir)
135 set nexty [lindex $ob(ball,$dir,center) 1]
set dist [edist $curx $cury $nextx $nexty] # did the cursor hit the target ball?
if {$dist < $ob(hitradius)} {
# puts "$curx $cury $nextx $nexty edist: $dist" enter_target_ball hit
if {$ob(blinking)} {
set ballid $ob(ball,$dir,id)
leave_target_ball $ballid $ob(bigcan) hit }
}
wm title . "$ob(gamename) $ob(patname) Average Time: $mob(avgtime) Hits: $mob(hits)" }
# this handles the menu clock and the red/green center of the clock # only called when you hit a target ball.
proc bumpclock {} { global ob mob # rolling average set avi $ob(avi) set t1 [clockms]
set dt [expr {$t1 - $ob(t0)}]
set ob(avgtime) [expr {$ob(avgtime) + $dt - $ob(avg,$avi)}] # change blue/red/green if we want.
if {$mob(thresh) > 0 && $ob(avgtime) > [expr {$mob(thresh) * 10}]} { set acol firebrick
} else {
#set acol darkgreen set acol blue
}
set mob(avgtime) [format %.3f [expr {($ob(avgtime)/($ob(avn)*1000.0))}]] $ob(bigcan) itemconfigure inner -fill $acol
set ob(avg,$avi) $dt
set ob(avi) [expr {($avi + 1) % $ob(avn)}] set mob(hittime) [expr {$dt/1000.0}] set ob(t0) $t1
}
# some simple games have no edge balls