Any idea to detect bounded and unbounded shapes drawn by user ?

I am trying to make an app which involves user drawing on the canvas.

I want to identify if the user draws a bounded shape ( like, “O”) or an unbounded shape (like, “U”).

This is how the drawing is implemented.

local function myTouchListener( event ) local tempGroup = display.newGroup() if ( event.phase == "began" ) then coorX = event.x coorY = event.y print( "object touched = "..tostring(event.target) ) elseif ( event.phase == "moved" ) then local paint = { 1, 0, 0.5 } s = s+1 --initially -1 strokeCircle[s] = display.newCircle(event.x,event.y,3) --circle shape used as brush stroke strokeCircle[s].fill = paint tempGroup:insert(strokeCircle[s]) --Finding the circumference, aka the length of outline of shape. distanceNew = math.sqrt(math.pow((event.x - coorX),2) + math.pow((event.y - coorY),2) ) print("distanceOld,distanceNew,distance"..distanceOld..","..distanceNew..","..distance) if((distanceOld) \<= (distanceNew)) then distance = distanceNew farthestPtX = event.x farthestPtY = event.y distanceOld = distanceNew end print( "touch location in content coordinates = "..event.x..","..event.y ) elseif ( event.phase == "ended" ) then print("farthestPtX , farthestPtY"..farthestPtX..","..farthestPtY) print("coorX , coorY"..coorX..","..coorY) centerX = (farthestPtX + coorX) /2 centerY = (farthestPtY + coorY) /2 print("Center is "..centerX..","..centerY) --finds the centre point of shape drawn by user. for x = 1,s do for y = 1, s do if ( strokeCircle[y] and hasCollidedCircle( strokeCircle[x],strokeCircle[y]) ) then print("It is a Circle") end end end radius = math.sqrt(math.pow((farthestPtX - coorX)/2,2) + math.pow((farthestPtY - coorY)/2,2) ) local paint = {0,0,1} local centerCircle = display.newCircle(centerX,centerY,5) centerCircle.fill = paint circle\_autoComplete(centerX,centerY,radius) --replaces the outline drawn by user as a circle print( "touch ended on object "..tostring(event.target) ) --Remove the user drawn outline for x =0,table.maxn(strokeCircle ) do display.remove(strokeCircle[x]) end s = -1 end return true --prevents touch propagation to underlying objects end 

My goal is to replace the outline drawn by user with a circle (the circle_autocomplete function) only if the outline is a bounded shape(like O, D).

Right now, the code calls the circle_autocomplete() function even if the user draws an unbounded shape (like I, U, C) etc.

I tried to use a collision function, like if the distance between two circles eg, Distance(strokeCircle[x], strokeCircle[x+1]) < circleSize, then there is an overlap or collision. But this did not work, as if the user draws slowly nearby circles overlap even while drawing. (See image).

How do I detect if the user has drawn a bounded or unbounded shape from their stroke ?

Sound more like you’re looking for intersection testing.

I have a bit of code in the SSK wip section that fixes a bad list of vertices to make a valid shape.  Basically, it detect the first crossing in a list of vertices and removes the rest.  

This may not be exactly what you want, but it can likely be adapted to your needs:

https://github.com/roaminggamer/SSKCorona/blob/master/ssk/wip/intersectFixer.lua

In this function, if you want to check for a crossing change it like this:

intersectFixer.removeCrossing = function( list ) ... -- return flat -- is this return flat, crossedAt -- change to this end

Then, when you call it do this:

local result, crossedAt = intersectFixer.removeCrossing( myListOfPoints ) if( crossedAt \> 0 ) then print("Bounded shape - Intersection occurred") else print("Unbounded shape - No intersection occurred") end

Note: Take out line 20 and you can use the file as a module:

\_G.ssk.intersectFixer = intersectFixer

Thanks for the module roaminggamer ! But, I get this way too common error, "attempt to index global ‘intersectFixer’ (a nil value) " :mellow:   at this first line below. (At line 36 in the first post’s code).

 local result, crossedAt = intersectFixer.removeCrossing( strokeCircle ) if( crossedAt \> 0 ) then print("Bounded shape - Intersection occurred") else print("Unbounded shape - No intersection occurred") end

I added this piece of code in the touch event “ended” phase. So, strokeCircle list should be filled. Any idea why?  :blink:

Show all your code.  Where and how did you require the module.

