wingetpos with respect to virtual dimensions Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
colt
Posts: 291
Joined: 04 Aug 2014, 23:12
Location: Portland Oregon

wingetpos with respect to virtual dimensions

01 Sep 2020, 09:31

When I change my default monitor I get different values for winGetPos. The x origin is always 0 on left side of main display. How can I map winGetPos result to virtual dimensions without setting manual offsets?

Code: Select all

SysGet, VirtualWidth, 78
SysGet, VirtualHeight, 79
loop
{
	winGetPos,x,y,,,A
	tooltip % VirtualWidth . " " . VirtualHeight . "`n" . x . " " . y ;want x and y to always be positive no matter layout or main monitor setting
	sleep 50
}
User avatar
boiler
Posts: 17215
Joined: 21 Dec 2014, 02:44

Re: wingetpos with respect to virtual dimensions

01 Sep 2020, 12:37

Code: Select all

SysGet, VirtualWidth, 78
SysGet, VirtualHeight, 79

loop
{
	Pos := WinGetRelativePos("A")
	ToolTip, % VirtualWidth . " " . VirtualHeight . "`n" . Pos.x . " " . Pos.y
	Sleep, 50
}
return

WinGetRelativePos(winTitle) {
	SysGet, monNum, MonitorPrimary
	SysGet, mon, Monitor, % monNum
	WinGetPos, x, y,,, % winTitle
	return {x: x - monLeft, y: y - monTop}
}
colt
Posts: 291
Joined: 04 Aug 2014, 23:12
Location: Portland Oregon

Re: wingetpos with respect to virtual dimensions

02 Sep 2020, 09:10

I thought this solved it, but after more thorough testing it appears to give the same result as the original script.
If you position your monitors with the primary display to the right of your secondary display then the origin will be directly between the two screens. My goal is to move it to the upper left of the secondary screen.
Problem is that I don't know what my monitor layout will be. If I change your code to

Code: Select all

WinGetRelativePos(winTitle) {	
	SysGet, mon, Monitor, 2
	WinGetPos, x, y,,, % winTitle
	return {x: x - monLeft, y: y - monTop}
}
then I get the result I want. Only because I know the physical layout. If I have three monitors side by side then I would need to offset by the width of two monitors.
I guess what it really comes down to is detecting the positioning of the monitors like shown in display settings.
User avatar
DyaTactic
Posts: 221
Joined: 04 Apr 2017, 05:52

Re: wingetpos with respect to virtual dimensions  Topic is solved

07 Sep 2020, 07:30

Maybe this works on all monitor setups:

Code: Select all

SysGet, VirtualWidth, 78
SysGet, VirtualHeight, 79

loop
{
	Pos := WinGetRelativePos("A")
	ToolTip, % VirtualWidth . " " . VirtualHeight . "`n" . Pos.x . " " . Pos.y
	Sleep, 50
}
return

WinGetRelativePos(winTitle="A") {
	WinGetPos, x, y,,, % winTitle
	PosIsOnMonitor(x, y, monLeft, monTop)
	return {x: x - monLeft, y: y - monTop}
}

PosIsOnMonitor(Mx, My, ByRef MonLeft, ByRef MonTop) {
	; Get the dimensions of all monitors.
	SysGet, MonCount, MonitorCount
	Loop, % MonCount
	{
		SysGet, Mon, Monitor, % A_Index
		LastMon := A_Index
		
		; Check if the position is on this monitor.
		If IsBetween(Mx, MonLeft, MonRight, "Lower") AND IsBetween(My, MonTop, MonBottom, "Lower") {
			Break	; Leave MonLeft, MonRight, MonTop and MonBottom as is and continue.
		}
	}
	Return LastMon
}


IsBetween(Value, LowerLimit, UpperLimit, IncludingLimit:="0") {
	If (IncludingLimit = "0") {
		If (Value > LowerLimit) AND (Value < UpperLimit) {
			Return True
		} Else {
			Return False
		}
		
	} Else If (IncludingLimit = "Lower") {
		If (Value >= LowerLimit) AND (Value < UpperLimit) {
			Return True
		} Else {
			Return False
		}
		
	} Else If (IncludingLimit = "Upper") {
		If (Value > LowerLimit) AND (Value <= UpperLimit) {
			Return True
		} Else {
			Return False
		}
		
	} Else If (IncludingLimit = "1") {
		If (Value >= LowerLimit) AND (Value <= UpperLimit) {
			Return True
		} Else {
			Return False
		}
	}
}
colt
Posts: 291
Joined: 04 Aug 2014, 23:12
Location: Portland Oregon

Re: wingetpos with respect to virtual dimensions

11 Sep 2020, 10:43

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					
				}
			}			
		}
	}		
}

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Chunjee, GEOVAN and 197 guests