TEMPEST.114 Posted February 21, 2023 Share Posted February 21, 2023 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 More sharing options...
cfrag Posted February 21, 2023 Share Posted February 21, 2023 (edited) 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 February 21, 2023 by cfrag 1 Link to comment Share on other sites More sharing options...
TEMPEST.114 Posted February 21, 2023 Author Share Posted February 21, 2023 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 More sharing options...
TEMPEST.114 Posted February 23, 2023 Author Share Posted February 23, 2023 Vector Cross Product and then Dot Product will do this too. Link to comment Share on other sites More sharing options...
cfrag Posted February 23, 2023 Share Posted February 23, 2023 (edited) 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. Apologies for not going into details before, you seemed a bit uncomfortable with vector math so I thought I'd spare you Edited February 23, 2023 by cfrag 1 Link to comment Share on other sites More sharing options...
TEMPEST.114 Posted February 23, 2023 Author Share Posted February 23, 2023 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. Apologies for not going into details before, you seemed a bit uncomfortable with vector math so I thought I'd spare you 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. Apologies for not going into details before, you seemed a bit uncomfortable with vector math so I thought I'd spare you 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 More sharing options...
cfrag Posted February 23, 2023 Share Posted February 23, 2023 2 minutes ago, Elphaba said: collinear They are all on the same line. Advantages of my German upbringing. It means exactly that in German 1 Link to comment Share on other sites More sharing options...
PravusJSB Posted February 23, 2023 Share Posted February 23, 2023 (edited) easy... Edited February 23, 2023 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 More sharing options...
cfrag Posted February 23, 2023 Share Posted February 23, 2023 (edited) 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 February 23, 2023 by cfrag Link to comment Share on other sites More sharing options...
PravusJSB Posted February 23, 2023 Share Posted February 23, 2023 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 More sharing options...
TEMPEST.114 Posted February 23, 2023 Author Share Posted February 23, 2023 (edited) 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 February 23, 2023 by Elphaba Link to comment Share on other sites More sharing options...
cfrag Posted February 23, 2023 Share Posted February 23, 2023 Just now, Elphaba said: Wuss. Plus incorrect wuss; AI isn't the future of Humanity No, sorry to correct you - @PravusJSB was unwilling to argue with me. 1 Link to comment Share on other sites More sharing options...
cfrag Posted February 23, 2023 Share Posted February 23, 2023 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. 1 Link to comment Share on other sites More sharing options...
TEMPEST.114 Posted February 23, 2023 Author Share Posted February 23, 2023 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 More sharing options...
TEMPEST.114 Posted February 23, 2023 Author Share Posted February 23, 2023 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 More sharing options...
cfrag Posted February 23, 2023 Share Posted February 23, 2023 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 More sharing options...
cfrag Posted February 23, 2023 Share Posted February 23, 2023 (edited) 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 February 23, 2023 by cfrag 1 Link to comment Share on other sites More sharing options...
cfrag Posted February 23, 2023 Share Posted February 23, 2023 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 By my side.miz 1 Link to comment Share on other sites More sharing options...
HC_Official Posted February 23, 2023 Share Posted February 23, 2023 wow its like some math nerd show down, 10/10 would defo read again , nice work folks No more pre-orders Click here for tutorials for using Virpil Hardware and Software Click here for Virpil Flight equipment dimensions and pictures. . Link to comment Share on other sites More sharing options...
Recommended Posts