Detect overlapping circles

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
PuzzledGreatly
Posts: 1303
Joined: 29 Sep 2013, 22:18

Detect overlapping circles

26 Sep 2021, 18:34

I know how to place png images randomly on the screen and I know how to detect if two rectangular objects are overlapping but how to detect whether two circles are overlapping?.My math is miserable. Is there a formula? Can anyone think of an approach for how to detect overlaps for randomly placed circles? Thanks.
User avatar
mikeyww
Posts: 26599
Joined: 09 Sep 2014, 18:38

Re: Detect overlapping circles

26 Sep 2021, 18:44

There is probably a much better method, but you could loop through the combined rectangular area. For each pixel in the rectangle, determine if the pixel's distance to the center of circle 1 is within the circle's radius. Do the same for circle 2. You then know when any pixel was within the radius of both circles. Of course, this exercise would be unnecessary if the rectangles bounding the circles do not overlap.
Last edited by mikeyww on 26 Sep 2021, 18:45, edited 1 time in total.
User avatar
boiler
Posts: 16768
Joined: 21 Dec 2014, 02:44

Re: Detect overlapping circles

26 Sep 2021, 18:45

No looping required. If the distance between their centers is greater than the sum of their two radii, then they do not overlap. Otherwise, they do, unless they are exactly the same then they meet at a single point.
Last edited by boiler on 26 Sep 2021, 18:47, edited 1 time in total.
User avatar
mikeyww
Posts: 26599
Joined: 09 Sep 2014, 18:38

Re: Detect overlapping circles

26 Sep 2021, 18:46

Well, like I said, .... :thumbup: boiler.
User avatar
boiler
Posts: 16768
Joined: 21 Dec 2014, 02:44

Re: Detect overlapping circles

26 Sep 2021, 18:51

Of course, use the Pythagorean Theorem to determine the distance between the centers of the two circles when you know the coordinates of each.
User avatar
Hellbent
Posts: 2102
Joined: 23 Sep 2017, 13:34

Re: Detect overlapping circles

26 Sep 2021, 20:34

Using a vector object.

Code: Select all

				;   x   ,   y
Center1 := New Vector( 10 , 10)
Center2 := New Vector( 500 ,500 )

MsgBox, % "The distance between the center of object 1 to the center of the other object is: " Center1.Dist( Center2 ) " px"
ExitApp
;*************************************************
Class Vector	{
	__New( x := 0 , y := 0 ){
		This.X := x , This.Y := y
	}
	dist( in1 ){
		return Sqrt( ( ( This.X - In1.X ) ** 2 ) + ( ( This.Y - In1.Y ) ** 2 ) )
	}
}
User avatar
PuzzledGreatly
Posts: 1303
Joined: 29 Sep 2013, 22:18

Re: Detect overlapping circles

27 Sep 2021, 00:54

Thanks for all the replies, Sorry, I don't under stand the vector class idea at all. I tried to follow an example from gamedevelopment.blog and came up with this:

Code: Select all

Grow()
{
	static cx, cy, r, n, nw, nx,ny, dist, sidea, sideb
	
	random, r, 1000,4000
	SetTimer, grow, % - r
	
	return
	
	Grow:
	
	random, r, 3,3
	random, n, 1, 10

	grow := r = 3 ? grow : n
	
	Guicontrolget, b%grow%, game:pos
	
	random, r, 110, 150
	nw := floor(b%grow%w * (r/100))
	nx := b%grow%x - ((nw - b%grow%w)/2)
	ny := b%grow%y - ((nw - b%grow%w)/2)
	
	Guicontrol, Game:move, b%grow%, % "x" nx "y" ny  "w" nw "h" nw
	
	nx := nx + (nw/2)
	ny := ny + (nw/2)
	
	collide := ""
	loop 10
	{
		if (A_index = grow)
		continue
		
		Guicontrolget, b%A_index% , game:pos
		
		cx := b%A_index%x + (b%A_index%w/2)
		cy := b%A_index%y + (b%A_index%w/2)
		
		sidea := abs(nx - cx)
		sideb := abs(ny - cy)
		
		sidea := sidea * sidea
		sideb := sideb * sideb
		
		dist := sqrt(sidea + sideb)
		
		if dist < (nw/2) + (b%A_index%w/2)
		{
			collide = 1
			break
		}
	}
	
	if collide
	msgbox, 4096, Collision!
	else
	{
		soundplay, #expand1.mp3
		sleep 1000
		Grow()
	}	
	
	return
}
My pngs are growing but I get no message when they collide. Can anyone see what is wrong with my math? Thanks
User avatar
PuzzledGreatly
Posts: 1303
Joined: 29 Sep 2013, 22:18

