Jump to content


Constrain Mouse to a circle of X radius


  • Please log in to reply
12 replies to this topic

#1 Guests

  • Guests

Posted 10 August 2012 - 01:45 PM

Hi guys,

I'm trying to write a script which will prevent the mouse moving outside of a circle
of a set radius of pixels e.g. 100. Now it has been over 10 years since i have played with trigonometry at school...and Im not much of a programmer either - so can some one here see where i have gone wrong.



Posted Image


At the beginning of the function, the original mouse position is set to 0,0 (the center of the above circle),

For the large triangle: X = 120, Y = 90
therefore the angle & = Atan ( 90 / 120 )

Find the hypotenuse of the large triangle c = Sqrt( A^2 + B^2)
If C is > 100 pixels
...well you can see where i'm going with this from my code.

This script:
circle_radius_max := 200 ;hypotenuse of little triangle

F1::
	MouseGetPos, center_x, center_y ;set the current location to be the center of the circle
	stop := "go"
	SetTimer, circle, -1
Return

F2::stop := "stop"
	
circle:
Loop,
{
	if ( stop = "stop" )
		Break
	MouseGetPos, current_x, current_y
	offset_x := current_x - center_x	;make the ceter of the circle (0,0) - offset_x = x of big triangle
	offset_y := current_y - center_y	;(0,0) - offset_y = y of big triangle
	
	radius_current := Sqrt((offset_x * offset_x) + (offset_y * offset_y)) ; find the radius of the big/current triangle
	if ( radius_current > circle_radius_max )
	{
		if (offset_x < 0)
		{
			circle_angle := ATan(offset_x/offset_Y) ; big triangle
			move_x := center_x + Sin(circle_angle) * circle_radius_max ;little triangle
			move_y := center_y + Cos(circle_angle) * circle_radius_max ;little triangle
			MouseMove, move_x, move_y
		}
		Else
		{	
			circle_angle := ATan(offset_y/offset_x) ; big triangle
			move_x := center_x + cos(circle_angle) * circle_radius_max ;little triangle
			move_y := center_y + sin(circle_angle) * circle_radius_max ;little triangle
			MouseMove, move_x, move_y
		}
	}
}
return

