#include <WindowsConstants.au3>
#include <GUIConstants.au3>
#include <GDIPlus.au3>
#include <WinAPI.au3>

; - Author: name22 (www.autoit.de)

Opt("GUIOnEventMode", 1)

;###-v-SETTINGS-v-###

$iGUIWidth = 400
$iGUIHeight = 400
$iARGB_BG = 0xFFFFFFFF
$nFPS = 60

$nGravity = 10
$nAirFriction = 0.99
$nGroundFriction = 0.99
$nBounciness = 0.6

$nAccellForce = 5

$nX_CirclePos = $iGUIWidth / 2
$nY_CirclePos = 10
$nX_CircleVel = 0
$nY_CircleVel = 0

$nX_FixPoint = $iGUIWidth / 2
$nY_FixPoint = $iGUIHeight

$nRadiusCircle = 10
$nRadiusFixCircle = 250

;###-^-SETTINGS-^-###

$bContact = True
$bMouseDown = False
$nX_NewVel = 0
$nY_NewVel = 0

$nSleepTime = 1000 / $nFPS

$nFPS_Display = 0
$nFPS_Average = $nFPS

$vNTdll = DllOpen("ntdll.dll")

$tPrecSleep = DllStructCreate("int64 time;")
$pPrecSleep = DllStructGetPtr($tPrecSleep)

$hMain = GUICreate("Example by name22", $iGUIWidth, $iGUIHeight)
GUISetState()

$hDC_Main = _WinAPI_GetDC($hMain)
$hDC_Buffer = _WinAPI_CreateCompatibleDC($hDC_Main)
$hBitmap_Buffer = _WinAPI_CreateCompatibleBitmap($hDC_Main, $iGUIWidth, $iGUIHeight)
_WinAPI_SelectObject($hDC_Buffer, $hBitmap_Buffer)

_GDIPlus_Startup()

$hGraphics = _GDIPlus_GraphicsCreateFromHDC($hDC_Buffer)
_GDIPlus_GraphicsSetSmoothingMode($hGraphics, 2)

$hBrush_Black = _GDIPlus_BrushCreateSolid(0xFF000000)
$hBrush_Circle = _GDIPlus_BrushCreateSolid(0xFFFF0000)

$hPen_Circle = _GDIPlus_PenCreate(0xFF0000FF, 2)
$hPen_NewVel = _GDIPlus_PenCreate(0xF000FF00, 5)
_GDIPlus_PenSetEndCap($hPen_NewVel, $GDIP_LINECAPARROWANCHOR)

$hStringFormat = _GDIPlus_StringFormatCreate()
$hFamily_FPS = _GDIPlus_FontFamilyCreate("Visitor TT1 BRK")
$hFont_FPS = _GDIPlus_FontCreate($hFamily_FPS, 7)

$aMeasure = _GDIPlus_GraphicsMeasureString($hGraphics, "FPS: 000", $hFont_FPS, _GDIPlus_RectFCreate(), $hStringFormat)
$tLayout_FPS = $aMeasure[0]
$aMeasure = ""
DllStructSetData($tLayout_FPS, "X", $iGUIWidth - DllStructGetData($tLayout_FPS, "Width") - 3)
DllStructSetData($tLayout_FPS, "Y", $iGUIHeight - DllStructGetData($tLayout_FPS, "Height"))
DllStructSetData($tLayout_FPS, "Width", DllStructGetData($tLayout_FPS, "Width") + 3)

GUISetOnEvent($GUI_EVENT_CLOSE, "_Close", $hMain)
OnAutoItExitRegister("_Shutdown")

