# Create Physics Polygons

When working with the physics engine in Codea, one thing that I found lacking was the ability to create shapes other then simple circles and rectangles, so I have been working on a class/function to create other types of shapes with given parameters.

Currently the function is capable of creating simple equilateral, equiangular polygons as well as circles and rectangles, and I am working on creating non equilateral triangles as well in addition to shapes like rhombi and trapezoids.

The current format of the function is below –

– Creates Polygon/Ellipse (s = sides, d = {dimensions}

PhysicsShapes:createShape(x,y,s,d)

if #diagonal == 1 then d = {diagonal length}
if #diagonal == 2 then d = {width, height}

Screenshots of the function in action can be seen at the link below.
http://www.flickr.com/photos/99433588@N04/?saved=1

Have you looked at physics.body(POLYGON,vec2…) . This allows you to create just about any shape you want. I’ll admit it takes work figuring out the vec2 points, but it’s doable.

Basically what I was doing is putting together a function that uses a circle with a radius equal to half the specified diagonal and then rotating points around the origin of the circle to form the vector points of the shape vertices, then I create the body as with the

local body = physics.body(POLYGON,unpack(self.points))

I have been having a little difficulty figuring out how I would declare the arguments in the initial function however for creating non standard shapes, because right now the process is segmented.

First calculates turn angle of points
Calculates x points and dumps to table self.xp
Calculates y points and dumps to table self.yp

Performs transformation of points if sides == 4 (to form rectangle)

Orders self.xp,self.yp into vec2(self.xp[i],self.yp[i]) and dumps into self.points

Performs rotation to get standard orientation of rectangles / odd sided polygons by comparing self.points to vec2(0,radius) and updates self.points

Creates polygon with given points or if circle creates circle with specified radius.

local bodyConstruct = function ()

``````    if sides == 0 or sides == 1 then

return CIRCLE, r end

if sides >= 3 then

return POLYGON, unpack(self.points) end

end
``````

local body = physics.body(bodyConstruct())

I haven’t gotten the triangle transformation or trapazoid / rhombus (slants) to work yet however.

P.S. - I’m having kind of a hard time posting code. Whenever I just try and paste it it comes back without proper formatting unless I space out every line … Is there a more efficient way to do this?

You wouldn’t set the physics body as a local variable, it won’t be useable in other functions and I’m pretty sure it doesn’t take long for it to be put in to the garbage collector

Sounds good. I didn’t fully understand what was happening from your 1st post.

I was using the Test Class structure from the physics simulation of Codea to set up all of the different screens and creating the shapes in the TestX:setup function. All of the physics shapes created by the function were retained by the simulation until the test is changed, simulation reset, or cleared by the debugDraw:clear. I haven’t figured out how to actually delete the shapes either however, and because I created walls all around the screen they just tend to stay there as long as there is room on the screen.

@Luatee - Is there a more efficient way to call the shape creation function then setting it to a local variable?

This is my testing environment for the shapes creation:

These are the parameters set in the Main class

``````tests = {Test1(),Test2(),Test3()}

defaultGravity = physics.gravity()
parameter.integer("TestNumber", 1, #tests)
parameter.text("Function","Create Shape")
parameter.integer("Sides", 0,15,4)
parameter.color("StrokeColor", color(184, 180, 223, 255))
parameter.integer("Width",30,200,50)
parameter.integer("Height",30,200,50)
parameter.number("Density",0.5,10,1 )
parameter.number("AngularDamping",0,10,0)
parameter.number("LinearDamping",0,10,0 )
parameter.number("ForceGravity",0,10,0.5)
``````

The shapes that are created by touch are defined in the Test#:touched(touch) function

function Test3:touched(touch)

``````   local dynamic = math.random (20,30)

if touch.state == BEGAN then

local Contents = debugDraw:returnContents (touch.x,touch.y)

local sides = Sides
local size = {Width,Height}

local physicsShape = PhysicsShapes:createShape(touch.x,touch.y,Sides,size)
if SmartPolygon == true then PhysicsShapes:createSmartShape (Sides,Size,physicsShape) end
physicsShape.gravityScale = ForceGravity
physicsShape.density = Density
physicsShape.stroke = {StrokeColor}
physicsShape.name = Shape
physicsShape.linearDamping = LinearDamping
physicsShape.angularDamping = AngularDamping
end
``````

end

This is an example of what I would put in the Test#:setup function

– pendulum is an example of a circle

local pendulum = PhysicsShapes:createShape(WIDTH/2, HEIGHT/2 - 135, 0,{120})

``````pendulum.stroke = {self.defaultStroke}
PhysicsShapes:createSmartShape (0,120,pendulum)
pendulum.gravityscale = 2.0
pendulum.friction = 1.0
pendulum.density = 5.0
``````

– pendulumArm is an example of a Rectangle
local pendulumArm = PhysicsShapes:createShape(WIDTH/2, HEIGHT/2 - 75, 4,{25,150})

``````pendulumArm.stroke = {self.defaultStroke}
``````

– pentagonS is an example of a Prntagon
local pentagonS = PhysicsShapes:createShape(WIDTH/2, HEIGHT/2, 5, {50}

@Beckett2000 I wrote this awhile ago. I don’t know if you could use any of this or if this even applies.

``````
function setup()
sides=3
end

function draw()
background(40, 40, 50)
fill(255)
stroke(255)
strokeWidth(3)
for z=2,#tab do
line(a1.x+tab[z].x,a1.y+tab[z].y,a1.x+tab[z-1].x,a1.y+tab[z-1].y)
end
line(a1.x+tab[#tab].x,a1.y+tab[#tab].y,a1.x+tab.x,a1.y+tab.y)
text(sides,a1.x,a1.y)
if a1.y<-100 then
a1:destroy()
sides = sides + 1
end
end

function create(sides,r)
tab={}
for a=1,360,360/sides do
table.insert(tab,vec2(x,y))
end
a1=physics.body(POLYGON,unpack(tab))
a1.x=300
a1.y=600
a1.gravityScale=.2
end

``````

This is the function that I am currently using for shape creation. It is a little bit redundant however and could use optimization.

@dave1707 Thanks, that is helpful. I have only been working with Codea/Lua for a couple weeks, and that seems like a much more efficient way to calculate all the points. I went about it by breaking it into parts, but it does take up a lot of space.

Do you have any suggestions for how I could handle creating other types of parallelograms or trapezoid?

``````

PhysicsShapes = class()

function PhysicsShapes:init()

self.rotateXY = {}
self.xp = {}
self.yp = {}
self.points = {}

end

-- Create Physics Ellipse or Polygon
-- s = sides d = {dimensions}

function PhysicsShapes:createShape(x,y,s,d)

local dimensions = {unpack(d)}

local sides = s
local Turn = math.rad(360/sides)

self.dimentional = dimensions

-- Calculate Point Turn Angles --
local Angle = Turn
local angleadd = 0
self.rotateXY = {}
for i = 1, sides do
local Rotate = Angle + angleadd
table.insert(self.rotateXY,Rotate)
local deltaAngle = Angle + angleadd
Angle = deltaAngle
end

-- Verify Length of Table --
local checkLength = function (t,l)
local table = {unpack(t)}
local countLength = #table
local Length = l
local verify = countLength == Length
if not verify  == true then do
return verify end

elseif verify == true then do
return verify end
end
end

-- Check Arguments to Dimensions {} --
local tester1 = checkLength(dimensions,1)
if tester1 then
self.dimentional = dimensions
print("1 Argument To Dimensions")
print("Specified Diagonal", (self.dimentional))
end

local tester2 = checkLength(dimensions,2)
if tester2 then
self.dimentional = math.sqrt((dimensions^2)+(dimensions^2))
print("2 Arguments To Dimensions")
if sides == 4 then
print("Width",(dimensions))
print("Height",(dimensions))
end
print("Derived Diagonal", (self.dimentional))
end

local r = 0.5 * self.dimentional
local shapeWidth = dimensions
local shapeHeight = dimensions
local posX = x
local posY = y

self.xp = {}
self.yp = {}
self.points = {}

-- Create Iteration of Table Values --
local iterateTable = function (table)
local i = 0
local n = #table
return function ()
i = i + 1
if i <= n then return table[i] end
end
end

-- Calculate X Coordinates --
local RotateX = iterateTable(self.rotateXY)
while true do
local rotateAngleX = RotateX()
if rotateAngleX == nil then break end

for i = 1, 1 do
local xv = r * math.cos(rotateAngleX)
table.insert(self.xp,xv)
end
end

-- Calculate Y Coordinates --
local RotateY = iterateTable(self.rotateXY)
while true do
local rotateAngleY = RotateY()
if rotateAngleY == nil then break end

for i = 1, 1 do
local yv = r * math.sin(rotateAngleY)
table.insert(self.yp,yv)
end
end

-- Transform 3 Sided Polygon (incomplete) --
local triangleS = sides == 3

if triangleS then

local tri1 = vec2(self.xp,self.yp)
local tri2 = vec2(self.xp,self.yp)
local tri3 = vec2(self.xp,self.yp)

local dist12 = tri1:dist(tri2)
local dist23 = tri2:dist(tri3)
local dist31 = tri3:dist(tri1)

local testPoints = {}
for i = 1, sides do
table.insert(testPoints,vec2(self.xp[i],self.yp[i]))
i = i + 1 end

--[[
for i = 1, sides do
print (testPoints[i])
i = i + 1
]]--
end

-- Transform 4 Sided Polygon (Create Rectangle) --
local rectangleS = sides == 4

if rectangleS then

local point4 = vec2(self.xp,self.yp)
local point3 = vec2(self.xp,self.yp)
local point2 = vec2(self.xp,self.yp)
local point1 = vec2(self.xp,self.yp)

local distAdj = point3:dist(point2)

if tester2 and rectangleS then
local porportionXY = dimensions <= dimensions

self.rectDistribute = 0

-- Dimension Transformation Distributor --
if dimensions < dimensions then
print("Transforming Height")
self.rectDistribute = ((((dimensions - distAdj)^2)/2)^0.5)/2
print("Height Distributor", self.rectDistribute)
elseif dimensions == dimensions then
print("No Transformation Value")
elseif dimensions > dimensions then
print("Transforming Width")
self.rectDistribute = (((((math.abs(dimensions - distAdj))^2)/2)^0.5)/2)*-1
print("Width Distributor", self.rectDistribute)
end

local distributeT = self.rectDistribute

-- Transform XP
self.xp = self.xp + distributeT
self.xp = self.xp - distributeT
self.xp = self.xp - distributeT
self.xp = self.xp + distributeT

-- Transform YP
self.yp = self.yp + distributeT
self.yp = self.yp - distributeT
self.yp = self.yp - distributeT
self.yp = self.yp + distributeT
end
end

local polyS = sides % 2 ~= 0
local rectS = sides == 4

-- Order point lists into Coordinate Pairs --
local Lines = table.getn(self.xp)
for i = 1, Lines do
local xVec = self.xp[i]
local yVec = self.yp[i]
i = i + 1

local vectorPoints = vec2(xVec,yVec)
table.insert(self.points, vectorPoints)
end

-- Rotate Odd Sided Polygon to standard orientation --
if polyS then

local r = 0.5 * self.dimentional
local pointV = vec2(0,r)
local rotationV = self.points:angleBetween(pointV)
print("Polygon Initial Rotation",math.deg(rotationV),"Degrees")

local polyPoints = #self.points
for i = 1, polyPoints do
self.points[i] = self.points[i]:rotate(rotationV)
i = i + 1
end
end

-- Rotates Rectangles to standard orientation --
if rectS then

local rectPoints = #self.points
for i = 1, rectPoints do
i = i + 1
end
end

-- Determines Body Shape Type --
local bodyConstruct = function ()
if sides == 0 or sides == 1 then
return CIRCLE, r end
if sides >= 3 then
return POLYGON, unpack(self.points) end
end

-- Renders Shape with Given Parameters --
local shape = physics.body(bodyConstruct())
shape.x = posX
shape.y = posY
shape.sleepingAllowed = true
shape.restitution = 0.4
print("Creation Successful")
return shape

end

``````

@Becket2000 If you don’t mind random odd shapes, try this. I added math.random to the code.

``````
function setup()
sides=3
end

function draw()
background(40, 40, 50)
fill(255)
stroke(255)
strokeWidth(3)
for z=2,#tab do
line(a1.x+tab[z].x,a1.y+tab[z].y,a1.x+tab[z-1].x,a1.y+tab[z-1].y)
end
line(a1.x+tab[#tab].x,a1.y+tab[#tab].y,a1.x+tab.x,a1.y+tab.y)
text(sides,a1.x,a1.y)
if a1.y<-100 then
a1:destroy()
sides = sides + 1
end
end

function create(sides,r)
tab={}
for a=1,360,360/sides do
x=x+math.random(-50,50)
y=y+math.random(-50,50)
table.insert(tab,vec2(x,y))
end
a1=physics.body(POLYGON,unpack(tab))
a1.x=300
a1.y=600
a1.gravityScale=.2
end

``````

@Beckett2000 I had to look up the definition of trapezoid and parallelogram. Here’s some code that creates random trapezoids and parallelograms. Tap the screen for a different object.

``````
displayMode(FULLSCREEN)

function setup()
create()
end

function draw()
background(40, 40, 50)
fill(255)
text("tap screen for next object",350,700)
text("trapezoid",100,500)
text("parallelogram",100,200)
stroke(255)
strokeWidth(3)

-- draw trapezoid
for z=2,#ttab do
line(ttab[z].x,ttab[z].y,ttab[z-1].x,ttab[z-1].y)
end
line(ttab[#ttab].x,ttab[#ttab].y,ttab.x,ttab.y)

-- draw parallelogram
for z=2,#ptab do
line(ptab[z].x,ptab[z].y,ptab[z-1].x,ptab[z-1].y)
end
line(ptab[#ptab].x,ptab[#ptab].y,ptab.x,ptab.y)
end

function create()
ttab={}    -- trapezoid table
ptab={}    -- parallelogram table

a1=math.random(150,300)
a2=math.random(30,100)
x1=math.random(0,200)
x2=math.random(0,200)
y1=math.random(50,200)

-- trapezoid
table.insert(ttab,vec2(200+x1+a1,400+y1))
table.insert(ttab,vec2(200+x1,400+y1))
table.insert(ttab,vec2(200+x2,400))
table.insert(ttab,vec2(200+x2+a2,400))

-- parallelogram
table.insert(ptab,vec2(200+x1+a1,100+y1))
table.insert(ptab,vec2(200+x1,100+y1))
table.insert(ptab,vec2(200+x2,100))
table.insert(ptab,vec2(200+x2+a1,100))
end

function touched(t)
if t.state==BEGAN then
create()
end
end

``````

@dave1707 - Thank you, that is exactly what I was looking for. I am trying to create a function that can be called on demand to create shapes as various bodies to act in a simulation.

The PhysicsObject class draws all of the shapes and keeps track of their locations. It is in a simmilar setup as the Debug Draw class from the Physics Lab with regards to storing the shapes in tables, but I’m currently trying to enter the shapes created as keyed values so they can be more easily found and removed.

I used part of the create function you posted above to show the structure I am trying to achieve.

``````

function create(x,y,sides,d)

local r = 0.5 * d

-- Calculate Vec2 Points of Polygon --
local points = {}
for a=1,360,360/sides do
local x = math.sin(math.rad(a))*r
local y = math.cos(math.rad(a))*r
table.insert(points,vec2(x,y))
end

-- If Side Number Is Even Rotate --
if sides % 2 == 0 then
for i = 1, sides do
i = i + 1 end
end

-- Determines Body Shape Type --
local bodyConstruct = function ()
if sides == 0 or sides == 1 then
return CIRCLE, r end
if sides >= 3 then
return POLYGON, unpack(points) end
end

local shape = physics.body(bodyConstruct())
shape.x = x
shape.y = y
shape.gravityScale = .2
shape.sleepingAllowed = false
shape.restitution = 0.4
return shape
end

``````

I still need to add parts to create the rectangles, parallelograms, and trapezoids however and am working on that.

I added the ability to create rectangles, and fixed the previous rotation which didn’t actually set the even sided shapes to the proper rotation.

Previously the shapes seemed to be rotated properly, but bounced slightly to the left when created and hitting the ground, because they were not entirely aligned. I fixed this by orienting the rotation to a point instead of just rotating by a set degree measure of 180/sides.

Next I am going to add trapezoids/parallelograms

``````

-- Create Physics Ellipse or Polygon
-- s = sides d = {dimensions}

function create(x,y,s,d)

-- Checks Aruments to Dimensions --
local radius = function(d)
if #d == 1 then return 0.5 * d end
if #d == 2 then return 0.5 * (math.sqrt((d^2)+(d^2))) end
end

local r = radius(d)
local sides = s

-- Calculate Vec2 Points of Polygon --
local points = {}
for a=1,360,360/sides do
local x = math.sin(math.rad(a))*r
local y = math.cos(math.rad(a))*r
table.insert(points,vec2(x,y))
end

-- If Side Number Is Even Rotate (180/Sides Degrees) --
if sides % 2 == 0 then
local pointA = vec2(0,r):rotate(math.rad(180/sides))
local pointB = points
for i = 1, sides do
points[i] = points[i]:rotate(pointB:angleBetween(pointA)) end
end

-- Creates Non-Equilateral Triangle If Sides == 3 and Width ~= Height --
if sides == 3 and #d == 2 and d ~= d then

points = points + vec2(0,(d - d)/2)
points = points - vec2(0,(d - d)/2)
points = points - vec2(0,(d - d)/2)
end

-- Creates Rectangle If Sides == 4 and Width ~= Height --
if sides == 4 and #d == 2 and d ~= d then

points = points + vec2(0,(d - d)/2)
points = points + vec2(0,(d - d)/2)
points = points - vec2(0,(d - d)/2)
points = points - vec2(0,(d - d)/2)
end

-- Determines Body Shape Type --
local bodyConstruct = function ()
if sides == 0 or sides == 1 then return CIRCLE, r end
if sides >= 3 then return POLYGON, unpack(points) end
end

-- Creates Shape Physics Body --
local shape = physics.body(bodyConstruct())
shape.x = x
shape.y = y
shape.gravityScale = .2
shape.sleepingAllowed = false
shape.restitution = 0.4