Jump to content

Other than headings is there a fast/clever way to use maths to tell if a bandit is on your left or right side of your 12/6 line?


TEMPEST.114

Recommended Posts

I'm useless at maths beyond basic trig, so vectors and quadratics and stuff is an immense struggle.

I can 'brute force' this by looking at headings and working it out but it's a pain and slow and lots of if statements to deal with the wrap arounds.

I was wondering if there is a clever and faster way to tell if - REGARDLESS of your ownship heading - another aircraft or any unit really is on the left of your nose to tail line or on the right side, or even if it's perfectly on the nose/tail line.

Anyone much smarter at maths than me know of a cool way? (Bonus gratitude if you can explain HOW the solution works too)

Thanks

Link to comment
Share on other sites

42 minutes ago, Elphaba said:

I was wondering if there is a clever and faster way to tell if - REGARDLESS of your ownship heading - another aircraft or any unit really is on the left of your nose to tail line or on the right side, or even if it's perfectly on the nose/tail line.

Since your 12/6 line *is* your heading, no. To find out on which side it is is usually a very quick calculation: Your plane's location defines a point, your plane's heading (actually: the XZ components of your velocity) defines a vector that together with your position defines a line on the map (i.e. it's a 2D problem, much simplified). So all you need to do is find out on which side of a line the other plane's location is. IIRC, it boils down to two multiplications and five subtractions (if you have the ownship's velocity, you don't even need the heading), and all you look at is the sign: if it's negative it's on the right side, positive it's on the left (or vice versa, depending on your coordinate system, but it's always the same). If the number is very close to 0, it's dead ahead.

Here's an easy formula I use to determine if a point P is left of a line from A to B (the funny name of that method is because we use DCS's XZ plane - the map plane):

function cfxZones.isLeftXZ(A, B, P)
	return ((B.x - A.x)*(P.z - A.z) - (B.z - A.z)*(P.x - A.x)) > 0
end

In your code, A would be the location of the plane, B would be the A + Plane's velocity (i.e. a point forward and in the direction of your plane) and P the bogey's location. 

It's so easy, you'd be hard pressed not to use it 🙂 

 


Edited by cfrag
  • Thanks 1
Link to comment
Share on other sites

2 hours ago, cfrag said:

Since your 12/6 line *is* your heading, no. To find out on which side it is is usually a very quick calculation: Your plane's location defines a point, your plane's heading (actually: the XZ components of your velocity) defines a vector that together with your position defines a line on the map (i.e. it's a 2D problem, much simplified). So all you need to do is find out on which side of a line the other plane's location is. IIRC, it boils down to two multiplications and five subtractions (if you have the ownship's velocity, you don't even need the heading), and all you look at is the sign: if it's negative it's on the right side, positive it's on the left (or vice versa, depending on your coordinate system, but it's always the same). If the number is very close to 0, it's dead ahead.

Here's an easy formula I use to determine if a point P is left of a line from A to B (the funny name of that method is because we use DCS's XZ plane - the map plane):

function cfxZones.isLeftXZ(A, B, P)
	return ((B.x - A.x)*(P.z - A.z) - (B.z - A.z)*(P.x - A.x)) > 0
end

In your code, A would be the location of the plane, B would be the A + Plane's velocity (i.e. a point forward and in the direction of your plane) and P the bogey's location. 

It's so easy, you'd be hard pressed not to use it 🙂 

 

 

To my rescue again thanks @cfrag And thanks for the lesson. I'd never have come up with that on my own; I wish I knew more about maths. 

Link to comment
Share on other sites

7 hours ago, Elphaba said:

Vector Cross Product and then Dot Product will do this too.

Indeed - the formula in isLeftXZ() is *exactly* doing that 🙂 - the cross product gets the normal vector N of the line AB (which is perpendicular to the line AB, always pointing to the right of the line), and the dot product of N with AP then "projects" AP on N (like a shadow: it calculates how long the projection of AP is on the vector N). If the result is positive, they point in the same direction (AP and N), and therefore (since N always points right) P is on the right side of the line; if the projection's value is negative, P is on the other side of the line. If the value is very close to 0, AB and P are (almost) collinear, and therefore P directly in front or directly behind AB.

image.png

Apologies for not going into details before, you seemed a bit uncomfortable with vector math so I thought I'd spare you 🙂  

image.png


Edited by cfrag
  • Thanks 1
Link to comment
Share on other sites

40 minutes ago, cfrag said:

Indeed - the formula in isLeftXZ() is *exactly* doing that 🙂 - the cross product gets the normal vector N of the line AB (which is perpendicular to the line AB, always pointing to the right of the line), and the dot product of N with AP then "projects" AP on N (like a shadow: it calculates how long the projection of AP is on the vector N). If the result is positive, they point in the same direction (AP and N), and therefore (since N always points right) P is on the right side of the line; if the projection's value is negative, P is on the other side of the line. If the value is very close to 0, AB and P are (almost) collinear, and therefore P directly in front or directly behind AB.