Re: Detect overlapping circles

27 Sep 2021, 01:57

Arg! forgot my brackets. This seems to work:

Code: Select all

if (dist < (nw/2) + (b%A_index%w/2))
User avatar
Hellbent
Posts: 2102
Joined: 23 Sep 2017, 13:34

Re: Detect overlapping circles

27 Sep 2021, 04:41

PuzzledGreatly wrote:
27 Sep 2021, 00:54
Sorry, I don't under stand the vector class idea at all.
That's ok. In that example it is just a object with a X and a Y key.
Other than that it had one math function which didn't require you to pass two values to it because the object that calls up the math function automatically gets used. So Distance := Obj1.Dist( Obj2 ) is the pixel distance between obj1 and obj2

There are a number of other math functions you can add to the class, but in your case it was all that was needed.


Anyways, here is a simple example of the whole vectors and distance thing.

This creates a red square that will jump to a new spot if the distance from your cursor to the center of the red square gets less than 60px

Code: Select all

#SingleInstance, Force
CoordMode, Mouse, Screen

Width := 100 , Height := 100

x := Random( 0 , A_ScreenWidth - Width )																	;Set a random x and y for the gui
y := Random( 0 , A_ScreenHeight - Height )
GuiPosition := New Vector( x , y ) 																			;Create a vector object ( a x/y pair ) for the gui. 

Gui, 1:+AlwaysOnTop -Caption
Gui, 1:Color, RED
Gui, 1:Show, % "x" GuiPosition.X " y" GuiPosition.Y " w" 100 " h" 100 " NA"
SetTimer, WatchCursor, 30
return

WatchCursor:
	MouseGetPos, x, y 																							;Get the cursor position
	CursorPosition 	:= New Vector( x , y ) 																		;Create a vector object ( a x/y pair ) for the cursor. 
	GuiCenter 		:= New Vector( GuiPosition.X + Width / 2 , GuiPosition.Y + Height / 2 )						;Create a vector for the center of the gui.
	
	if( CursorPosition.Dist( GuiCenter ) < 60 ){  ; Check to see if the cursor is within 60px of the center of the gui.
		
		GuiPosition := New Vector(  Random( 0 , A_ScreenWidth - Width ) , Random( 0 , A_ScreenHeight - Height ) ) 				;Create a new vector object ( a x/y pair ) for the gui. 
		Gui, 1:Show, % "x" GuiPosition.X " y" GuiPosition.Y " w" 100 " h" 100 " NA"    											;Move the window to its new position
	}
	GuiCenter := , CursorPosition := "" ;clean up
	return


Random( Min := 1 , Max := 100 ){ ;Function returns a random number
	local out
	Random, out, Min, Max
	return out
}

Class Vector	{ ;This is just a object with a x and y key and a function to get the distance between two points. 
	__New( x := 0 , y := 0 ){
		This.X := x , This.Y := y
	}
	dist( in1 ){
		return Sqrt( ( ( This.X - In1.X ) ** 2 ) + ( ( This.Y - In1.Y ) ** 2 ) ) ; Pythagorean Theorem
	}
}

*ESC::ExitApp
This one has 500 red squares that teleport away from your cursor.

Code: Select all

#SingleInstance, Force
CoordMode, Mouse, Screen
SetBatchLines, -1
SetControlDelay, -1
TargetCount := 500
Width := 20 , Height := 20
GuiPosition := []
Handles := []
Loop, % TargetCount	{
	
	GuiPosition[ A_Index ] := New Vector( Random( 10 , A_ScreenWidth - ( Width + 10 ) ) , Random( 10 , A_ScreenHeight - ( Height + 10 ) ) )
	Gui, New, +AlwaysOnTop -Caption +HwndHwnd +ToolWindow
	Gui, Color, RED
	Gui, Show, % "x" GuiPosition[ A_Index ].X " y" GuiPosition[ A_Index ].Y " w" Width " h" Height " NA"
	Handles[ A_index ] := hwnd
}
SetTimer, WatchCursor, 30
return

