• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[JASS] unit group selecting by line

Status
Not open for further replies.
Level 7
Joined
Jan 7, 2009
Messages
44
Hello everyone, is there a function (in JASS) to select units in a line instead of selecting unit groups thousand times using PolarProjection. Can anyone post it here if it even exist?

EDIT: I found it by my self (GroupEnumUnitsInLine - made by Tossrock)

JASS:
function GroupEnumUnitsInLine takes group g, real x1, real y1, real x2, real y2, real width returns nothing
    local real angle = Atan2(y2-y1,x2-x1)
    local real dist = SquareRoot( (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1) )
    local real cdist = 0
    local group temp
    loop
        exitwhen (cdist > dist)
        set temp = CreateGroup()
        call GroupEnumUnitsInRange(temp,x1,y1,width/2,null)
        set bj_wantDestroyGroup = true
        call GroupAddGroup(temp,g)
        set x1 = x1+((width/4)*Cos(angle))
        set y1 = y1+((width/4)*Sin(angle))
        set cdist = cdist + (width/4)
    endloop
    set temp = null
endfunction
 
Last edited:

peq

peq

Level 6
Joined
May 13, 2007
Messages
171
I made a library which does this some time ago. It uses the same method as Vexorian in his Disintegrate-Spell, which is more precise than the function you posted and probably it also is faster.

JASS:
library groupaddunitsnearlines

globals
    //upper bound for the collision size of units
    private constant real MAX_COLLISSION = 128.

    
    //---
    private rect temprect = Rect(0,0,0,0)
    private group tempenumgroup = CreateGroup()
    private group tempgroup
    private real temppx
    private real temppy
    private real tempdnx
    private real tempdny
    private real tempwidth
    private real tempd
endglobals



private function Add takes nothing returns boolean
    //position of unit
    local real x = GetUnitX(GetFilterUnit())
    local real y = GetUnitY(GetFilterUnit())
    //vector from the beginning of the line to the unit
    local real dx = x - temppx
    local real dy = y - temppy
    //distance from the beginning of the line to that point on the line which is closest to the unit (uses dot-product)
    local real d = dx * tempdnx + dy * tempdny
    local real cx
    local real cy
    //cut off points outside the line
    if d <= 0 then
        set d = 0
    elseif d >= tempd then
        set d = tempd
    endif
    //the closest point to the unit on the line 
    set cx = temppx + d * tempdnx
    set cy = temppy + d * tempdny 

    if IsUnitInRangeXY(GetFilterUnit(), cx, cy, tempwidth) then
        call GroupAddUnit(tempgroup, GetFilterUnit())
    endif
    return false
endfunction



function GroupAddUnitsNearLine takes group g, real px, real py, real qx, real qy, real width returns nothing
    //d = from p to q
    local real dx = qx - px
    local real dy = qy - py
    local real dlen = SquareRoot(dx*dx + dy*dy)
    //normalized d
    local real dxn = dx / dlen
    local real dyn = dy / dlen
    //bonus so that the whole line is covered
    local real bonus = width + MAX_COLLISSION
    
    //move rect to right position
    if dx > 0 then
        if dy > 0 then
            call SetRect(temprect, px - bonus, py - bonus, qx + bonus, qy + bonus)
        else
            call SetRect(temprect, px - bonus, qy - bonus, qx + bonus, py + bonus)
        endif
    else
        if dy > 0 then
            call SetRect(temprect, qx - bonus, py - bonus, px + bonus, qy + bonus)
        else
            call SetRect(temprect, qx - bonus, qy - bonus, px + bonus, py + bonus)
        endif
    endif
    
    //set temp vars for groupenum
    set tempgroup = g
    set temppx = px
    set temppy = py
    set tempdnx = dxn
    set tempdny = dyn
    set tempwidth = width 
    set tempd = dlen
    
    //enum units in rect
    call GroupEnumUnitsInRect(tempenumgroup, temprect, Condition(function Add))

endfunction

endlibrary
 
Level 21
Joined
Aug 21, 2005
Messages
3,699
Wouldn't it be faster to enumerate all units on the map, and then make a check for each unit if it's in the rectangle?

checking if it's in the rectangle is done pretty easily. The rectangle can be seen as a rectangle perpendicular to the X/Y axis that's rotated and translated. So you just use the inverse translation and rotation on the position of the unit. If that new point is close enough to the X and Y axis, it's in the rectangle.
 

peq

peq

Level 6
Joined
May 13, 2007
Messages
171
He does not say that he rotates the rect. He means having something like a second coordinate system in which the rect is not rotated. The problem with this is that you had to translate the position of all the units to the second coordinate system. Although this can be done with just a few multiplications and additions it would not be faster as it would do the calculations for all the units on the map instead of taking just a small subset.
 
Level 21
Joined
Aug 21, 2005
Messages
3,699
Well, here's the big difference:

You do 4 comparisons first on all units on the map. Then you do, say, 15 calculations for each unit that's still remaining as well as some additional comparisons.

I do 4 calculations on all units on the map, as well as 4 comparisons.

Might be about equally fast in the average case.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,258
Most efficent way is you reduce your group scope size to the bare minimum. For this you simply pick all units in radius of the centre of the rectangle with the smallest possiable radius which fits the entire rectangle in.
You then check if they are in the rectangle.
 
Status
Not open for further replies.
Top