image.png

Apologies for not going into details before, you seemed a bit uncomfortable with vector math so I thought I'd spare you 🙂  

image.png

 

Ah. I appreciate it, I DO actually want to learn the maths, but just seeing the formula means nothing to me... but when it's explained and can be applied to a specific problem I understand, then I can internalise it and learn. And I love learning. 

Thanks for the diagram - not see that but it's SUPER helpful. 

45 minutes ago, cfrag said:

Indeed - the formula in isLeftXZ() is *exactly* doing that 🙂 - the cross product gets the normal vector N of the line AB (which is perpendicular to the line AB, always pointing to the right of the line), and the dot product of N with AP then "projects" AP on N (like a shadow: it calculates how long the projection of AP is on the vector N). If the result is positive, they point in the same direction (AP and N), and therefore (since N always points right) P is on the right side of the line; if the projection's value is negative, P is on the other side of the line. If the value is very close to 0, AB and P are (almost) collinear, and therefore P directly in front or directly behind AB.

image.png

Apologies for not going into details before, you seemed a bit uncomfortable with vector math so I thought I'd spare you 🙂  

image.png

 

Don't go throwing expensive words like 'collinear' at me... I have no idea what it means and I'm sure I can't afford it. 

Link to comment
Share on other sites

easy... 

image.png


Edited by PravusJSB

Creator & Developer of XSAF ::An AI model that wants to kill you, and needs no help from humans.

Discord: PravusJSB#9484   twitch.tv/pravusjsb  https://www.patreon.com/XSAF  https://discord.gg/pC9EBe8vWU https://bmc.link/johnsbeaslu

Work with me on Fiverr: https://www.fiverr.com/pravusjsb

Link to comment
Share on other sites

6 minutes ago, PravusJSB said:

easy...

Not so fast... 🙂 points have no directionality, so what is shown above with 'left' or 'right' doesn't make sense. Above calculates the heading from p1 to p2 and calculates left/right only if P1 has an own heading of 0. The same can be much easier had when you check if x2 < x1. What above does not calculate is if P1 is flying at a heading of 120° degrees, on what side of P1's flight path point P2 is.


Edited by cfrag
Link to comment
Share on other sites

Im not going to argue with the future of Humanity.

Creator & Developer of XSAF ::An AI model that wants to kill you, and needs no help from humans.

Discord: PravusJSB#9484   twitch.tv/pravusjsb  https://www.patreon.com/XSAF  https://discord.gg/pC9EBe8vWU https://bmc.link/johnsbeaslu

Work with me on Fiverr: https://www.fiverr.com/pravusjsb

Link to comment
Share on other sites

5 minutes ago, PravusJSB said:

Im not going to argue with the future of Humanity.

Wuss. 

Plus incorrect wuss; AI isn't the future of Humanity it's the end of Humanity.

 

function IsTargetLeftOfUnit(unit, targetUnit)
 
  local angle       = math.atan2(targetUnit:getPoint().y - unit:getPoint().y, targetUnit:getPoint().x - unit:getPoint().x)
  local direction = ""
 
  if     angle   < 0.001 then
 
    direction = TUC.Side.LEFT
 
  elseif angle > 0.001 then
 
    direction = TUC.Side.RIGHT
 
  else
 
    direction = TUC.Side.PARALLEL
 
  end
 
  return math.abs(angle), direction
 
end

Edited by Elphaba
Link to comment
Share on other sites

9 minutes ago, Elphaba said:

function TUC.IsTargetLeftOfUnit(unit, targetUnit)

Note that this method is only returning correct results for units that are heading 0 (straight north) since in your calculations you nowhere take into account the heading of unit iself, merely its location. Take a setup, run it, and note the result. Turn the unit by 180 degrees. The result should now be the opposite side. You will notice that it is not. That is why we went to all the trouble with cross and dot product by using the velocity vector. With ground units that don't move, you'll need to create a point in the direction it is facing (not too difficult), and then run the same cross/dot product. 

  • Thanks 1
Link to comment
Share on other sites

10 minutes ago, cfrag said:

Note that this method is only returning correct results for units that are heading 0 (straight north) since in your calculations you nowhere take into account the heading of unit iself, merely its location. Take a setup, run it, and note the result. Turn the unit by 180 degrees. The result should now be the opposite side. You will notice that it is not. That is why we went to all the trouble with cross and dot product by using the velocity vector. With ground units that don't move, you'll need to create a point in the direction it is facing (not too difficult), and then run the same cross/dot product. 

Good catch. Thanks.

Link to comment
Share on other sites

