```
function setup()
--displayMode(FULLSCREEN_NO_BUTTONS)
-- Make better use of the available screen
supportedOrientations(LANDSCAPE_ANY)
-- Table for our cubes
cubez={}
-- Number of cubes to create
ncubes = 5
-- Marker for which cube was last selected
lastcube = ncubes
-- Create the cubes at random points in a [-2,2] region
for i=1,ncubes do
cubez[i]=Cube(4*vec3(math.random()-.5,
math.random()-.5,
math.random()-.5),
vec3(math.random()+.5,
math.random()+.5,
math.random()+.5)
)
end
-- Make the initial order vaguely sensible
table.sort(cubez,function(a,b) return a.pos.z>b.pos.z end)
-- Allow the user to rotate he viewport
parameter.integer("azimuth",-180,180,0)
parameter.integer("zenith",-90,90,0)
end
function draw()
background(40, 40, 50)
--[[
-- uncomment this section to see a small circle at the touch point
-- useful for checking that what you think is happening actually is
noSmooth()
noStroke()
fill(0, 255, 238, 255)
ellipse(CurrentTouch.x,CurrentTouch.y,15)
--]]
-- Set the projection matrix
perspective(40, WIDTH/HEIGHT)
-- Set the view matrix
camera(0,0,10, 0,0,0, 0,1,0)
-- Apply the user-specified rotations
rotate(zenith,1,0,0)
rotate(azimuth,0,1,0)
-- Draw each shape
for i,c in pairs(cubez) do
c:draw()
end
end
function touched(touch)
if touch.state == BEGAN then
-- New touch, our goal is to see if it touched anything
cube = nil
local j
-- We step through the objects asking each one if it was
-- touched.
-- The starting point of the array is offset so that the
-- object that was just touched is asked last.
-- This means that by tapping an object we move it to the
-- end of the list and so can select objects behind it next
-- time.
for i=1,ncubes do
j = ((i+lastcube-1)%ncubes)+1
if cubez[j]:isTouchedBy(t) then
cube = cubez[j]
lastcube = j
break
end
end
else
-- Old touch, so hand it off to the selected object for
-- processing (assuming one was selected).
if cube then
cube:processTouch(touch)
end
end
end
-- This is a class for defining and handling a cube
Cube = class()
-- For simplicity, all our cubes are the same so we use the same
-- mesh to draw them.
-- The locality of these variables is not significant here, but
-- if this were on another tab they would be hidden from the
-- main code since tabs are "chunks".
local __cube = mesh()
local corners = {}
for l=0,7 do
i,j,k=l%2,math.floor(l/2)%2,math.floor(l/4)%2
table.insert(corners,{vec3(i,j,k),color(255*i,255*j,255*k)})
end
local vertices = {}
local colours = {}
local u
for l=0,2 do
for i=0,1 do
for k=0,1 do
for j=0,2 do
u = (i*2^l + ((j+k)%2)*2^((l+1)%3)
+ (math.floor((j+k)/2)%2)*2^((l+2)%3)) + 1
table.insert(vertices,corners[u][1])
table.insert(colours,corners[u][2])
end
end
end
end
__cube.vertices = vertices
__cube.colors = colours
-- We're done with the temporary variables now
vertices = nil
colours = nil
corners = nil
-- All a cube needs to know at the start is its position and size
-- We actually want to apply the position first, but the user
-- will think of the position as applying after the scale, so
-- we adjust the position accordingly
function Cube:init(v,s)
self.pos = vec3(v.x/s.x,v.y/s.y,v.z/s.z)
self.size = s
end
-- To draw, we move to the position and draw ourselves
function Cube:draw()
pushMatrix()
scale(self.size.x,self.size.y,self.size.z)
translate(self.pos.x,self.pos.y,self.pos.z)
-- We save the mayrix in place at time of draw for checking
-- against touches. This could be optimised as we only need
-- the matrix from the time the screen was touched.
self.matrix = modelMatrix() * viewMatrix() * projectionMatrix()
__cube:draw()
popMatrix()
end
-- This returns "true" if we claim the touch
function Cube:isTouchedBy(t)
-- Store the matrix in effect at the start of the touch
self.smatrix = self.matrix
-- Compute the vector along the ray defined by the touch
local n = screennormal(t,self.matrix)
local plane
-- The next segments of code ask if the touch fell on one of the
-- faces of the cube. We use the normal vector to determine
-- which faces are towards the viewer. Then for each face that
-- is towards the viewer, we test if the touch point was on that
-- face.
if n.z > 0 then
plane = {vec3(0,0,1),vec3(1,0,0),vec3(0,1,0)}
else
plane = {vec3(0,0,0),vec3(1,0,0),vec3(0,1,0)}
end
if self:touchFace(plane,t) then
return true
end
if n.y > 0 then
plane = {vec3(0,1,0),vec3(1,0,0),vec3(0,0,1)}
else
plane = {vec3(0,0,0),vec3(1,0,0),vec3(0,0,1)}
end
if self:touchFace(plane,t) then
return true
end
if n.x > 0 then
plane = {vec3(1,0,0),vec3(0,1,0),vec3(0,0,1)}
else
plane = {vec3(0,0,0),vec3(0,1,0),vec3(0,0,1)}
end
if self:touchFace(plane,t) then
return true
end
return false
end
-- This tests if the touch point is on a particular face.
-- A face defines a plane in space and generically the touch line
-- will intersect that plane once. We compute that point and
-- test if it is on the corresponding face.
-- If so, we save the plane as that will be our plane of movement
-- while this touch is active.
-- As the position and size are encoded in the matrix, when we test
-- coordinates we just need to test against the original cube where
-- the faces are [0,1]x[0,1]
function Cube:touchFace(plane,t)
local tc = screentoplane(t,
plane[1],
plane[2],
plane[3],
self.matrix)
if tc:dot(plane[2]) > 0 and tc:dot(plane[2]) < 1 and
tc:dot(plane[3]) > 0 and tc:dot(plane[3]) < 1 then
self.plane = plane
self.starttouch = tc - self.pos
return true
end
return false
end
-- This computes our displacement relative to the initial touch
-- and sets our position accordingly.
function Cube:processTouch(t)
local tc = screentoplane(t,
self.plane[1],
self.plane[2],
self.plane[3],
self.smatrix)
self.pos = tc - self.starttouch
end
-- These are all auxiliary macros, some predate matrix and
-- vector types in Codea so could now be optimised
-- Apply a 4-matrix to a 4-vector
function applymatrix4(v,m)
local u = {}
u[1] = m[1]*v[1] + m[5]*v[2] + m[09]*v[3] + m[13]*v[4]
u[2] = m[2]*v[1] + m[6]*v[2] + m[10]*v[3] + m[14]*v[4]
u[3] = m[3]*v[1] + m[7]*v[2] + m[11]*v[3] + m[15]*v[4]
u[4] = m[4]*v[1] + m[8]*v[2] + m[12]*v[3] + m[16]*v[4]
return u
end
-- Apply a 3-matrix to a 3-vector
function applymatrix3(v,m)
local u = {}
u[1] = m[1]*v[1] + m[4]*v[2] + m[7]*v[3]
u[2] = m[2]*v[1] + m[5]*v[2] + m[8]*v[3]
u[3] = m[3]*v[1] + m[6]*v[2] + m[9]*v[3]
return u
end
-- Compute the cofactor matrix of a 3x3 matrix
function cofactor3(m)
local rm = {}
local sgn,l
local fm = {}
for k=1,9 do
fm = {}
l = math.floor((k-1)/3) + 1 + 3*((k-1)%3)
sgn = (-1)^(math.floor((k-1)/3))*(-1)^((k-1)%3)
for j=1,9 do
if j%3 ~= k%3
and math.floor((j-1)/3) ~= math.floor((k-1)/3)
then
table.insert(fm,m[j])
end
end
rm[l] = sgn*Det2(fm)
end
return rm
end
-- Determinant of a 2x2 matrix (needed for the cofactor of a 3x3)
function Det2(t)
return t[1]*t[4] - t[2]*t[3]
end
-- Given a plane in space, this computes the transformation
-- matrix from that plane to the screen
function __planetoscreen(o,u,v,A)
A = A or modelMatrix() * viewMatrix() * projectionMatrix()
o = o or vec3(0,0,0)
u = u or vec3(1,0,0)
v = v or vec3(0,1,0)
-- promote to 4-vectors
o = vec4(o.x,o.y,o.z,1)
u = vec4(u.x,u.y,u.z,0)
v = vec4(v.x,v.y,v.z,0)
local oA, uA, vA
oA = applymatrix4(o,A)
uA = applymatrix4(u,A)
vA = applymatrix4(v,A)
return { uA[1], uA[2], uA[4],
vA[1], vA[2], vA[4],
oA[1], oA[2], oA[4]}
end
-- Given a plane in space, this computes the transformation
-- matrix from the screen to that plane
function screentoplane(t,o,u,v,A)
A = A or modelMatrix() * viewMatrix() * projectionMatrix()
o = o or vec3(0,0,0)
u = u or vec3(1,0,0)
v = v or vec3(0,1,0)
t = t or CurrentTouch
local m = __planetoscreen(o,u,v,A)
m = cofactor3(m)
local ndc = {}
local a
ndc[1] = (t.x/WIDTH - .5)*2
ndc[2] = (t.y/HEIGHT - .5)*2
ndc[3] = 1
a = applymatrix3(ndc,m)
if (a[3] == 0) then return end
a = vec2(a[1], a[2])/a[3]
return o + a.x*u + a.y*v
end
-- This computes the vector along the "touch ray"
function screennormal(t,A)
A = A or modelMatrix() * viewMatrix() * projectionMatrix()
t = t or CurrentTouch
local u,v,w,x,y
u = vec3(A[1],A[5],A[9])
v = vec3(A[2],A[6],A[10])
w = vec3(A[4],A[8],A[12])
x = (t.x/WIDTH - .5)*2
y = (t.y/HEIGHT - .5)*2
u = u - x*w
v = v - y*w
return u:cross(v)
end
```

