Table Tennis
Tutorial Resources | |
---|---|
Data Files | Project: Open “TableTennis.p3prj” in PFC3D [1] |
Introduction
This example simulates a table tennis game.
The basic use of the wall generate
command is shown. The fish callback
command is used so that specific operations are performed when the ball hits the table or the net. The ball trajectory is traced during the game using the ball trace
command.
Numerical Model
The “CreateTable.p3dat” data file creates the table and the rackets. First, the table dimensions are calculated from the domain size.
fish define dim_table
xTable=array.create(1,2)
yTable=array.create(1,2)
xTable(1,1) = -1
xTable(1,2) = 1
Lx = math.abs(xTable(1,1))+math.abs(xTable(1,2))
Ly = Lx*0.6
yTable(1,1) = domain.min.y*0.95
yTable(1,2) = yTable(1,1)+Ly
zTable = (domain.min.z+domain.max.z)*0.5
end
@dim_table
The table surface is then created using the wall generate
command to create a planar polygonal wall (polygon
keyword). The legs of the table are also created by introducing four cylindrical walls with the cylinder
keyword. Finally, the net is created as a new polygonal wall. Walls are grouped and named to facilitate later operations.
; creation of the table
wall generate group 'table' polygon ...
@xTable(1,2) @yTable(1,1) @zTable ...
@xTable(1,2) @yTable(1,2) @zTable ...
@xTable(1,1) @yTable(1,1) @zTable ...
@xTable(1,1) @yTable(1,2) @zTable
; insertion of table legs
wall generate group 'table' cylinder ...
axis 0 0 -1 ...
base @xTable(1,2) @yTable(1,1) @zTable ...
height [Ly*0.5] ...
one-wall ...
radius [Lx*0.02]
; creation of the net
wall generate group 'net' polygon ...
[(xTable(1,1)+xTable(1,2))*0.5] @yTable(1,1) @zTable ...
[(xTable(1,1)+xTable(1,2))*0.5] @yTable(1,2) @zTable ...
[(xTable(1,1)+xTable(1,2))*0.5] @yTable(1,1) [zTable+Lx*0.075] ...
[(xTable(1,1)+xTable(1,2))*0.5] @yTable(1,2) [zTable+Lx*0.075]
Then the rackets are created and placed at the table extremities. Rackets are represented by two disk-shaped walls using the disk
keyword.
;creation of the rackets
wall generate group 'player1' disk ... ;racket Player1
dip 90 ...
dip-direction 90 ...
position @xTable(1,1) [(yTable(1,1)+yTable(1,2))*0.5] [zTable+Ly*0.3] ...
rad [Lx*0.05]
wall generate group 'player2' disk ... ;racket Player2
dip 90 ...
dip-direction 90 ...
position @xTable(1,2) [(yTable(1,1)+yTable(1,2))*0.5] [zTable+Ly*0.3] ...
rad [Lx*0.05]
The last step in preparing the game is the creation of the ball. Its initial velocity is set on a per-component basis. The ball trajectory during the game is traced using the ball trace
command. The ball density and initial velocity are set with the ball attribute
command.
;define the initial velocity of the ball
[XballInitialVelocity = -3.0]
[ZballInitialVelocity = 2.0]
;creation of the ball
ball create id 1 ...
radius [Lx*0.005] ...
position [xTable(1,2)*0.98] ...
[(yTable(1,1)+yTable(1,2))*0.5] ...
[zTable+Ly*0.3]
ball trace id 1
ball attribute density 10 ...
velocity-x @XballInitialVelocity ...
velocity-z @ZballInitialVelocity
The Contact Model Assignment Table (CMAT) must be defined in each PFC model. Each contact in the model domain is assigned a contact constitutive model in this way. The default slot of the CMAT is defined for ball-facet contacts. A linear model with a Coulomb criterion associated with the contacts is chosen.
;linear model for ball-facet contacts
contact cmat default type ball-facet model linear ...
To complete the preparation of the model, some function is needed to control the movement of the rackets and to stop the game in case of a fault. To do this, two FISH functions are created and registered with callback events with the fish callback
command.
fish callback add @checknet event contact_activated
;set fish callback to prepare the racket position
fish callback add @tablecontact event contact_activated
Use the program list cycle-sequence
command to list the cycle points during a PFC cycle and the fish list callback-list
command to list the available callback events. One can insert FISH functions between the preset cycle points during the cycle sequence.
In this example we choose to manage the ball-net and/or ball-table interactions using callback events. Regarding ball-net contacts using the contact_activated event (see “Linear Model Callback Events”), every time a contact is activated, the function checknet checks whether it is a ball-net contact. If so, the Fault! message is sent.
;check for ball-net contacts
fish define checknet(arr)
ct=arr(1)
;in case of ball-facet contacts, contact.end1 refers to the ball,
;contact.end2 refers to the facet
if wall.group(wall.facet.wall(contact.end2(ct)),"Default") == "net"
io.out("Net!!")
ball.vel(bp,1)=0
ball.vel(bp,3)=0
endif
end
Please be aware of the different ways one can use contact_create/delete and/or contact_activated events: a contact may be created whenever the cell extents of two pieces touch or overlap (see the 35.0 - Create/delete Contacts reference page). The contacts are initially inactive upon creation, and contact resolution is required to determine their activity state. Ball-net contacts could be created and remain inactive, and the game must continue in such a case.
Regarding ball-table contacts, every time a contact is deleted, the function tablecontact is called. If the ball is found to have touched the table, the racket of the respective player is moved to a new position that is predicted according to the theory of parabolic projectiles.
;check fi=ot ball-table contacts!
fish define tablecontact(ct)
con = ct(1)
;in case of ball-facet contacts, contact.end1 refers to the ball,
;contact.end2 refers to the facet
if wall.group(wall.facet.wall(contact.end2(con)),"Default") == "table"
moverackets
endif
end
;compute the new position of the racket and move it!
fish define moverackets
xvel = ball.vel.x(bp)
zvel = ball.vel.z(bp)
xpos = ball.pos.x(bp)
zpos = ball.pos.z(bp)
if xvel < 0
if xpos > 0
ball.vel(bp,1)=0
ball.vel(bp,3)=0
io.out("Fault!!")
endif
wp = wall.find(7)
L = xTable(1,1) - xpos
else
if xpos < 0
ball.vel(bp,1)=0
ball.vel(bp,3)=0
io.out("Fault!!")
endif
wp = wall.find(8)
L = xTable(1,2) - xpos
endif
zM = -(zvel/xvel)*L + (-9.81/2)*((L/xvel)^2)
if zM > domain.min.z
if zM < domain.max.z
wall.pos.z(wp) = zM + zpos
endif
endif
end
Everything is ready … have fun!
Discussion
This tutorial presents an example of registering FISH functions with callback events to produce custom behavior (see the fish callback
command). This demonstrates one of the core features of PFC: the ability to model complex phenomena using FISH.
Endnote
[1] | These may be found in PFC3D under the “tutorials/table_tennis” folder in the Examples dialog ( on the menu). If this entry does not appear, please copy the application data to a new directory. (Use the menu commands . See the “Copy Application Data” section for details.) |
Was this helpful? ... | PFC 6.0 © 2019, Itasca | Updated: Nov 19, 2021 |