| 07-10-2008, 11:13 PM | #1 | |
Line Segments JASS:// ================================================================================== // LineSegment - by Ammorth, Feb 13, 2010 - rev5 // // This script, and myself, can be found at wc3c.net // // Used to determine certain things about segments, involving units and geometry. // // Requirements: // - vJass // - optional: xebasic - by Vexorian ( [url]http://www.wc3c.net/showthread.php?t=101150[/url] ) // will use XE_MAX_COLLISION_SIZE if xebasic is present, otherwise it will ignore it. // // Installation: // - Create a new trigger called LineSegments and convert it to custom text // - Copy the code to the new trigger, replacing everything // // How To Use: // - call GetNearestPointOnSegment(Ax, Ay, Bx, By, Cx, Cy) to get the nearest point on // the line segment AB to point C (returns a location) // - call GetDistanceFromSegment(Ax, Ay, Bx, By, Cx, Cy) to get the distance from the // line segment AB to a given point C // - call GroupEnumUnitsInRangeOfSegment(whichgroup, Ax, Ay, Bx, By, distance, filter) to // add all of the units within distance of segment AB to whichgroup, according to the filter. // - call IsUnitInRangeOfSegment(unit, Ax, Ay, Bx, By, distance) to see if the unit givin is // within distance of segment AB // - xebasic is no longer required, but if avaliable, will return correct results with // GroupEnumUnitsInRangeOfSegment() using XE_MAX_COLLISION_SIZE. // // Notes: // - All functions have a location version wrappers incase you would rather pass locations // They are named as follows: // > GetNearestPointOnSegmentLoc() // > GetDistanceFromSegmentLoc() // > GroupEnumUnitsInRangeOfSegmentLoc() // > IsUnitInRangeOfSegmentLoc() // // ================================================================================== library LineSegment requires optional xebasic globals private group udg_LineTempGroup = CreateGroup() endglobals // with optionals we can add/improve features if other libraries are present // this should in-line with an optimizer, since the static if makes it constant private constant function xebasic_wrapper takes nothing returns real static if LIBRARY_xebasic then return XE_MAX_COLLISION_SIZE else return 0.0 endif endfunction function GetNearestPointOnSegment takes real Ax, real Ay, real Bx, real By, real Cx, real Cy returns location local real r // could use a point struct here if you really wanted, instead of a location. local real dx = Bx-Ax local real dy = By-Ay local real L = ((dx)*(dx) + (dy)*(dy)) // Get quasi length if L == 0 then // seg is actually a point so lets return the point return Location(Ax, Ay) endif set r = ((Cx-Ax)*(dx) + (Cy-Ay)*(dy))/(L) // get the ratio if r > 1 then // closests point is past seg, so return end point B return Location(Bx, By) elseif r < 0 then // same as B, but at A instead return Location(Ax, Ay) endif // In the middle of A and B so use the ratio to find the point return Location(Ax+r*(dx), Ay+r*(dy)) endfunction function GetNearestPointOnSegmentLoc takes location A, location B, location C returns location return GetNearestPointOnSegment(GetLocationX(A), GetLocationY(A), GetLocationX(B), GetLocationY(B), GetLocationX(C), GetLocationY(C)) endfunction function GetDistanceFromSegment takes real Ax, real Ay, real Bx, real By, real Cx, real Cy returns real local real r local real dx = Bx-Ax local real dy = By-Ay local real L = ((dx)*(dx) + (dy)*(dy)) // Get quasi length if L == 0 then // seg is actually a point so lets return the distance to the point return SquareRoot((Cx-Ax)*(Cx-Ax)+(Cy-Ay)*(Cy-Ay)) endif set r = ((Cx-Ax)*(dx) + (Cy-Ay)*(dy))/(L) // get the ratio if r > 1 then // closests point is past seg, so return distance to point B return SquareRoot((Cx-Bx)*(Cx-Bx)+(Cy-By)*(Cy-By)) elseif r < 0 then // same as B, but at A instead return SquareRoot((Cx-Ax)*(Cx-Ax)+(Cy-Ay)*(Cy-Ay)) endif // In the middle of A and B so use the ratio to find the point set Ax = Ax+r*(dx) set Ay = Ay+r*(dy) return SquareRoot((Cx-Ax)*(Cx-Ax)+(Cy-Ay)*(Cy-Ay)) endfunction function GetDistanceFromSegmentLoc takes location A, location B, location C returns real return GetDistanceFromSegment(GetLocationX(A), GetLocationY(A), GetLocationX(B), GetLocationY(B), GetLocationX(C), GetLocationY(C)) endfunction function GroupEnumUnitsInRangeOfSegment takes group whichgroup, real Ax, real Ay, real Bx, real By, real distance, boolexpr filter returns nothing local real dx = Bx-Ax local real dy = By-Ay local real L = ((dx)*(dx) + (dy)*(dy)) // Get quasi length local real r = SquareRoot(dx*dx+dy*dy)/2+distance + xebasic_wrapper() // double-purpose for r local unit u call GroupClear(udg_LineTempGroup) call GroupEnumUnitsInRange(udg_LineTempGroup, Ax+(dx/2), Ay+(dy/2), r, filter) loop set u = FirstOfGroup(udg_LineTempGroup) exitwhen u == null if L == 0 and IsUnitInRangeXY(u, Ax, Ay, distance) then // seg is actually a point so lets return the point call GroupAddUnit(whichgroup, u) else set r = ((GetUnitX(u)-Ax)*(dx) + (GetUnitY(u)-Ay)*(dy))/(L) // get the ratio if r > 1 then // split if/thens so that it exists properly if IsUnitInRangeXY(u, Bx, By, distance) then // closests point is past seg, so return end point B call GroupAddUnit(whichgroup, u) endif elseif r < 0 then if IsUnitInRangeXY(u, Ax, Ay, distance) then // same as B, but at A instead call GroupAddUnit(whichgroup, u) endif elseif IsUnitInRangeXY(u, Ax+r*(dx), Ay+r*(dy), distance) then // In the middle of A and B so use the ratio to find the point call GroupAddUnit(whichgroup, u) endif endif call GroupRemoveUnit(udg_LineTempGroup, u) endloop set u = null endfunction function GroupEnumUnitsInRangeOfSegmentLoc takes group whichgroup, location A, location B, real distance, boolexpr filter returns nothing call GroupEnumUnitsInRangeOfSegment(whichgroup, GetLocationX(A), GetLocationY(A), GetLocationX(B), GetLocationY(B), distance, filter) endfunction function IsUnitInRangeOfSegment takes unit u, real Ax, real Ay, real Bx, real By, real distance returns boolean local real r local real dx = Bx-Ax local real dy = By-Ay local real L = ((dx)*(dx) + (dy)*(dy)) // Get quasi length if L == 0 then // seg is actually a point so lets return the point return IsUnitInRangeXY(u, Ax, Ay, distance) endif set r = ((GetUnitX(u)-Ax)*(dx) + (GetUnitY(u)-Ay)*(dy))/(L) // get the ratio if r > 1 then // closests point is past seg, so return end point B return IsUnitInRangeXY(u, Bx, By, distance) elseif r < 0 then // same as B, but at A instead return IsUnitInRangeXY(u, Ax, Ay, distance) endif // In the middle of A and B so use the ratio to find the point return IsUnitInRangeXY(u, Ax+r*(dx), Ay+r*(dy), distance) endfunction function IsUnitInRangeOfSegmentLoc takes unit u, location A, location B, real distance returns boolean return IsUnitInRangeOfSegment(u, GetLocationX(A), GetLocationY(A), GetLocationX(B), GetLocationY(B), distance) endfunction endlibrary If xebasic by Vexorian is present, it will use XE_MAX_COLLISION_SIZE to properly Enum units in range. Those that don't care or will not use this for units do not need to include xebasic anymore. It is useful for line-spells as it's more accurate than the multiple GroupEnumUnitsInRange() method. Might also be faster in certain situations. Can also be used in geometric systems. For lines, instead of segments, remove the ratio checks (r < 0 ; r > 1) and just return the final return.
|
| 07-11-2008, 12:48 AM | #2 |
I never made any line spells just because I knew code would look like this... |
| 07-11-2008, 01:50 AM | #3 | |
Use a static group for the enum, then it would get approved. Quote:
|
| 07-11-2008, 03:37 AM | #4 | ||
Quote:
Done, and still kept the easy non-vJass conversion. Quote:
Really? I thought you said awhile ago that OOP is the key and that incorporating a physics system into a spell is ridiculous. Either way, this can be used outside spells as well (like in my PAS system). |
| 07-11-2008, 03:58 AM | #5 |
The scripts section is getting fat fast, I think I will have to implement the tag stuff... |
| 07-11-2008, 05:37 PM | #6 |
+rep because I actually need this (I can't believe it). Can I just ask? (It sounds like a dumb question, but...) So if I use GroupEnumUnitsInRangeOfSeg, that will group the units along the line segment? (Within the range) So, if it were a visual, the area that would get considered for grouping would be something like =================== = =----------------------- = =================== Where "====" is the area units get grouped in, and "----" is the line segment. |
| 07-11-2008, 08:34 PM | #8 |
Way cool, thanks for that. I'll update my code. Btw, I think you should add a note about those curves, for anyone who might not know. |
| 07-30-2008, 10:51 PM | #9 |
Ok, first of all, the function GetDistanceFromSeg leaks a location. This is bad, so I'm temporarily moving this script back into submissions until this is corrected. I don't think you should call GetNearestPointOnSeg at all from the GetDistanceFromSeg function, but inline the code instead, it not only solves the leak problem but saves you the overhead of creating and destroying locations which is a big time saver when doing stuff like GroupEnumUnitsInRangeOfSeg. In the GetNearestPointOnSeg function, the check for L being negative is unneeded as L can't be negative the way it is calculated. Also, shouldn't the last return statement be return Location(Ax+r*(dx), Ay+r*(dy)) instead of return Location(r*(dx), r*(dy))? A useful function that could be included in this pack would be GetNearestPointOnLine, which would be similar to GetNearestPointOnSeg, except it wouldn't look for the nearest point just on the segment between A and B but on the whole line defined by points A and B. Same goes for GetDistanceFromLine, would be a useful thing to have. |
| 07-30-2008, 11:41 PM | #10 |
You don't need temp group, you can simply remove units from starting group that are outside of segment. (or even better make your function an EnumFilter that returns false if unit is out of segment) call GroupEnumUnitsInRange(whichgroup, Ax+(dx/2), Ay+(dy/2), r, And(userFilter, segmentFilter)) |
| 07-30-2008, 11:58 PM | #11 | |
Quote:
|
| 07-31-2008, 02:28 AM | #12 |
I found that when a unit is standing right next to a segment, it often returns a value from 10,000 to 11,000 instead of how far the unit actually is from the segment. |
| 07-31-2008, 03:58 AM | #13 |
can't you create a rect that covers the sgement, get the units from it, then get the units from both extremes using a range grouping, and then join the 3 groups? Nvm, that would work only for vertical and horizontal segments. but now that I think it, you could check if a unit is inside the Quad, there's a function somewhere around for checking that. |
| 07-31-2008, 04:14 AM | #14 |
GroupEnumUnitsInQuad, by Grim001. It's over @ Wc3jass. |
| 07-31-2008, 06:46 AM | #15 | ||||||
Quote:
Quote:
Quote:
Quote:
Quote:
Quote:
|