Produces this:
(The mouse can't enter the left-top-quarter of the circle.
Posted Image


Many thanks in advance.

#2 ibbignerd

ibbignerd
  • Members
  • 37 posts

Posted 10 August 2012 - 02:27 PM

How can X=120 when the Hypotenuse (radius) is 100?

#3 Guests

  • Guests

Posted 10 August 2012 - 02:49 PM

How can X=120 when the Hypotenuse (radius) is 100?

The X there is refering to the large large triangle - which is (partly) formed by the dashed lines. Hence, X = 120 (which should be 130)...unfortunately i cant edit the post as im a guest. - But the code explains it better than that semi written example.

The 100 pixel hypontenuse/radius is only for the small triangle/circle.

#4 ibbignerd

ibbignerd
  • Members
  • 37 posts

Posted 10 August 2012 - 03:14 PM

Ok, so to give a more detailed explanation of what's happening, this is what I got.

Posted Image

When you move your mouse to the 2nd quadrant border, it moves you to the opposite side through the center.

When you hit (0,200) it moves to (200,0).
When you hit (0,-200) it moves to (200,0).
When you hit (-200,0) it moves to (0,-200).

#5 Gogo

Gogo
  • Guests

Posted 10 August 2012 - 03:30 PM

ox:=300, oy:=200, r:=100                     [color=#008000]; center and radius of the circle[/color]



F1:: SetTimer Mice, % (t:=!t) ? 50 : "Off"   [color=#008000]; F1 to trap/free the mouse[/color]



Mice:

  CoordMode mouse, screen

  MouseGetPos x, y

  dx:= x-ox, dy:= y-oy           [color=#008000]; distances from center[/color]

  hyp:= Sqrt(dx*dx + dy*dy)      [color=#008000]; hypotenuse[/color] 

  If (hyp < r)

     exit

  scale:= r / hyp                [color=#008000]; correction scale[/color]

  dx:= dx*scale, dy:= dy*scale   [color=#008000]; to correct distances[/color]

  MouseMove ox+dx, oy+dy, 0

  return


#6 ibbignerd

ibbignerd
  • Members
  • 37 posts

Posted 10 August 2012 - 03:35 PM

Gogo's code requires autohotkey_l

#7 ibbignerd

ibbignerd
  • Members
  • 37 posts

Posted 10 August 2012 - 03:41 PM

The only problem with all this is that it's not an absolute border. You can go outside of the circle when you move the mouse fast enough

#8 Guests

  • Guests

Posted 10 August 2012 - 04:20 PM

Thanks gogo! That's excellent.

One thing though, when i add the following
F1::
{
	CoordMode mouse, screen
	MouseGetPos x, y
	ox:= x , oy= y
	SetTimer Mice, % (t:=!t) ? 1 : "Off"   ; F1 to trap/free the mouse

}

The 'center' point when you press F1, is not actually the center of the circle it draws. It's off to the side of the circle.

Also thanks ibbignerd for the detailed description of what was going on. I now see at some of those points i was dividing by 0 in the tan calculation. Also, I didn't account for some values being negative relative to the center....I think.

#9 Guests

  • Guests

Posted 10 August 2012 - 05:13 PM

This also seems to work, but Gogo's code is ever so slightly more elegant lol........

circle_radius_max := 200 ;hypotenuse of little triangle
CoordMode mouse, screen
F1::
	MouseGetPos, center_x, center_y ;set the current location to be the center of the circle
	stop := "go"
	SetTimer, circle, -1
Return

F2::stop := "stop"
	
circle:
Loop,
{
	if ( stop = "stop" )
		Break
	MouseGetPos, current_x, current_y
	offset_x := current_x - center_x	;make the ceter of the circle (0,0) - offset_x = x of big triangle
	offset_y := current_y - center_y	;(0,0) - offset_y = y of big triangle
	
	radius_current := Sqrt((offset_x * offset_x) + (offset_y * offset_y)) ; find the radius of the big/current triangle
	if ( radius_current > circle_radius_max )
	{
		if (offset_x = 0)
		{
			circle_angle := 0 ; big triangle
			IF (offset_y >= 0 )
				move_y := center_y +  circle_radius_max ;little triangle
			else
				move_y := center_y - circle_radius_max ;little triangle
			MouseMove, current_x, move_y
		}
		else if (offset_Y = 0)
		{
			circle_angle := 0 ; big triangle
			IF (offset_x > 0 )
				move_x := center_x + circle_radius_max ;little triangle
			Else
				move_x := center_x - circle_radius_max ;little triangle
			move_y := center_y ;little triangle
			MouseMove, move_x, current_y
		}
		Else
		{	
		offset_X_TAN := Sqrt(offset_X * offset_X)
		offset_y_TAN := Sqrt(offset_y * offset_y)
			circle_angle := ATan(offset_y_TAN/offset_X_TAN) ; big triangle
			if ( offset_x > 0 )
				move_x := center_x + cos(circle_angle) * circle_radius_max ;little triangle
			Else
				move_x := center_x - cos(circle_angle) * circle_radius_max ;little triangle
			if ( offset_y < 0 )	
				move_y := center_y - sin(circle_angle) * circle_radius_max ;little triangle
			Else
				move_y := center_y + sin(circle_angle) * circle_radius_max ;little triangle
			MouseMove, move_x, move_y
		}

	}
}
return

The only problem with all this is that it's not an absolute border. You can go outside of the circle when you move the mouse fast enough


Yeah i'm a little disappointed in this as well.

#10 Gogo

Gogo
  • Guests

Posted 10 August 2012 - 06:37 PM

To have "an absolute border" you should use a hook.
[color=#008000]; stolen from Laszlo[/color]
SetBatchLines -1
SetMouseDelay -1        [color=#008000]; fastest action[/color]
DllCall("SetWindowsHookEx",Int,14, Uint,RegisterCallback("Mouse","F") ; WH_MOUSE_LL = 14 
       , UInt,DllCall("GetModuleHandle",UInt,0), UInt,0) 
       
F1::                    [color=#008000]; F1 to trap/free the mouse[/color]
   trap:= !trap
   CoordMode mouse, screen
   MouseGetPos ox, oy
   r:= 100
   return

Mouse(nCode, wParam, lParam) { [color=#008000]; low-level mouse handler[/color]
   global
   CoordMode mouse, screen
   If (wParam = 0x200)
      If trap {
         dx := NumGet(lParam+0,"Short") - ox
         dy := NumGet(lParam+4,"Short") - oy
         scrl:= r/Sqrt(dx*dx + dy*dy)
         If (scrl < 1) {
            MouseMove Round(dx*scrl+ox), Round(dy*scrl+oy), 0
            return 1
         }
      }
   return DllCall("CallNextHookEx", UInt,0, Int,nCode, UInt,wParam, UInt,lParam) 
}


#11 ibbignerd

ibbignerd
  • Members
  • 37 posts

Posted 10 August 2012 - 07:48 PM

To have "an absolute border" you should use a hook.

[color=#008000]; stolen from Laszlo[/color]
SetBatchLines -1
SetMouseDelay -1        [color=#008000]; fastest action[/color]
DllCall("SetWindowsHookEx",Int,14, Uint,RegisterCallback("Mouse","F") ; WH_MOUSE_LL = 14 
       , UInt,DllCall("GetModuleHandle",UInt,0), UInt,0) 
       
F1::                    [color=#008000]; F1 to trap/free the mouse[/color]
   trap:= !trap
   CoordMode mouse, screen
   MouseGetPos ox, oy
   r:= 100
   return

Mouse(nCode, wParam, lParam) { [color=#008000]; low-level mouse handler[/color]
   global
   CoordMode mouse, screen
   If (wParam = 0x200)
      If trap {
         dx := NumGet(lParam+0,"Short") - ox
         dy := NumGet(lParam+4,"Short") - oy
         scrl:= r/Sqrt(dx*dx + dy*dy)
         If (scrl < 1) {
            MouseMove Round(dx*scrl+ox), Round(dy*scrl+oy), 0
            return 1
         }
      }
   return DllCall("CallNextHookEx", UInt,0, Int,nCode, UInt,wParam, UInt,lParam) 
}

That code doesn't work...

#12 guest89

guest89
  • Guests

Posted 10 August 2012 - 09:57 PM

do not use dllcall in modern games. they do not work. i can not speak for all games but the popular ones do not work.

#13 Guests

  • Guests

Posted 11 August 2012 - 01:53 AM

To have "an absolute border" you should use a hook.



Thanks again mate.

That works an absolute treat!!