Arrow to Point to Off Screen Target - Slope Error?

BlitzMax Forums/BlitzMax Programming/Arrow to Point to Off Screen Target - Slope Error?

therevills(Posted 2015) [#1]
Hi All,

I'm trying to create an arrow to point to an off screen target, but now and again I get the slope calculation wrong and it calculates 9.99999997e-007. Which of course the displays the arrow in the wrong spot.

Here is the arrow positional code:

Method CalcArrow(ax:Float, ay:Float)
	Local centerX:Float = ax - (SCREEN_WIDTH / 2)
	Local centerY:Float = ay - (SCREEN_HEIGHT / 2)

	Local slope:Float = centerY / centerX
		
	Local pad:Int = 200
	Local paddingX:Int = pad
	Local paddingY:Int = pad
		
	Local padWidth:Float = SCREEN_WIDTH - paddingX
	Local padHeight:Float = SCREEN_HEIGHT - paddingY
		
	If centerY < 0
		arrowX = (-padHeight / 2) / slope
		arrowY = -padHeight / 2
	Else
		arrowX = (padHeight / 2) / slope
		arrowY = padHeight / 2
	EndIf
		
	If arrowX < - padWidth / 2
		arrowX  = -padWidth / 2
		arrowY  = slope * -padWidth / 2
	ElseIf arrowX > padWidth / 2
		arrowX = padWidth / 2
		arrowY = slope * padWidth / 2
	EndIf
		
	If id = 1 Then
		DebugLog "slope = "+ slope
	EndIf
		
	arrowAngle = GetAngle2D(SCREEN_WIDTH / 2, SCREEN_HEIGHT/ 2, ax, ay)
	arrowX = arrowX + SCREEN_WIDTH / 2
	arrowY = arrowY + SCREEN_HEIGHT / 2

EndMethod


And here is some runnable code:


Can anyone see what I have done wrong?

Thanks!


therevills(Posted 2015) [#2]
Typically, as soon as I post for help I find the issue afterwards...

The issue is with this bit of the code:
Local centerX:Float = ax - (SCREEN_WIDTH / 2)
Local centerY:Float = ay - (SCREEN_HEIGHT / 2)

Local slope:Float = centerY / centerX


There are times when ax can be the same as SCREEN_WIDTH / 2 and ay can be the same as SCREEN_HEIGHT / 2. Which makes centerX/centerY close to zero (because of floating point inaccuracies). So I am now using a "close to zero" function to set them to a positive value:

Local centerX:Float = ax - (SCREEN_WIDTH / 2)
Local centerY:Float = ay - (SCREEN_HEIGHT / 2)

If CloseToZero(cx, 0.5) Then cx = 0.5
If CloseToZero(cy, 0.5) Then cy = 0.5

Local slope:Float = centerY / centerX

...

Function CloseToZero:Int(amount:Double, precision:Double)
	If amount= 0 Return True
	If amount> 0 And amount< precision  Return True
	If amount< 0 And amount> 0 - precision Return True
	Return False
EndFunction


Not perfect but good enough (after spending hours looking at the code!!)


Floyd(Posted 2015) [#3]
I don't see how "slope" contributes anything except a possible source of error. ATan2 already gives the necessary angle information. Notice that slope can legitimately be infinite for vertical lines.

Slope only defines an orientation for a line, with a range of 180 degrees. Since you use the term Arrow I'm guessing you really want a range of 360 degrees, which ATan2 provides.

One other comment, you use ATan2(dy, dx) + 360 Mod 360, apparently to change the range to 0 to 360 rather than -180 to +180. There is nothing wrong with using -180 to +180 and in fact you already are.

Mod has the same precedence as division. ATan2(dy, dx) + 360 Mod 360 is treated as ATan2(dy, dx) + ( 360 Mod 360 ), where 360 mod 360 is zero.


therevills(Posted 2015) [#4]
The "slope" I am referring to is the Slope–intercept form in Linear equations:

http://en.wikipedia.org/wiki/Linear_equation#Slope.E2.80.93intercept_form

If we can do this without the linear equations that would be great!

Execute the runnable example to show you what I am after.


Floyd(Posted 2015) [#5]
As mentioned you are already using ATan2 to get an orientation angle for the "arrow". The slope of the arrow is just the tangent of this angle, so it provides no new information. In fact it loses something because it only covers 180 degrees. For example it can't distinguish the direction of the positive x-axis from the negative x-axis. They both have the same slope ( zero ) but have ATan2 angles of 0 and 180.