| 02-03-2010, 08:13 PM | #1 |
Requires: GroupUtils GetNearest is a very simple library that allows you to find the nearest unit, destructable, or item to a specified point within a specified radius. You can use a boolexpr filter as well, just like native Enum functions. I made this because the concept behind FindFuncs seemed useful. You can still find the nearest tree by using IsDestructableTree within a boolexpr. GetNearestUnit can easily be recreated using PruneGroup, along with more generalized functionality. SortUtils should be used when actual sorting of units by values is required. This library is just meant to be small and easy to use. JASS:library GetNearest initializer Init requires GroupUtils //=========================================================================== // Information: //============== // // GetNearest is a very simple library that allows you to find the nearest // unit, destructable, or item to a specified point within a specified radius. // You can use a boolexpr filter as well, just like native Enum functions. All // units/destructables/items outside of the playable map area will be ignored. // //=========================================================================== // GetNearest API: //================= // // GetNearestUnit(x, y, radius, boolexpr) -> unit // This function returns the closest unit to the specified x/y point within // the specified radius matching the specified boolexpr filter. This function // will consider any unit whose collision radius overlaps the specified radius. // // GetNearestDestructable(x, y, radius, boolexpr) -> destructable // This function returns the closest destructable to the specified x/y point // within the specified radius matching the specified boolexpr filter. // // GetNearestItem(x, y, radius, boolexpr) -> item // This function returns the closest item to the specified x/y point within // the specified radius matching the specified boolexpr filter. // //=========================================================================== globals private real MAX_VALUE = 10000000000. //Larger than any squared distance value. private region MapArea private real MaxX private real MinX private real MaxY private real MinY endglobals //=========================================================================== private function GetNearestUnit_Sub takes unit nearest, real x, real y, real radius, boolexpr b returns unit local group g = NewGroup() local real nearestdist = MAX_VALUE local real dist local unit u call GroupEnumUnitsInArea(g, x, y, radius, b) //This function takes collision radius into account. loop //Iterate through the units in the group and find the closest to the specified point. set u = FirstOfGroup(g) exitwhen u == null set dist = Pow(x - GetUnitX(u), 2.) + Pow(y - GetUnitY(u), 2.) if IsUnitInRegion(MapArea, u) and dist < nearestdist then set nearestdist = dist set nearest = u endif call GroupRemoveUnit(g, u) endloop call ReleaseGroup(g) return nearest //Local handle parameters are automatically nulled. endfunction function GetNearestUnit takes real x, real y, real radius, boolexpr b returns unit return GetNearestUnit_Sub(null, x, y, radius, b) endfunction //Avoids leaking a reference to the returned unit. //=========================================================================== globals private rect enumrect = Rect(0., 0., 0., 0.) private real nearestdist = MAX_VALUE private real enumradius = 0. private real enumx = 0. private real enumy = 0. endglobals //! textmacro GetNearestFunc takes NAME, TYPE globals private $TYPE$ nearest$TYPE$ = null endglobals private function GetNearest$NAME$_Enum takes nothing returns nothing local real dist = Pow(enumx - Get$NAME$X(GetEnum$NAME$()), 2.) + Pow(enumy - Get$NAME$Y(GetEnum$NAME$()), 2.) if dist <= enumradius and dist < nearestdist then set nearest$TYPE$ = GetEnum$NAME$() set nearestdist = dist endif endfunction //Enumerate through the dests/items and find the closest one to the point. private function GetNearest$NAME$_Sub takes $TYPE$ nearest, real x, real y, real radius, boolexpr b returns $TYPE$ local real old_enumradius = enumradius //Cache the previous values of the globals. local real old_enumx = enumx local real old_enumy = enumy set enumradius = radius*radius //Assign new values to the globals for this call. set enumx = x set enumy = y call SetRect(enumrect, RMaxBJ(x - radius, MinX), RMaxBJ(y - radius, MinY), RMinBJ(x + radius, MaxX), RMinBJ(y + radius, MaxY)) //Adjust the rect to cover the radius of the enueration. Make sure the rect stays within map boundries. call Enum$NAME$sInRect(enumrect, b, function GetNearest$NAME$_Enum) //Find the closest dest/item to the point. set nearest = nearest$TYPE$ //Assign the closest dest/item to a local variable. set nearest$TYPE$ = null //Restore the previous values for the globals for nested enumerations. set nearestdist = MAX_VALUE set enumradius = old_enumradius set enumx = old_enumx set enumy = old_enumy return nearest //Local handle parameters are automatically nulled. endfunction function GetNearest$NAME$ takes real x, real y, real radius, boolexpr b returns $TYPE$ return GetNearest$NAME$_Sub(null, x, y, radius, b) endfunction //Avoids leaking a reference to the returned dest/item. //! endtextmacro //! runtextmacro GetNearestFunc("Destructable", "destructable") //! runtextmacro GetNearestFunc("Item", "item") //=========================================================================== private function Init takes nothing returns nothing set MapArea = CreateRegion() //Creating a region inside a global block fails, so do it here. set MaxX = GetRectMaxX(bj_mapInitialPlayableArea) //Store the boundries in globals to ensure that dests and items set MinX = GetRectMinX(bj_mapInitialPlayableArea) //are within map boundries. set MaxY = GetRectMaxY(bj_mapInitialPlayableArea) set MinY = GetRectMinY(bj_mapInitialPlayableArea) call RegionAddRect(MapArea, bj_mapInitialPlayableArea) //Add the map rect to a region for IsUnitInRegion(). endfunction endlibrary |
| 02-04-2010, 03:00 PM | #2 |
Isn't the Pow() function classically slower than just multiplying the numbers together? I seem to recall something like that. Anyways, this fixes most of the problems I had with Michael Peppers submission (lack of textmacros, etc). I can kind of see it being niche useful, but I'll let it sit here to get some more feedback before doing anything with it. |
| 02-04-2010, 03:07 PM | #3 |
Yes, Pow is slower then a*a. |
| 02-04-2010, 03:53 PM | #4 |
When you resize the rect, shouldn't it account for the map boundaries? While this is never an issue for units and items, it is not uncommon for destructables to be placed beyond the playable map area. |
| 02-04-2010, 04:00 PM | #5 | ||
Quote:
Depends on a lot of factors, like whether you would have to declare extra locals, perform extra set operations or make extra native calls without using Pow. I guess I'll benchmark it because I'm curious. Quote:
As far as practical usage goes, I guess this is important. I'll make all the functions filter out stuff outside of map boundries. |
| 02-05-2010, 03:38 AM | #6 |
I did a benchmark comparing GetNearestDestructable using Pow versus multiplication in the enum. There were 100 trees within range of the enumeration, and the test was repeated 100,000 times. The benchmark showed that the multiplication version was 0.0697% slower, which is such a small difference that it amounts to noise. In this case I'm going to go with the one that looks better. I also fixed a couple typos, optimized the code a bit, and made sure that none of the functions will return stuff outside the playable map area. |
| 02-05-2010, 02:09 PM | #7 |
Well, in this case, using multiplication requires you to declare additional locals or do the x - GetUnitX(u) calculation twice, so using Pow likely is faster. Otherwise, I believe I've seen other people post test results where Pow(x,2) was slightly slower than x*x while Pow(x,3) was slightly faster than its multiplication counterpart. Either way, as your test shows, the difference is inconsequential in the context of this function. |
| 02-05-2010, 04:00 PM | #8 |
*Shrug* Don't need a benchmark and two posts to tell me so, it was just something that came to mind as a possible improvement. I can think of nothing else this really needs, so if it works as listed and no other reviewer can think of anything, we can approve it. |
| 02-21-2010, 06:27 PM | #9 |
Going to go ahead and approve this, then. |