WatchCursor:
	MouseGetPos, x, y 																							
	CursorPosition 	:= New Vector( x , y ) 
	Loop, % TargetCount	{
		if( CursorPosition.Dist( GuiPosition[ A_Index ] ) < 20 ){  
			GuiPosition[ A_Index ] := New Vector( Random( 10 , A_ScreenWidth - ( Width + 10 ) ) , Random( 10 , A_ScreenHeight - ( Height + 10 ) ) )
			Gui, % Handles[ A_Index ] ":Show", % "x" GuiPosition[ A_Index ].X " y" GuiPosition[ A_Index ].Y " w" Width " h" Height " NA"
			Gui, % Handles[ A_Index ] ":Color", Blue
		}
	}
	return


Random( Min := 1 , Max := 100 ){
	local out
	Random, out, Min, Max
	return out
}

Class Vector	{ 
	__New( x := 0 , y := 0 ){
		This.X := x , This.Y := y
	}
	dist( in1 ){
		return Sqrt( ( ( This.X - In1.X ) ** 2 ) + ( ( This.Y - In1.Y ) ** 2 ) ) 
	}
}

*ESC::ExitApp
User avatar
boiler
Posts: 16768
Joined: 21 Dec 2014, 02:44

Re: Detect overlapping circles

27 Sep 2021, 06:17

PuzzledGreatly wrote: My pngs are growing but I get no message when they collide. Can anyone see what is wrong with my math? Thanks
Edit: I see you said it works now after a change, so maybe ignore the first comment below, but the second one would still seem valid.

I see you get the value of b1, b2, etc., from your GUI (using A_Index), but then I'm not seeing how it follows that you have the values for b1x, b1y, b1w, b2x, b2y, b2w, etc. Have you verified that those contain the values they're supposed to?

You are calling the Grow() function from within the Grow() function, which looks like would be many, many times in a recursive manner. Is there a reason for that? You would have a whole bunch instances of the function stacked up. If calling Grow() from within it is meant to be sort of like a "start the Grow() function over again", I don't think that's a good way to accomplish it.
User avatar
PuzzledGreatly
Posts: 1303
Joined: 29 Sep 2013, 22:18

Re: Detect overlapping circles

27 Sep 2021, 09:57

boiler wrote:
27 Sep 2021, 06:17
You are calling the Grow() function from within the Grow() function, which looks like would be many, many times in a recursive manner. Is there a reason for that? You would have a whole bunch instances of the function stacked up. If calling Grow() from within it is meant to be sort of like a "start the Grow() function over again", I don't think that's a good way to accomplish it.
What would be a better way to "restart" the grow function? Thanks.
User avatar
Hellbent
Posts: 2102
Joined: 23 Sep 2017, 13:34

Re: Detect overlapping circles

27 Sep 2021, 10:09

PuzzledGreatly wrote:
27 Sep 2021, 09:57
What would be a better way to "restart" the grow function? Thanks.
One way you could go about it is drop the function (or at least break part of the function into a Label and have the function called from the label)
Then you can use [goto] as many times as you want.

***Edit***
Perhaps even a timer with a negative value ( run once )
User avatar
boiler
Posts: 16768
Joined: 21 Dec 2014, 02:44

Re: Detect overlapping circles

27 Sep 2021, 10:52

PuzzledGreatly wrote:
27 Sep 2021, 09:57
What would be a better way to "restart" the grow function? Thanks.
If I were to state what you are trying to do, it is to "loop until you collide." The best code control flow is that which follows the logic most directly, in my opinion (I also think goto should only be used as a last resort). Thus, I would use loop/until like this:

Code: Select all

Grow()
{
	static cx, cy, r, n, nw, nx,ny, dist, sidea, sideb

	loop
	{
		random, r, 1000,4000
		SetTimer, grow, % - r
		
		return
		
		Grow:
		
		random, r, 3,3
		random, n, 1, 10

		grow := r = 3 ? grow : n
		
		Guicontrolget, b%grow%, game:pos
		
		random, r, 110, 150
		nw := floor(b%grow%w * (r/100))
		nx := b%grow%x - ((nw - b%grow%w)/2)
		ny := b%grow%y - ((nw - b%grow%w)/2)
		
		Guicontrol, Game:move, b%grow%, % "x" nx "y" ny  "w" nw "h" nw
		
		nx := nx + (nw/2)
		ny := ny + (nw/2)
		
		collide := ""
		loop 10
		{
			if (A_index = grow)
			continue
			
			Guicontrolget, b%A_index% , game:pos
			
			cx := b%A_index%x + (b%A_index%w/2)
			cy := b%A_index%y + (b%A_index%w/2)
			
			sidea := abs(nx - cx)
			sideb := abs(ny - cy)
			
			sidea := sidea * sidea
			sideb := sideb * sideb
			
			dist := sqrt(sidea + sideb)
			
			if (dist < (nw/2) + (b%A_index%w/2))
			{
				collide = 1
				break
			}
		}
		
		if !collide
		{
			soundplay, #expand1.mp3
			sleep 1000
		}
	} until collide
	msgbox, 4096, Collision!
	return
}