$nT_Sleep = TimerInit() + $nSleepTime
$nT_UpdateFPS = TimerInit()
$nT_AdjustSleepTime = TimerInit()
While True
	DllStructSetData($tPrecSleep, "time", -10000 * ($nSleepTime - TimerDiff($nT_Sleep)))
	DllCall($vNTdll, "dword", "ZwDelayExecution", "int", 0, "ptr", $pPrecSleep)
	$nFrameTime = TimerDiff($nT_Sleep)
	$nT_Sleep = TimerInit()

	$nFPS_Cur = 1000 / $nFrameTime
	If TimerDiff($nT_UpdateFPS) >= 500 Then
		$nFPS_Display = $nFPS_Cur
		$nT_UpdateFPS = TimerInit()
	EndIf
	If TimerDiff($nT_AdjustSleepTime) >= 5000 Then
		$nFPS_Average = ($nFPS_Average + $nFPS_Cur) / 2
		If $nFPS_Average - $nFPS < -2 Then $nSleepTime += 0.02
		If $nFPS_Average - $nFPS < 2 Then $nSleepTime -= 0.02
		$nT_AdjustSleepTime = TimerInit()
	EndIf

	$aCursorInfo = GUIGetCursorInfo($hMain)

	_GDIPlus_GraphicsClear($hGraphics, $iARGB_BG)

	Switch $aCursorInfo[2]
		Case True
			Switch $bMouseDown
				Case True
					$nX_NewVel = $aCursorInfo[0] - $nX_CirclePos
					$nY_NewVel = $aCursorInfo[1] - $nY_CirclePos
					_GDIPlus_GraphicsDrawLine($hGraphics, $nX_CirclePos, $nY_CirclePos, $nX_CirclePos + $nX_NewVel, $nY_CirclePos + $nY_NewVel, $hPen_NewVel)
				Case False
					GUISetCursor(16, 1, $hMain)
					$bMouseDown = True
					$nX_CirclePos = $aCursorInfo[0]
					$nY_CirclePos = $aCursorInfo[1]
			EndSwitch
		Case False
			Switch $bMouseDown
				Case True
					GUISetCursor(2, 1, $hMain)
					$bMouseDown = False
					$nX_CircleVel = $nX_NewVel * $nAccellForce
					$nY_CircleVel = $nY_NewVel * $nAccellForce
			EndSwitch

			$nY_CircleVel += $nGravity
			$nX_CircleVel *= $nAirFriction
			$nY_CircleVel *= $nAirFriction

			$nX_V = $nX_CircleVel / $nFPS_Cur
			$nY_V = $nY_CircleVel / $nFPS_Cur
			$nL_V = Sqrt($nX_V ^ 2 + $nY_V ^ 2)


			$nX_N = $nX_V / $nL_V
			$nY_N = $nY_V / $nL_V

			$nX_C = $nX_FixPoint - $nX_CirclePos
			$nY_C = $nY_FixPoint - $nY_CirclePos
			$nL_C = Sqrt($nX_C ^ 2 + $nY_C ^ 2)

			Switch $nL_V > 0
				Case True
					Switch $nL_C - ($nRadiusCircle + $nRadiusFixCircle) <= $nL_V
						Case True
							$nD = $nX_N * $nX_C + $nY_N * $nY_C
							Switch $nD >= 0
								Case True
									$nF = $nL_C ^ 2 - $nD ^ 2
									Switch $nF <= ($nRadiusCircle + $nRadiusFixCircle) ^ 2
										Case True
											$nDistance = $nD - Sqrt(($nRadiusCircle + $nRadiusFixCircle) ^ 2 - $nF)
											Switch $nDistance <= $nL_V
												Case True
													$nX_CirclePos += $nX_N * $nDistance
													$nY_CirclePos += $nY_N * $nDistance

													$nX_C = $nX_FixPoint - $nX_CirclePos
													$nY_C = $nY_FixPoint - $nY_CirclePos
													$nL_C = Sqrt($nX_C ^ 2 + $nY_C ^ 2)

													$nX_UN = $nX_C / $nL_C
													$nY_UN = $nY_C / $nL_C

													$nVel_N = ($nX_UN * $nX_CircleVel + $nY_UN * $nY_CircleVel) * $nBounciness
													$nVel_T = (-$nY_UN * $nX_CircleVel + $nX_UN * $nY_CircleVel) * $nGroundFriction

													$nX_CircleVel = -$nVel_N * $nX_UN + $nVel_T * - $nY_UN
													$nY_CircleVel = -$nVel_N * $nY_UN + $nVel_T * $nX_UN
											EndSwitch
									EndSwitch
							EndSwitch
					EndSwitch
			EndSwitch
			Switch True
				Case $nX_CirclePos + $nX_CircleVel / $nFPS_Cur <= $nRadiusCircle
					$nX_CircleVel *= -$nBounciness
					$nY_CircleVel *= $nGroundFriction
					$nX_CirclePos = $nRadiusCircle
				Case $nX_CirclePos + $nX_CircleVel / $nFPS_Cur >= $iGUIWidth - $nRadiusCircle
					$nX_CircleVel *= -$nBounciness
					$nY_CircleVel *= $nGroundFriction
					$nX_CirclePos = $iGUIWidth - $nRadiusCircle
			EndSwitch
			Switch True
				Case $nY_CirclePos + $nY_CircleVel / $nFPS_Cur <= $nRadiusCircle
					$nY_CircleVel *= -$nBounciness
					$nX_CircleVel *= $nGroundFriction
					$nY_CirclePos = $nRadiusCircle
				Case $nY_CirclePos + $nY_CircleVel / $nFPS_Cur >= $iGUIHeight - $nRadiusCircle
					$nY_CircleVel *= -$nBounciness
					$nX_CircleVel *= $nGroundFriction
					$nY_CirclePos = $iGUIHeight - $nRadiusCircle
			EndSwitch

			$nX_CirclePos += $nX_CircleVel / $nFPS_Cur
			$nY_CirclePos += $nY_CircleVel / $nFPS_Cur
	EndSwitch

	_GDIPlus_GraphicsFillEllipse($hGraphics, $nX_CirclePos - $nRadiusCircle, $nY_CirclePos - $nRadiusCircle, $nRadiusCircle * 2, $nRadiusCircle * 2, $hBrush_Circle)
	_GDIPlus_GraphicsDrawEllipse($hGraphics, $nX_FixPoint - $nRadiusFixCircle, $nY_FixPoint - $nRadiusFixCircle, $nRadiusFixCircle * 2, $nRadiusFixCircle * 2, $hPen_Circle)
	_GDIPlus_GraphicsDrawStringEx($hGraphics, "FPS: " & Int($nFPS_Display), $hFont_FPS, $tLayout_FPS, $hStringFormat, $hBrush_Black)
	_WinAPI_BitBlt($hDC_Main, 0, 0, $iGUIWidth, $iGUIHeight, $hDC_Buffer, 0, 0, $SRCCOPY)