Following the various discussions about touching and moving 3D objects, I thought I’d post my code. It’s an extension of the code (of mine) that Ignatz posted in another thread. The extensions are: the code is commented, and the objects are now cuboids, not cubes.

@Andrew_Stacey => That was great ! I understand until screen normal for touch, but after… I’m confused. I have made 2 classes Screen and Mesh, can you help me for the function Mesh:isTouched(touch, offset) please ?

Very nice indeed! I’m certainly going to be looking here for inspiration when I take the dreaded step into is something touched in 3D space or perhaps some extra matrix maths

@Andrew_Stacey, if you can help me please ^^

Latest version includes spheres and flat rectangles to demonstrate different ways of reacting to touch information:

- Cubes always move parallel to one of their faces
- Spheres always move parallel to the screen
- Rectangles move in their plane of definition if they are currently “face on”, but if “side on” move in a plane that includes the vector normal to their plane of definition.

Also now includes an initial scale and initial rotation.

@HyroVitalyProtago take a look at how the Picture object reacts to touches in this code.

Available as:

- AutoGisted version: https://gist.github.com/loopspace/5937499
- Self-installer: https://gist.github.com/loopspace/5937539

(@Briarfox: first attempt at an auto install - hope I got it right!)

mmm… I’ve tried to understand the system for plane (that i need), and to implement the same thing in my project, but that doesn’t work…

This is the project in a one file : https://gist.github.com/HyroVitalyProtago/5939178

As your mesh is not of unit size, you need to check the touch against its actual size: in `Mesh:isTouched`

you should do the following.

```
if tc.x < 0 or tc.x > self.dim.w or tc.y < 0 or tc.y > self.dim.h then
```

Thanks god ^:)^

Great example, thanks.