Actually, the setting of a SetTimer within the loop makes it all not seem as clean, so maybe a variation on the above might make it look better logically, but I would start with that.
User avatar
Hellbent
Posts: 2102
Joined: 23 Sep 2017, 13:34

Re: Detect overlapping circles

27 Sep 2021, 11:05

boiler wrote:
27 Sep 2021, 10:52
(I also think goto should only be used as a last resort).
Yes, it would only be useful if he actually needed recursion ( only saw the comment about restarting the function and then seeing a call to the function inside it. I never went through the logic or calculations).
User avatar
PuzzledGreatly
Posts: 1303
Joined: 29 Sep 2013, 22:18

Re: Detect overlapping circles

27 Sep 2021, 18:57

Thanks for the replies, the code I posted was just part of my project so the intention isn't exactly to loop until there is a collision. I'm intending to expand the code so recursing back to Grow() would not be the only possibility. Earlier boiler wrote:
You would have a whole bunch instances of the function stacked up.
What does this mean? I thought a function just ran? Thanks again for all your help. I need to study Hellbent's red square example in depth.
User avatar
boiler
Posts: 16768
Joined: 21 Dec 2014, 02:44

Re: Detect overlapping circles

27 Sep 2021, 19:13

PuzzledGreatly wrote:
You would have a whole bunch instances of the function stacked up.
What does this mean? I thought a function just ran? Thanks again for all your help.
No, if you call a function from within a function then the first function is still running (actually waiting) and will continue to exist until you return from the second function so it can continue executing until it terminates. So if a function keeps calling itself, then you have instance after instance running while the ones that called them are still waiting to continue. It's like holding a camera directly at a mirror and the reflections keep stacking up:
Image

For a code example, see how this function calls itself, and it would continue doing so if I didn't have it stop itself at 50 so each instance would finally return to the one that called it. After 50 simultaneous instances, it starts counting down how many are remaining, but it could have been left to go on forever. If all of those individual instances of the function didn't simultaneously exist and wait for their turn to finish executing, it would only show one MsgBox instead of 50. Yours could potentially keep going until you've run out of memory if you don't hit a collision soon enough.

Code: Select all

Fn()
return

Fn() {
	static count := 1
	count++
	if (count > 50)
		return
	else
		Fn()
	count--
	MsgBox, % count
}
User avatar
Hellbent
Posts: 2102
Joined: 23 Sep 2017, 13:34

Re: Detect overlapping circles

27 Sep 2021, 19:21

PuzzledGreatly wrote:
27 Sep 2021, 18:57
You would have a whole bunch instances of the function stacked up.
What does this mean? I thought a function just ran?
When you do what you were doing your code never hits the end of the function. It just calls up a new instance and then does the same over and over.

Sooner or later it is going to have to go back through each instance and hit the end of each function it entered or you will end up with an error.
I can't remember the exact amount you can call but I remember playing around with making a minesweeper clone and I hit the limit somewhere in the ball park of about 200 recursions. [been a few years now]
User avatar
Hellbent
Posts: 2102
Joined: 23 Sep 2017, 13:34

Re: Detect overlapping circles

27 Sep 2021, 20:59

@PuzzledGreatly

Here is an example of growing two circles until they touch.
You don't have to run the code, reading it and watching the gif might be enough but it is there if need be.
Temp (1).gif
Temp (1).gif (26.13 KiB) Viewed 1237 times

I took away the concept of classes and vectors, but it works the same.

The parts of the code that relate most to you are the 3 marked sections.

Code: Select all

#Include <My Altered GDIP lib> ;Requires the ahk gdip lib 
#Include <PopUpWindow Class> ;https://www.autohotkey.com/boards/viewtopic.php?f=6&t=94961
#SingleInstance, Force
SetBatchlines, -1
#NoEnv
GDIP_Startup()
Brush := GreenBrush := Gdip_BrushCreateSolid( "0xFF00FF00" ) 
RedBrush := Gdip_BrushCreateSolid( "0xFFFF0000" ) 

;***********************************************************
Circles := []
Circles[1] := { X: 100 , Y: 85 , R: 15 }
Circles[2] := { X: 200 , Y: 85 , R: 15 }
;***********************************************************