WEnd

Func _Close()
	Exit
EndFunc   ;==>_Close

Func _Shutdown()
	_WinAPI_ReleaseDC($hMain, $hDC_Main)
	_WinAPI_DeleteDC($hDC_Buffer)
	_WinAPI_DeleteObject($hBitmap_Buffer)

	_GDIPlus_GraphicsDispose($hGraphics)
	_GDIPlus_BrushDispose($hBrush_Black)
	_GDIPlus_BrushDispose($hBrush_Circle)
	_GDIPlus_PenDispose($hPen_Circle)
	_GDIPlus_PenDispose($hPen_NewVel)
	_GDIPlus_StringFormatDispose($hStringFormat)
	_GDIPlus_FontFamilyDispose($hFamily_FPS)
	_GDIPlus_FontDispose($hFont_FPS)
	_GDIPlus_Shutdown()

	DllClose($vNTdll)
EndFunc   ;==>_Shutdown

;- Copyright: name22, Donnerstag 12. Mai 2011


; #FUNCTION#;===============================================================================
;
; Name...........: _HighPrecisionSleep()
; Description ...: Sleeps down to 0.1 microseconds
; Syntax.........: _HighPrecisionSleep( $iMicroSeconds, $hDll=False)
; Parameters ....:  $iMicroSeconds      - Amount of microseconds to sleep
;                  $hDll  - Can be supplied so the UDF doesn't have to re-open the dll all the time.
; Return values .: None
; Author ........: Andreas Karlsson (monoceres)
; Modified.......:
; Remarks .......: Even though this has high precision you need to take into consideration that it will take some time for autoit to call the function.
; Related .......:
; Link ..........;
; Example .......; No
;
;;==========================================================================================
Func _HighPrecisionSleep($iMicroSeconds, $hDll = False)
	Local $hStruct, $bLoaded
	If Not $hDll Then
		$hDll = DllOpen("ntdll.dll")
		$bLoaded = True
	EndIf
	$hStruct = DllStructCreate("int64 time;")
	DllStructSetData($hStruct, "time", -1 * ($iMicroSeconds * 10))
	DllCall($hDll, "dword", "ZwDelayExecution", "int", 0, "ptr", DllStructGetPtr($hStruct))
	If $bLoaded Then DllClose($hDll)
EndFunc   ;==>_HighPrecisionSleep