Determining if shapes overlap by coordinates and sizes?

First: I can absolutely write this myself, only look at this question for the fun on it.

I started to write this myself and thought “This algorithm is stupid. There is clearly a much better way to write this.”

Usually when I think that, I stop writing it and a better solution pops into my head within the next couple of days.

But just for the fun of it, I thought I’d post this one and see if anyone comes up with something elegant.

I’ve been UI scripting an application with no dictionary and little access to System Events UI scripting, using mostly scripted mouse clicks and keyboard.

If the user places some of the application windows so they overlap, the script breaks - the clicks it thinks are in one window are sent to another window in front of it. If they’re really unlucky in how the windows overlap, it could break catastrophically - clicking buttons to delete things.

But while I can’t get much UI info or click UI elements with System Events in this App, I can at least get the open window’s origin points and sizes… so that’s enough info to determine whether or not there are any overlaps, and warn the user to sort out their windows and run the script again if there are.

tell application "System Events"
	tell application process "ICISS Scanner"
		set frontmost to true
		delay 0.2
		set windowPositions to the position of every window
		set windowSizes to the size of every window
	end tell
end tell

The grid here is top left corner of screen = X0, Y0, counting up as you go right or down.

windowPositions is the coordinates for the top left corner of each window.
windowSizes is the width and height

Test data set results:
windowPositons: {{18, 730}, {25, 980}, {23, 28}, {101, 227}, {374, 450}, {502, 23}}
windowSizes: {{174, 223}, {216, 168}, {159, 255}, {159, 135}, {447, 455}, {1070, 1572}}

Anybody want to take a crack at an elegant way of determining whether or not there are any overlaps?

If not, I’ll probably think of a good way, or else get bored waiting for an idea and just hammer through it in an inelegant way.

Thanks,

Tom.

Oh, and just to further this thought, I thought I’d add that I may have already thought of one of the funniest solutions.

Make a new Photoshop document the size of the display.
Make a new layer for each window, with a fill of the same neutral grey covering the size and position of the window.
Set the blending mode on all the layers to multiply.
Run a histogram on the document. If any pixels are any color other than white or the neutral grey I filled with, then there’s an overlap.

  • Tom.

Nevermind

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

tell application "System Events"
	tell application process "ICISS Scanner"
		set frontmost to true
		delay 0.2
		set {windowPositions, windowSizes} to {the position, the size} of every window
	end tell
end tell

set windowConflict to false
set windowCount to count of windowPositions
repeat with i from 1 to windowCount
	set {X1, Y1} to item i of windowPositions
	set {W1, H1} to item i of windowSizes
	repeat with j from i + 1 to windowCount
		set {X2, Y2} to item j of windowPositions
		set {W2, H2} to item j of windowSizes
		if (X1 + W1 > X2 and Y1 + H1 > Y2 and X2 + W2 > X1 and Y2 + H2 > Y1) then set windowConflict to true
	end repeat
end repeat

This is quite a bit slower, although still not too bad. Just a bit different in approach…

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set windowPositions to {{18, 730}, {25, 980}, {23, 28}, {101, 227}, {374, 450}, {502, 23}}
set windowSizes to {{174, 223}, {216, 168}, {159, 255}, {159, 135}, {447, 455}, {1070, 1572}}
set windowCount to count of windowPositions
repeat with i from 1 to windowCount - 1
	repeat with j from i + 1 to windowCount
		if current application's NSIntersectsRect({item i of windowPositions, item i of windowSizes}, {item j of windowPositions, item j of windowSizes}) is true then return true
	end repeat
	if i = windowCount - 1 then return false
end repeat

Hi Shane.

Your script works (on my machine). But shouldn’t that be current application’s NSRect’s NSIntersectsRect?

No – it’s a Foundation function, not a method.

OK, Shane. Thanks.

The only reference I could find to it in the Xcode 9.2 documentation was under NSRect. The script even works with “NSRect’s” written into it. But I see NSRect isn’t a class but a “Type Alias”. It probably makes sense to someone. :wink:

Edit: Typo corrected.

NSRect is a C struct. Think of a kind of record with labels that are not part of the data, and content defined by it’s position and length. Or see wikipedia, which will do a much better job of explaining it than me :expressionless: It’s much lower level and more efficient than a class.

Thanks Shane.

It’s interesting the contrast between Applescript and ObjectiveC, where Applescript hardly has a built-in function for anything, and ObjectiveC seems to have a built-in function for everything.

I just had to sort a list of records by the contents of one record item, and I definitely felt like I must be re-inventing the wheel for the bajillionth time as I wrote the handler in Applescript.

  • Tom.

It was designed as a language for talking to applications, with very little of its own functionality. It’s gained a few things over the years, but not much.

Think libraries :).

I missed this part. NSRects are aliases to CGRects. Short version: In 32-bit times NSRect stored 32-bit values, so CGRect was invented for Core Graphics, which was 64-bit and required 64-bit versions. Now, when running 64-bit mode NSRect is just an alias for CGRect.

Think customisable sort. :slight_smile:

Thanks. Customisable sort looks great, but that sort of solution quickly runs into the practical limit of: is it faster to google up an existing solution, or write the handler myself?

I tried a couple of searches for maybe 1 minute, but wasn’t finding what I was looking for. Writing it took maybe 10 minutes? I don’t know if I’d have found this in 10 minutes of Googling.

Of course, this is a good general purpose handler to add to a library and keep in mind for the future.

Thanks,

Tom.

The idea is that you can write the isGreater() handler yourself, which is very simple to do (even in Script Editor :wink: ), and pass the script object containing it to the already existing sort handler. All isGreater() has to do is to return a boolean indicating whether its first parameter is greater than (or should come after) its second parameter according to the sort criteria. So if you’re sorting a list of records by one property, say ‘age’, it’s simply:

use sorter : script "Custom Iterative Dual-Pivot Quicksort.scpt" -- Or whatever you've called the sort script file.

-- set myList to a list of records with 'age' properties.

script byAge
	on isGreater(a, b)
		return (a's age > b's age)
	end isGreater
end script

tell sorter to sort(myList, 1, -1, {comparer:byAge})

It doesn’t take ten minutes.

But this is hijacking the current thread. Can we move any further discussion of custom sorts to the thread linked to in post #12 or to a new thread altogether?

I understand, I’m just saying that at the time I needed to perform this sort, that discovering the existence of your “sorter” function would have taken me longer than simply it took to write the handler.

Now that I am aware of your sorter function, I’d use that going forward.