function IsTargetLeftRightOrParallelOfTrack(playerUnit, targetUnit)
 
  -- Left = Positive      Right = Negative      Parallel = Zero
 
  local pVec = playerUnit:getPosition().x
  local tVec  = targetUnit:getPosition().x
 
  local result = VecDot( { x = 1, y = 0, z = 0 }, VecCross(pVec, tVec))
 
  if     result >  0.001 then
 
    return Side.LEFT
 
  elseif result < -0.001 then
 
    return Side.RIGHT
 
  else
 
    return Side.PARALLEL
 
  end
 
end
2 minutes ago, Elphaba said:
function IsTargetLeftRightOrParallelOfTrack(playerUnit, targetUnit)
 
  -- Left = Positive      Right = Negative      Parallel = Zero
 
  local pVec = playerUnit:getPosition().x
  local tVec  = targetUnit:getPosition().x
 
  local result = VecDot( { x = 1, y = 0, z = 0 }, VecCross(pVec, tVec))
 
  if     result >  0.001 then
 
    return Side.LEFT
 
  elseif result < -0.001 then
 
    return Side.RIGHT
 
  else
 
    return Side.PARALLEL
 
  end
 
end

Is this okay?

Link to comment
Share on other sites

Just now, Elphaba said:

Good catch. Thanks.

screw it, as soon as I get home, I'll write a proper 'bogey's side of mine' method for us all dissect.

33 minutes ago, PravusJSB said:

Im not going to argue

I think this example illustrates well the pitfalls of our current AI implementations. Their answers seem absolute, unassailable. They brook no argument, even if they are wrong if some of the underlying assumptions aren't met - assumptions that are never shown. In that example, it's silently assumed that we are using a left-handed co-ordinate system (positive x, y, z are right, up and forward). That may be common, but is a dangerous assumption. The second problem is that we ourselves, when formulating a question, are also really bad and tend to leave out pertinent information that we simply assume is "common knowledge", and which the AI then simply infers. Here, @PravusJSB's question (I love the fact that this was posed to an AI chat bot btw !) omitted the all-important fact that the direction that A is facing is relevant. That is leads to the fact that the answer to the left/right question - which is the most pertinent point in this thread - is wrong. 

Now, if we have P1's own heading (say 120°), and the bearing from P1 to P2 (say 20°), a simple subtraction (modulo 360) can also lead the correct result left/right (<0 is left). But bearing calculations rely on atan2, a function I try to avoid, hence my multiplication/subtraction-only approach (I'm old-school, pre FPU-equipped CPU, can't help myself 🙂 )

If I have the time, I'll see if I can give you both approaches side-by side for comparison. 

Link to comment
Share on other sites

10 minutes ago, Elphaba said:

Is this okay?

Highly unlikely. Nowhere in that calculation I find unit's heading. All you have are the vectors from 0 to unit and 0 to target. Their cross product is the normal vector of the plane that is defined by the three points origin (0,0,0), unitLoc and targetLoc, and you procject x onto this normal vector, returning a value which I'm currently unable to assign correct meaning (then agaian, I *did* have wine for lunch).

If you give me some time to sober up and return home, I'll see what I can come up with

 


Edited by cfrag
  • Like 1
Link to comment
Share on other sites

On 2/21/2023 at 2:25 PM, Elphaba said:

is a clever and faster way

This was much easier than I thought. Scratch the cross product approach, we use bearings, since they are much easier to explain (and I am lazy):

function dcsCommon.whichSideOfMine(theUnit, target) -- returs two values: -1/1 = left/right and "left"/"right" 
	if not theUnit then return nil end 
	if not target then return nil end 
	local uDOF = theUnit:getPosition() -- returns p, x, y, z Vec3
	-- with x, y, z being the normalised vectors for right, up, forward 
	local heading = math.atan2(uDOF.x.z, uDOF.x.x) -- returns rads
	if heading < 0 then
		heading = heading + 2 * math.pi	-- put heading in range of 0 to 2*pi
	end
	-- heading now runs from 0 through 2Pi
	local A = uDOF.p
	local B = target:getPoint() 
	 
	-- now get bearing from theUnit to target  
	local dx = B.x - A.x
	local dz = B.z - A.z
	local bearing = math.atan2(dz, dx) -- in rads
	if bearing < 0 then
		bearing = bearing + 2 * math.pi	-- make bearing 0 to 2*pi
	end

	-- we now have bearing to B, and own heading. 
	-- subtract own heading from bearing to see at what 
	-- bearing target would be if we 'turned the world' so
	-- that theUnit is heading 0
	local dBearing = bearing - heading
	-- if result < 0 or > Pi (=180°), target is left from us
	if dBearing < 0 or dBearing > math.pi then return -1, "left" end
	return 1, "right"
	-- note: no separate case for straight in front or behind
end

The function returns two items: a number -1/1 (-1 for left, 1 for right), and a string "left"/"right". 

... aaaand a miz to demonstrate 

image.png

By my side.miz

  • Thanks 1
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...