It looks like you're new here. If you want to get involved, click one of these buttons!
I thought I'd post some sample code that didn't make it into 1.3 due to time restrictions. This example shows you how to use the triangulate function to draw arbitrary polygons as well as methods for editing them using touches. The example also shows how physics interact with these polygons as well.
-- distPointToLineSeg(): shortest distance of a point to a line segment.
function distPointToLineSeg(p , s1, s2)
local v = s2 - s1
local w = p - s1
c1 = w:dot(v)
if c1 <= 0 then
return p:dist(s1)
end
c2 = v:dot(v)
if c2 <= c1 then
return p:dist(s2)
end
b = c1 / c2;
pb = s1 + b * v;
return p:dist(pb)
end
--===================================================================
-- Use this function to perform your initial setup
function setup()
debugDraw = PhysicsDebugDraw()
print("Hello Polygon!")
print("1. Tap in clockwise order to create a polygon.")
print("2. Drag existing points to move them.")
print("3. Drag on lines to add new points.")
-- the mesh to draw the polygon with
polyMesh = mesh()
-- the current set of vertices for the polygon
verts = {}
-- the polygon fill color
col = color(255, 188, 0, 255)
index = -1
touchID = -1
-- rigid body for the polygon
polyBody = nil
timer = 0
end
-- This function gets called once every frame
function draw()
timer = timer + DeltaTime
-- create a circle every 2 seconds
if timer > 2 then
local body = physics.body(CIRCLE, 25)
body.restitution = 0.5
body.x = WIDTH/2
body.y = HEIGHT
debugDraw:addBody(body)
timer = 0
end
-- This sets the background color to black
background(0, 0, 0)
-- draw physics objects
debugDraw:draw()
-- draw the polygon interia
fill(col)
polyMesh:draw()
pushStyle()
lineCapMode(PROJECT)
fill(255, 255, 255, 255)
-- draw the polygon outline
local pv = verts[1]
for k,v in ipairs(verts) do
noStroke()
ellipse(v.x, v.y, 10, 10)
stroke(col)
strokeWidth(5)
line(pv.x, pv.y, v.x, v.y)
pv = v
end
if pv then
line(pv.x, pv.y, verts[1].x, verts[1].y)
end
popStyle()
end
function touched(touch)
local tv = vec2(touch.x, touch.y)
if touch.state == BEGAN and index == -1 then
-- find the closest vertex within 50 px of thr touch
touchID = touch.id
local minDist = math.huge
for k,v in ipairs(verts) do
local dist = v:dist(tv)
if dist < minDist and dist < 50 then
minDist = dist
index = k
end
end
-- if no point is found near the touch, insert a new one
if index == -1 then
index = #verts
if index == 0 then
index = index + 1
end
-- if touch is within 50px to a line, insert point on line
if #verts > 2 then
local minDist = math.huge
local pv = verts[index]
for k,v in ipairs(verts) do
local dist = distPointToLineSeg(tv, pv, v)
if dist < minDist and dist < 50 then
minDist = dist
index = k
end
pv = v
end
end
table.insert(verts, index, tv)
else
verts[index] = tv
end
elseif touch.state == MOVING and touch.id == touchID then
verts[index] = tv
elseif touch.state == ENDED and touch.id == touchID then
index = -1
end
-- use triangulate to generate triangles from the polygon outline for the mesh
polyMesh.vertices = triangulate(verts)
if polyBody then
polyBody:destroy()
end
if #verts > 2 then
polyBody = physics.body(POLYGON, unpack(verts))
polyBody.type = STATIC
end
end
PhysicsDebugDraw = class()
function PhysicsDebugDraw:init()
self.bodies = {}
self.joints = {}
self.touchMap = {}
self.contacts = {}
end
function PhysicsDebugDraw:addBody(body)
table.insert(self.bodies,body)
end
function PhysicsDebugDraw:addJoint(joint)
table.insert(self.joints,joint)
end
function PhysicsDebugDraw:clear()
-- deactivate all bodies
for i,body in ipairs(self.bodies) do
body:destroy()
end
for i,joint in ipairs(self.joints) do
joint:destroy()
end
self.bodies = {}
self.joints = {}
self.contacts = {}
self.touchMap = {}
end
function PhysicsDebugDraw:draw()
pushStyle()
smooth()
strokeWidth(5)
stroke(128,0,128)
local gain = 2.0
local damp = 0.5
for k,v in pairs(self.touchMap) do
local worldAnchor = v.body:getWorldPoint(v.anchor)
local touchPoint = v.tp
local diff = touchPoint - worldAnchor
local vel = v.body:getLinearVelocityFromWorldPoint(worldAnchor)
v.body:applyForce( (1/1) * diff * gain - vel * damp, worldAnchor)
line(touchPoint.x, touchPoint.y, worldAnchor.x, worldAnchor.y)
end
stroke(0,255,0,255)
strokeWidth(5)
for k,joint in pairs(self.joints) do
local a = joint.anchorA
local b = joint.anchorB
line(a.x,a.y,b.x,b.y)
end
stroke(255,255,255,255)
noFill()
for i,body in ipairs(self.bodies) do
pushMatrix()
translate(body.x, body.y)
rotate(body.angle)
if body.type == STATIC then
stroke(255,255,255,255)
elseif body.type == DYNAMIC then
stroke(150,255,150,255)
elseif body.type == KINEMATIC then
stroke(150,150,255,255)
end
if body.shapeType == POLYGON then
strokeWidth(5.0)
local points = body.points
for j = 1,#points do
a = points[j]
b = points[(j % #points)+1]
line(a.x, a.y, b.x, b.y)
end
elseif body.shapeType == CHAIN or body.shapeType == EDGE then
strokeWidth(5.0)
local points = body.points
for j = 1,#points-1 do
a = points[j]
b = points[j+1]
line(a.x, a.y, b.x, b.y)
end
elseif body.shapeType == CIRCLE then
strokeWidth(5.0)
line(0,0,body.radius-3,0)
strokeWidth(2.5)
ellipse(0,0,body.radius*2)
end
popMatrix()
end
stroke(255, 0, 0, 255)
fill(255, 0, 0, 255)
for k,v in pairs(self.contacts) do
for m,n in ipairs(v.points) do
ellipse(n.x, n.y, 10, 10)
end
end
popStyle()
end
Comments
That's fun just to play with. It will come in handy as well.
Thanks for that @John! Will be useful in upcoming projects!
I still love this. So, much so it's called 112 in my list of projects.
I'm now using it to freestyle draw vertices. I've found a place to add a clearoutput() and print that allow the vertices to be cut and pasted out.
This is an awesome old project! I’ve attached a zip that updates the deprecated “unpack” syntax.
Does anybody know how one would set the mesh to have an image in it?
@UberGoober great wee program.
Here a non perfect solution but will hopefully push you in the right direction for mapping an image
Thanks @West, though of course it’s @John ’s not mine. Did I seem like I was claiming it was mine?
@West I can’t make hide nor hair of the code you added. Can you explain it a little?
@UberGoober I can try, but I don't fully understand meshes/triangulation which I suspect is leading to the discrepancies in the images. Anyway, here is my train of thought:
In setup
This assigns the image to be used as the texture.
verts - this is the array of the polygon vertices as input by the user
The triangulate turns the user inputted vertices into an array of mesh vertices. In a mesh the polygon is represented by a series of triangles. For example a rectangle would be represented by two triangles, and expressed by the 6 vertices - 3 for the first triangle and three for the second (even though some of the vertices are shared, I think they need to be explicitly represented.
To overlay a texture, each vertex in the mesh (polyMesh.vertices) needs to reference a point/position in the texture image. This goes into polyMesh.texcoords as a list of equivalent texture coordinates. These coordinates need to run between 0 and 1 for both left to right and bottom to top of the image.
This bit of code is finding the bounding box of the user inputted polygon
then (hopefully) scaling each point to this bounding box (for example if the leftmost user inputted point was at x=100 and the right most point was at 300, then a point at 200 would be in the middle and would have a texture coordinate of x=0.5)
So the array t, should contain a scaled version of the user inputted polygon, between 0 and 1.
Finally, we need to have the array of the mesh texcoords, rather than the user inputted texCoords so we need to triangulate the texcoords too (like we did with the vertices at the start)
I am assuming that the triangulate function will work in the same way on both the vertices and the texcoords - but this may be where it is falling down.
Maybe a better way would be to map the polyMesh.vertices (after initial triangulation) to the texture rather than fitting the user inputted vertices (verts) to the texture then triangulating.
@West… I think I get it but…
So it’s like this: first, we need to know the bounding box of the entire polygon mesh, and the position of each mesh vertex in relation to that bounding box.
Then, we need to know what part of the image is being used, in other words which four points on the image should correspond to each corner of the bounding box of the polygon.
Finally, once we have the bounding box of the polygon mapped to a box somewhere on the image (or maybe the whole image), we can get the texCoords of each vertex, by translating the (relative) vertex coordinates into the (absolute) coordinates in the bounding box of the image.
…is that right in theory?
@UberGoober Yes I think so. The issue is you are mapping a rectangle (the texture) onto a polygon, but didn’t specify how this was to be mapped.
The bounding box approach is to allow a cookie cutter type approach - you are effectively stretching the polygon in the x and y direction to the size of the texture image until one of a polygon vertex touches the each edge of the texture then chop out the shape of polygon from the texture.
An alternative would be to map each point perimeter of the polygon to a set of equally spaced points around the perimeter of the mesh - then the texture would be squashed and distorted on to the shape - I think this might be a bit harder to implement though
@UberGoober @West Here’s a simple example. Drag your finger around the screen to enclose an area. When you lift your finger, what you enclosed will be cut out of the background image.
@dave1707 I am not sure but I think your example may not be quite applicable to the problem, because you’re using a mesh shape as a mask for drawing a sprite, instead of applying an image to a mesh itself as a texture.
It’s a really clever solution to the visual needs of this demo, neatly avoiding all that triangulation jazz.
But I’m not clear if it helps with of drawing an image inside a 2D physics body—for example, if you used this code on a 2D square that was bouncing off a floor, the sprite wouldn’t stay aligned with the cube’s position and rotation, would it?
@UberGoober So you’re creating a mesh of different shapes that you want an image applied to. I’ll have to look thru what else I have and see if I have something for that would work.
Moved code that was here to another discussion.