07102008, 11:13 PM  #1  
I blink, therefore I am.
Join Date: Sep 2006
Posts: 1,812

LineSegments
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 inline 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 = BxAx local real dy = ByAy 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 = ((CxAx)*(dx) + (CyAy)*(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 = BxAx local real dy = ByAy 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((CxAx)*(CxAx)+(CyAy)*(CyAy)) endif set r = ((CxAx)*(dx) + (CyAy)*(dy))/(L) // get the ratio if r > 1 then // closests point is past seg, so return distance to point B return SquareRoot((CxBx)*(CxBx)+(CyBy)*(CyBy)) elseif r < 0 then // same as B, but at A instead return SquareRoot((CxAx)*(CxAx)+(CyAy)*(CyAy)) 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((CxAx)*(CxAx)+(CyAy)*(CyAy)) 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 = BxAx local real dy = ByAy local real L = ((dx)*(dx) + (dy)*(dy)) // Get quasi length local real r = SquareRoot(dx*dx+dy*dy)/2+distance + xebasic_wrapper() // doublepurpose 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 = BxAx local real dy = ByAy 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 linespells 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.
Last edited by Ammorth : 02132010 at 11:15 PM. 

Sponsored Links  Login to hide this ad! 

07112008, 12:48 AM  #2 
master of fugue
Join Date: Jun 2007
Posts: 2,453

I never made any line spells just because I knew code would look like this...
__________________ 
07112008, 01:50 AM  #3  
Free Software Terrorist
Technical Director

Use a static group for the enum, then it would get approved.
__________________Quote:


07112008, 03:37 AM  #4  
I blink, therefore I am.
Join Date: Sep 2006
Posts: 1,812

Quote:
Done, and still kept the easy nonvJass 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). 

07112008, 03:58 AM  #5 
Free Software Terrorist
Technical Director

The scripts section is getting fat fast, I think I will have to implement the tag stuff...
__________________ 
07112008, 05:37 PM  #6 
Alpha Male of Wc3c
Official Map Reviewer

+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. Last edited by darkwulfv : 07112008 at 05:42 PM. 
07112008, 06:40 PM  #7 
I blink, therefore I am.
Join Date: Sep 2006
Posts: 1,812

it would actually curve on the outside of the line:
purple/blue is line seg, red is the enum area and yellow is the test area. If you don't want to have the curves, you can add a check to see if the closest point is an endpoint. If it is, then it's part of the curve. JASS: function GroupEnumUnitsInRangeOfSegNoCurve takes group whichgroup, real Ax, real Ay, real Bx, real By, real distance, boolexpr filter returns nothing local real dx = BxAx local real dy = ByAy local real r = SquareRoot(dx*dx+dy*dy)/2+distance local unit u local location l = Location(0., 0.) call GroupEnumUnitsInRange(udg_LineTempGroup, Ax+(dx/2), Ay+(dy/2), r, filter) loop set u = FirstOfGroup(udg_LineTempGroup) exitwhen u == null set l = GetNearestPointOnSeg(Ax, Ay, Bx, By, GetUnitX(u), GetUnitY(u)) set dx = GetLocationX(l) set dy = GetLocationY(l) set r = SquareRoot(dx  GetUnitX(u)) * (dx  GetUnitX(u)) + (dy  GetUnitY(u)) * (dy  GetUnitY(u))) if (r <= distance) and (dx != Ax and dy != Ay) and (dx != Bx and dy != By) then // check for end points so it doesn't curve call GroupAddUnit(whichgroup, u) endif call GroupRemoveUnit(udg_LineTempGroup, u) endloop set u = null endfunction If you only want one side or the other, just remove the check for that side. Last edited by Ammorth : 07112008 at 06:57 PM. 
07112008, 08:34 PM  #8 
Alpha Male of Wc3c
Official Map Reviewer

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. 
07302008, 10:51 PM  #9 
Procrastination Incarnate
Development Director

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. 
07302008, 11:41 PM  #10 
master of fugue
Join Date: Jun 2007
Posts: 2,453

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)) Last edited by cohadar : 07302008 at 11:44 PM. 
07302008, 11:58 PM  #11  
Procrastination Incarnate
Development Director

Quote:


07312008, 02:28 AM  #12 
User

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.

07312008, 03:58 AM  #13 
User
Respected User

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. Last edited by BlinkBoy : 07312008 at 04:00 AM. 
07312008, 04:14 AM  #14 
Alpha Male of Wc3c
Official Map Reviewer

GroupEnumUnitsInQuad, by Grim001. It's over @ Wc3jass.
__________________ 
07312008, 06:46 AM  #15  
I blink, therefore I am.
Join Date: Sep 2006
Posts: 1,812

Quote:
Quote:
Quote:
Quote:
Quote:
Quote:


Thread Tools  Search this Thread 