Width := 500 , Height := 200
Gui1 := New PopUpWindow( { WindowName: "1" , WindowOptions: " -DPIScale +AlwaysOnTop " , WindowSmoothing: 2 , X: "Center" , Y: "Center" , W: Width , H: Height } )
Gui1.ShowWindow( MyWindowTitle := "" )

SetTimer, Grow, 30
return

*ESC::ExitApp

Grow:

	;***********************************************************
	loop 2	{
		Circles[ A_Index ].R += 1
		Circles[ A_Index ].X -= 1
		Circles[ A_Index ].Y -= 1
	}
	
	CurrentDistance := Distance( Circles[ 1 ] , Circles[ 2 ] ) 	;Get the current distance between the two objects
	MinDistance := Circles[ 1 ].R + Circles[ 2 ].R 				;Get the minimum distance the two objects can be apart before they touch. ( r1 + r2 )
	
	if( CurrentDistance < MinDistance ){ ;if they are touching
		Brush := RedBrush
		SetTimer, Grow, Off
	}
	;***********************************************************
	
	Gui1.ClearWindow()
	
	Loop 2
		Gdip_FillEllipse( Gui1.G , Brush , Circles[ A_Index ].X , Circles[ A_Index ].Y , Circles[ A_Index ].R * 2 , Circles[ A_Index ].R * 2 )
	
	Gui1.UpdateWindow()
	
	return

;***********************************************************
Distance( obj1 , obj2 ){
		return Sqrt( ( ( obj1.X - obj2.X ) ** 2 ) + ( ( obj1.Y - obj2.Y ) ** 2 ) ) 
}
;***********************************************************
User avatar
PuzzledGreatly
Posts: 1303
Joined: 29 Sep 2013, 22:18

Re: Detect overlapping circles

28 Sep 2021, 22:22

Thanks for the explanations. I'm separating my functions to make sure each one finishes before it is called again, I'm trying to do a semi-random layout of 10 circles and they shouldn't touch initially. My Collision function works when I run my whole gui but not when I am doing the initial layout. Here is the layout function:

Code: Select all

LayOut() ;_W and _H = monitor's width and height
{
	static n := "1|2|3|4|5|6|7|8|9|10",f, z
	sort, n, random D|
	
	loop 10
	{
		random r, 4, 12
		s%A_index% := floor((_H/100) * r)
	}	
	
	loop, parse, n, |
	{	
		z := A_index
		f := A_loopfield
		while !Ayes(orb, f)
		{
			random, rx, floor(_W * 0.04), floor(_W - (_H * 0.16))
			
			if (z < 6)
			random, ry, floor(_H * 0.06), floor(_H * 0.34)
			else
			random, ry, floor(_H * 0.50), floor(_H * 0.84)
			
			if !Collide(f, rx,ry, s%f%)
			{
				Guicontrol, Game:move, s%f%, % "x" rx "y" ry "w" s%f% "h" s%f%
				orb.push(f)
			}
		}
	}

	return
}

Ayes(a,i)
{
	loop % a.Length()
	if (i = a[A_index])
	return true
}

And here is the Collision check:

Code: Select all

Collide(i, ax,ay,aw) ;item, x, y, width
{
	static collide, dist, sidea, sideb, viz
	static ar, vi, vr, vx, vy
	
	ar := aw/2
	
	collide := ""
	loop % orb.length()
	{
		vi := orb[A_index]
		Guicontrolget, viz, game:visible, s%vi%
		
		If vi = i
		continue
		
		Guicontrolget, s%vi% , game:pos
		
		vr := s%vi%w/2
		vx := s%vi%x + vr
		vy := s%vi%y + vr
		
		sidea := abs(ax - vx)
		sideb := abs(ay - vy)
			
		dist := sqrt((sidea * sidea) + (sideb * sideb))
		
		if (dist < ar + vr)
		{
			collide := vi
			break
		}
	}

	Return collide
}
When I start the Gui I sometimes have overlaps but I shouldn't. Can anyone spot what I am doing wrong? Thanks.

Edited to include the Ayes function
User avatar
Hellbent
Posts: 2102
Joined: 23 Sep 2017, 13:34

Re: Detect overlapping circles

29 Sep 2021, 04:16

Two things.

1. You didn't include _W and _H
2. You should consider using better names for some of your variables, having them all as short as possible just makes it a nightmare to follow.

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Google [Bot], mikeyww, mmflume, ShatterCoder and 154 guests