Thanks
@DyaTactic for putting that together. It works really well.
In the meantime I came up with an alternative solution, although it is much more complex. I got started down this track and couldn't stop until I got it to work. It is probably not useful for many people but maybe there are some nuggets in here that someone could find worthwhile. It first takes all screen edges and collapses them to a single minimum edge polygon
https://stackoverflow.com/questions/13746284/merging-multiple-adjacent-rectangles-into-one-polygon Then it uses a ray casting check to determine if a point is inside the border polygon
https://stackoverflow.com/questions/11716268/point-in-polygon-algorithm
The only benefit to this method is that as the number of screens increases there is an increase in time savings. Not that it was taking any time at all to compute this in the first place. Still it was fun to learn about this stuff.
Code: Select all
#singleinstance Force
#persistent
setBatchLines -1
coordMode,Mouse,screen
polyBorder := new boxExplodeObj(type := "SCREEN")
xoffset := polyBorder.minX
yoffset := polyBorder.minY
setTimer showMouse,20
return
showMouse:
mouseGetPos,x,y
mx := x - xoffset
my := y - yoffset
winGetPos,wx,wy,,,A
tooltip % "corrected mouse coords : " mx . "," . my . "`nraw window coords : " . wx . "," . wy . "`ncorrected window coords : " . wx - xoffset . "," . wy - yoffset . "`npoint on screen : " . polyBorder.pointInPoly(new ptObj(wx,wy)) . "`nwindow on screen : " . polyBorder.windowInPoly("A")
return
class ptObj
{
__new(x,y)
{
this.x := x
this.y := y
}
}
class boxExplodeObj
{
__new(type := "")
{
this.ptArr := {}
this._maxX := ""
this._maxY := ""
this._minX := ""
this._minY := ""
this._border := []
if(type == "SCREEN") ;if type is empty then source points will need to be manually added by user
{
SysGet, numMons, MonitorCount
loop % numMons
{
SysGet, mon, Monitor, % A_index
ul := new ptObj(monLeft,monTop)
ur := new ptObj(monRight,monTop)
lr := new ptObj(monRight,monBottom)
ll := new ptObj(monLeft,monBottom)
;add corner points to scatter buffer
this.add(ul)
this.add(ur)
this.add(lr)
this.add(ll)
}
}
}
maxX[]
{
get
{
if(!this._maxX)
{
init := this.border
}
return this._maxX
}
}
maxY[]
{
get
{
if(!this._maxY)
{
init := this.border
}
return this._maxY
}
}
minX[]
{
get
{
if(!this._minX)
{
init := this.border
}
return this._minX
}
}
minY[]
{
get
{
if(!this._minY)
{
init := this.border
}
return this._minY
}
}
border[]
{
get
{
if(!this._border._maxIndex())
{
this._border := this.collapse()
;set extents
minX := 10000000000000
minY := 10000000000000
maxX := -10000000000000
maxY := -10000000000000
for index,pt in this._border
{
if(pt.x<minX)
{
minX := pt.x
}
if(pt.x>maxX)
{
maxX := pt.x
}
if(pt.y > maxY)
{
maxY := pt.y
}
if(pt.y < minY)
{
minY := pt.y
}
}
this._minX := minX
this._maxX := maxX
this._minY := minY
this._maxY := maxY
}
return this._border
}
}
pointInPoly(pt) ;raycast point check : https://stackoverflow.com/questions/11716268/point-in-polygon-algorithm
{
inPoly := false
b := this.border
loop % b._maxIndex() - 1
{
p1 := b[A_index]
p2 := b[A_index +1]
if((p1.y <= pt.y) && (p2.y > pt.y) || (p2.y <= pt.y) && (p1.y > pt.y))
{
cross := (p2.x - p1.x)*(pt.y - p1.y)/(p2.y - p1.y) + p1.x
if(cross < pt.x)
{
inPoly := !inPoly
}
}
}
return inPoly
}
windowInPoly(winName)
{
WinGet, mm, MinMax , %winName%
if(mm)
{
return true
}
winGetPos,x,y,w,h,%winName%
r := 0
r += this.pointInPoly(new ptObj(x,y),this.border)
r += this.pointInPoly(new ptObj(x,y+h),this.border)
r += this.pointInPoly(new ptObj(x+w,y+h),this.border)
r += this.pointInPoly(new ptObj(x+w,y),this.border)
if(r == 4)
{
return true
}
return false
}
add(pt)
{
ptKey := pt.x . "," . pt.y
if(this.ptArr.hasKey(ptKey))
{
this.ptArr.Delete(ptKey)
}
else
{
this.ptArr[ptKey] := pt
}
}
toBasicArray(arr) ;converts associative array to basic
{
result := []
for ptKey,pt in arr
{
result.push(pt)
}
return result
}
sortXtY() ;sorts points by x then by y
{
tempArr := this.toBasicArray(this.ptArr)
sorted :=[]
loop % this.ptArr.count()
{
minVal := 10000000000000
minIndex := 0
for index,pt in tempArr
{
if(pt.x<minVal)
{
minPt := pt
minIndex := index
minVal := pt.x
}
}
offset := 0
loop
{
checkPt := sorted[sorted._maxIndex()-offset]
if(checkPt.x == minPt.x)
{
if(minPt.y>checkPt.y) ;valid position so add
{
sorted.insertAt(sorted._maxIndex()-offset+1,minPt)
break
}
}
else
{
if(offset) ;offset involved where change in x found but min on y
{
sorted.insertAt(sorted._maxIndex()-offset+1,minPt)
}
else ;new x value on end of stack
{
sorted.push(minPt)
}
break
}
offset++
}
tempArr.RemoveAt(minIndex)
}
return sorted
}
sortYtX() ;sorts points by y then by x
{
tempArr := this.toBasicArray(this.ptArr)
sorted :=[]
loop % this.ptArr.count()
{
minVal := 10000000000000
minIndex := 0
for index,pt in tempArr
{
if(pt.y<minVal)
{
minPt := pt
minIndex := index
minVal := pt.y
}
}
offset := 0
loop
{
checkPt := sorted[sorted._maxIndex()-offset]
if(checkPt.y == minPt.y)
{
if(minPt.x>checkPt.x) ;valid position so add
{
sorted.insertAt(sorted._maxIndex()-offset+1,minPt)
break
}
}
else
{
if(offset) ;offset involved where change in x found but min on y
{
sorted.insertAt(sorted._maxIndex()-offset+1,minPt)
}
else ;new x value on end of stack
{
sorted.push(minPt)
}
break
}
offset++
}
tempArr.RemoveAt(minIndex)
}
return sorted
}
collapse() ;https://stackoverflow.com/questions/13746284/merging-multiple-adjacent-rectangles-into-one-polygon
{
;breaks multiple touching rectangles into a minimum edge simplified polygon
;algorithm on website will detect nested polygons. we only need exterior poly so it can be simplified
if(!this.ptArr.count())
{
msgbox ,,Error,Need to add border points to object before operating on it
reload
}
sortX := this.sortXtY()
sortY := this.sortYtX()
edgesH := {}
i := 1
while(i < this.ptArr.count()) ;link horizontal segments
{
currY := sortY[i].y
while(i < this.ptArr.count() && sortY[i].y == currY)
{
edgesH[sortY[i]] := sortY[i+1]
edgesH[sortY[i+1]] := sortY[i]
i += 2
}
}
edgesV := {}
i := 1
while(i < this.ptArr.count()) ;link vertical segments
{
currX := sortY[i].x
while(i < this.ptArr.count() && sortY[i].x == currX)
{
edgesV[sortX[i]] := sortX[i+1]
edgesV[sortX[i+1]] := sortX[i]
i += 2
}
}
while(edgesH.count()>1)
{
for startPt ,matchPt in edgesH
{
break
}
poly := []
poly.push(startPt)
poly.push(0)
edgesH.Delete(startPt)
loop
{
lastPt := poly[poly._maxIndex()-1]
flag := poly[poly._maxIndex()]
if(flag)
{
nextPt := edgesH[lastPt]
edgesH.Delete(lastPt)
poly.pop() ;remove last flag
poly.push(nextPt)
poly.push(0) ;next flag
}
else
{
nextPt := edgesV[lastPt]
edgesV.Delete(lastPt)
poly.pop() ;remove last flag
poly.push(nextPt)
poly.push(1) ;next flag
}
if(poly[1] == nextPt) ;chain is closed
{
poly.pop() ;remove last flag
poly.pop() ;remove duplicate end point chain is complete
return poly
}
}
}
}
}