You need to do this:

  1. Get the code.

  2. Make the code changes I suggested

  3. save it in a file called boblua (or whatever you want)

  4. In the code you’re going to use it in, do this:

    local mybob = require “bob” local flat, crossedAt = mybob.removeCrossing( strokeCircle )

The above assumes stroke circle is a table of x, y pairs. If it is not, you need to convert it to such a list.  Everything you need is in the module.  You simply need to modify it to suit your needs.  This is not a ‘ready made’ solution.  Your usage is not the one I wrote this for.  However, you can use my code to get where you want to go.

require("crossTest") --the module you provided local function init() verticeList = {} strokeCircle = {} v = -1 s = -1 end init() local function myTouchListener( event ) if ( event.phase == "began" ) then coorX = event.x coorY = event.y elseif ( event.phase == "moved" ) then s = s+1 strokeCircle[s] = display.newCircle(event.x,event.y,5) strokeCircle[s]:setFillColor(1,0,0.5) v = v+1 verticeList[v] = {event.x,event.y} elseif ( event.phase == "ended" ) then local result, crossedAt = intersectFixer.removeCrossing( verticeList ) if( crossedAt \> 0 ) then print("Bounded shape - Intersection occurred") else print("Unbounded shape - No intersection occurred") end s = -1 --Reinilitalize to -1 to start counting for next object drawn end return true --prevents touch propagation to underlying objects end local myButton = display.newRect( display.contentCenterX,display.contentCenterY,display.viewableContentWidth, display.viewableContentHeight ) myButton:addEventListener( "touch", myTouchListener ) 

This is what I did. ^

strokeCircle had the entire circle object in the list, my bad.

So, I created another list with just the { {x1,y1}, {x2,y2}…{Xn,Yn} }. But it shows the same error.

Problem #1

Well, I saw one immediate error:

require("crossTest") --the module you provided ... local result, crossedAt = intersectFixer.removeCrossing( verticeList 

You’re not storing the module in a variable so I don’t understand how you expect to access it.

This is better:

local intersectFixer = require("crossTest") --the module you provided ... local result, crossedAt = intersectFixer.removeCrossing( verticeList 

(This is exactly what I explained in my last post.  I think you’re confused because some of Corona’s modules and libraries are global after they are required.  Ex: display.* and others.  Modules in files do not work this way, unless the author explicitly makes them global.)

Problem #2

Regardless, you’re not passing the right kind of data to the function.  Please take a closer look at the module.  That function takes a flat list of x,y pairs:

local myVerticies = { 10, 10, 20, 20 } --\> \< 10, 10 \>, \< 20, 20 \>

You’re doing this:

v = v+1 verticeList[v] = {event.x,event.y}

Eventually you’ll have this:

verticeList = { { 10, 10 }, { 20, 20 }, ... }

This is close, but not right.  What you have is a ‘point list’.

Before you process that list do this:

verticeList = intersectFixer.toFlat( verticeList )

That converts it to a flat list.  ‘removeCrossing()’ only works on a flat list.

You can convert it back later like this:

verticeList = intersectFixer.toPointList( verticeList )

The first problem seems to have been fixed, thank you !
Coming to the second. I passed a flat list like {x1,y1,x2.y2…xn,yn) to the removeCrossing() function, and this errors pops up:

"crossTest.lua:102:attempt to perform arithmetic on local 'y4' (a nil value) stack traceback: crossTest.lua:102: in function 'lines' crossTest.lua:61:in function 'removeCrossing' main.lua:180:in function \<main.lua:75\> ?:in function \<?:221\>

The values seem fine too (See image)

Off the top of my head, it look like you’re passing in a list that has fewer than four <x,y> pairs.  You can’t do that.  Wait to start examining points lists till you have four sets or more.

Sound more like you’re looking for intersection testing.

I have a bit of code in the SSK wip section that fixes a bad list of vertices to make a valid shape.  Basically, it detect the first crossing in a list of vertices and removes the rest.  

This may not be exactly what you want, but it can likely be adapted to your needs:

https://github.com/roaminggamer/SSKCorona/blob/master/ssk/wip/intersectFixer.lua

In this function, if you want to check for a crossing change it like this:

intersectFixer.removeCrossing = function( list ) ... -- return flat -- is this return flat, crossedAt -- change to this end

Then, when you call it do this:

local result, crossedAt = intersectFixer.removeCrossing( myListOfPoints ) if( crossedAt \> 0 ) then print("Bounded shape - Intersection occurred") else print("Unbounded shape - No intersection occurred") end

Note: Take out line 20 and you can use the file as a module:

\_G.ssk.intersectFixer = intersectFixer

Thanks for the module roaminggamer ! But, I get this way too common error, "attempt to index global ‘intersectFixer’ (a nil value) " :mellow:   at this first line below. (At line 36 in the first post’s code).

 local result, crossedAt = intersectFixer.removeCrossing( strokeCircle ) if( crossedAt \> 0 ) then print("Bounded shape - Intersection occurred") else print("Unbounded shape - No intersection occurred") end

I added this piece of code in the touch event “ended” phase. So, strokeCircle list should be filled. Any idea why?  :blink:

Show all your code.  Where and how did you require the module.

You need to do this:

  1. Get the code.

  2. Make the code changes I suggested

  3. save it in a file called boblua (or whatever you want)

  4. In the code you’re going to use it in, do this:

    local mybob = require “bob” local flat, crossedAt = mybob.removeCrossing( strokeCircle )

The above assumes stroke circle is a table of x, y pairs. If it is not, you need to convert it to such a list.  Everything you need is in the module.  You simply need to modify it to suit your needs.  This is not a ‘ready made’ solution.  Your usage is not the one I wrote this for.  However, you can use my code to get where you want to go.

require("crossTest") --the module you provided local function init() verticeList = {} strokeCircle = {} v = -1 s = -1 end init() local function myTouchListener( event ) if ( event.phase == "began" ) then coorX = event.x coorY = event.y elseif ( event.phase == "moved" ) then s = s+1 strokeCircle[s] = display.newCircle(event.x,event.y,5) strokeCircle[s]:setFillColor(1,0,0.5) v = v+1 verticeList[v] = {event.x,event.y} elseif ( event.phase == "ended" ) then local result, crossedAt = intersectFixer.removeCrossing( verticeList ) if( crossedAt \> 0 ) then print("Bounded shape - Intersection occurred") else print("Unbounded shape - No intersection occurred") end s = -1 --Reinilitalize to -1 to start counting for next object drawn end return true --prevents touch propagation to underlying objects end local myButton = display.newRect( display.contentCenterX,display.contentCenterY,display.viewableContentWidth, display.viewableContentHeight ) myButton:addEventListener( "touch", myTouchListener ) 

This is what I did. ^

strokeCircle had the entire circle object in the list, my bad.

So, I created another list with just the { {x1,y1}, {x2,y2}…{Xn,Yn} }. But it shows the same error.

Problem #1

Well, I saw one immediate error:

require("crossTest") --the module you provided ... local result, crossedAt = intersectFixer.removeCrossing( verticeList 

You’re not storing the module in a variable so I don’t understand how you expect to access it.

This is better:

local intersectFixer = require("crossTest") --the module you provided ... local result, crossedAt = intersectFixer.removeCrossing( verticeList 

(This is exactly what I explained in my last post.  I think you’re confused because some of Corona’s modules and libraries are global after they are required.  Ex: display.* and others.  Modules in files do not work this way, unless the author explicitly makes them global.)

Problem #2

Regardless, you’re not passing the right kind of data to the function.  Please take a closer look at the module.  That function takes a flat list of x,y pairs:

local myVerticies = { 10, 10, 20, 20 } --\> \< 10, 10 \>, \< 20, 20 \>

You’re doing this:

v = v+1 verticeList[v] = {event.x,event.y}

Eventually you’ll have this:

verticeList = { { 10, 10 }, { 20, 20 }, ... }

This is close, but not right.  What you have is a ‘point list’.

Before you process that list do this:

verticeList = intersectFixer.toFlat( verticeList )

That converts it to a flat list.  ‘removeCrossing()’ only works on a flat list.

You can convert it back later like this:

verticeList = intersectFixer.toPointList( verticeList )

The first problem seems to have been fixed, thank you !
Coming to the second. I passed a flat list like {x1,y1,x2.y2…xn,yn) to the removeCrossing() function, and this errors pops up:

"crossTest.lua:102:attempt to perform arithmetic on local 'y4' (a nil value) stack traceback: crossTest.lua:102: in function 'lines' crossTest.lua:61:in function 'removeCrossing' main.lua:180:in function \<main.lua:75\> ?:in function \<?:221\>

The values seem fine too (See image)

Off the top of my head, it look like you’re passing in a list that has fewer than four <x,y> pairs.  You can’t do that.  Wait to start examining points lists till you have four sets or more.