Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chriss Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

IntersectRect API call problem 3

Status
Not open for further replies.

gmmastros

Programmer
Feb 15, 2005
14,910
US
I "modified" the API call slightly, and it seems to work. Basically, I am trying to use this API with a map (latitude/longitude). I got tired of getting confused about the top/bottom, min/max weirdness, so I created an alias for the API call and a 'different' rect structure to suit my needs a little better. Basically, I'm just renaming the API call and the 4 members of the Rect structure.

This seems to work properly, but it has lead to another problem, specifically with the API call, and I was wondering if someone knew how to fix or accommodate this problem.

Not that it's important,but... I am trying to use this API call to determine if a road (line) is within the bounds of my map. The problem is, if the road is completely vertical or horizontal, the API call returns 0, and I would prefer for it to return 1 (true).

Here's some sample code to illustrate the problem.

Code:
Option Explicit

Private Type MapBound
    MinLongitude As Long
    MinLatitude As Long
    MaxLongitude As Long
    MaxLatitude As Long
End Type

Private Declare Function MapIntersect Lib "user32.dll" Alias "IntersectRect" (lpDestRect As MapBound, lpSrc1Rect As MapBound, lpSrc2Rect As MapBound) As Long

Private Sub Command1_Click()
    
    Dim Intersect As MapBound
    Dim Rect1 As MapBound
    Dim Rect2 As MapBound
    
    Rect1.MinLongitude = 7500000
    Rect1.MaxLongitude = 7600000
    Rect1.MinLatitude = 4000000
    Rect1.MaxLatitude = 4100000

    Rect2.MinLongitude = 7550000
    Rect2.MaxLongitude = 7550001
    Rect2.MinLatitude = 4050000
    Rect2.MaxLatitude = 4050001
    
    [green]' The following returns 1[/green]
    Debug.Print MapIntersect(Intersect, Rect2, Rect1)
    
    [green]-- make longitude values the same[/green]
    Rect2.MinLongitude = 7550000
    Rect2.MaxLongitude = 7550000
    Rect2.MinLatitude = 4050000
    Rect2.MaxLatitude = 4050001
    
    [green]' The following returns [!]0[/!][/green]
    Debug.Print MapIntersect(Intersect, Rect2, Rect1)
    
    [green]-- make latitude values the same[/green]
    Rect2.MinLongitude = 7550000
    Rect2.MaxLongitude = 7550001
    Rect2.MinLatitude = 4050000
    Rect2.MaxLatitude = 4050000
    
    [green]' The following returns [!]0[/!][/green]
    Debug.Print MapIntersect(Intersect, Rect2, Rect1)

End Sub

The API call is faster than vb code for determining if something is within the bounds, so I prefer to use it. I'm wondering if there is another API call that would suit my needs any better. Most of the lines I am testing are not vertical or horizontal, but it can happen so I need to accommodate it.



-George

"The great things about standards is that there are so many to choose from." - Fortune Cookie Wisdom
 
Well, I guess a rect cannot have a width or height of 0. Then it doesn't exist as a rect. The rect needs to have some width and height for it to be a rect.


So, I guess if MinLatitude(left border) and MaxLongitude(right border) are equal, subtract 1 from MinLatitude(left border) to produce a fictional MinLatitude (left border).
Then I would think you can then determine if at least the MaxLatitude(right border) is within the boundries.




 

...but only to the middle point. After that you would add 1 to make a fictional right border.
 

Where's my coffee..

>After that you would add 1 to make a fictional right border

That is, added to the xMax
 
George,

In addition to SBerthold's post(s) if you pass an empty rectangle into MapIntersect it will always return 0 and the returned Intersect (Mapbound type) will also be empty.

An empty rectangle is one of 0 width and/or 0 height and can be tested using the IsRectEmpty() API call (I did a quick test using your code, included below which does indicate that for once I'm not talking absolute gibberish! [wink])
Code:
Private Declare Function IsRectEmpty Lib "user32" (lpRect As MapBound) As Long

Private Sub Command2_Click()
    Dim Intersect As MapBound
    Dim Rect1 As MapBound
    Dim Rect2 As MapBound
    
    Rect1.MinLongitude = 7500000
    Rect1.MaxLongitude = 7600000
    Rect1.MinLatitude = 4000000
    Rect1.MaxLatitude = 4100000

    Rect2.MinLongitude = 7550000
    Rect2.MaxLongitude = 7550001
    Rect2.MinLatitude = 4050000
    Rect2.MaxLatitude = 4050001
    
    ' Teh following returns 1,0,0,0
    Debug.Print MapIntersect(Intersect, Rect2, Rect1)
    Debug.Print IsRectEmpty(Rect1)
    Debug.Print IsRectEmpty(Rect2)
    Debug.Print IsRectEmpty(Intersect)
    
    '-- make longitude values the same
    Rect2.MinLongitude = 7550000
    Rect2.MaxLongitude = 7550000
    Rect2.MinLatitude = 4050000
    Rect2.MaxLatitude = 4050001
    
    ' The following returns 0,1,1
    Debug.Print MapIntersect(Intersect, Rect2, Rect1)
    Debug.Print IsRectEmpty(Rect2)
    Debug.Print IsRectEmpty(Intersect)
    
    '-- make latitude values the same
    Rect2.MinLongitude = 7550000
    Rect2.MaxLongitude = 7550001
    Rect2.MinLatitude = 4050000
    Rect2.MaxLatitude = 4050000
    
    ' The following returns 0,1,1
    Debug.Print MapIntersect(Intersect, Rect2, Rect1)
    Debug.Print IsRectEmpty(Rect2)
    Debug.Print IsRectEmpty(Intersect)
End Sub
I'd tend to have to agree with SBerthold also in that the method he mentioned seems to be a sensible solution.

Hope this helps


HarleyQuinn
---------------------------------
The most overlooked advantage to owning a computer is that if they foul up there's no law against wacking them around a little. - Joe Martin

Get the most out of Tek-Tips, read FAQ222-2244 before posting.
 
On another tangent, if the road is completely horizontal/vertical could you use the PtInRect() function instead?

HarleyQuinn
---------------------------------
The most overlooked advantage to owning a computer is that if they foul up there's no law against wacking them around a little. - Joe Martin

Get the most out of Tek-Tips, read FAQ222-2244 before posting.
 
My biggest goal here is to get functionality that is fast and accurate. Trust me when I say... I have spent considerable time examining every line of code to 'trim the fat' so to speak.

I have certain process within my app that loop hundreds of thousands of times. Speed is critical for me. I did not know about the IsRectEmpty function. However, I don't (immediately) see how that will help.

If two rects do not overlap, the IntersectRect API returns 0. If one of the rects is empty (because it's purely horizontal or vertical), IntersectRect returns 0.

So, I could test IsRectEmpty for the Intersect rect, but it would also be empty for Rect's completely outside the other rect too. What I am trying to avoid is a dozen if checks/function calls to determine if there is an overlap on the rects.

What I am considering is a combination of both HarlyeQuinn's and SBerthold's advice. Unfortunately, this would cause a lot of code changes.

The idea is....

When I load the data, I could create a MapBound for each line. Immediately after creating the Bound, I could Check IsRectEmpty. If it's empty, add 1 to the max latitude and longitude, and subtract 1 from the min latitude and longitude. (If I only do this for empty rects, I'd be guaranteed to get a non-empty rect.) This would only have to happen once (when I load the data).

Then, when I go loopy on the data, I would have a MapBound structure that is NOT empty, guaranteed. Basically, by taking a slight hit when loading the data, I could gain performance when using it.

-George

"The great things about standards is that there are so many to choose from." - Fortune Cookie Wisdom
 
Unfortunately, this idea also adds to the memory overhead. I would have to keep (in memory) the bounds data and the 'real' data. 16 extra bytes for each line. [sad]


-George

"The great things about standards is that there are so many to choose from." - Fortune Cookie Wisdom
 
George said:
Unfortunately, this idea also adds to the memory overhead. I would have to keep (in memory) the bounds data and the 'real' data. 16 extra bytes for each line. sad[/quoteI can see why that's a real no-no of an idea then mate [sad]

HarleyQuinn
---------------------------------
The most overlooked advantage to owning a computer is that if they foul up there's no law against wacking them around a little. - Joe Martin

Get the most out of Tek-Tips, read FAQ222-2244 before posting.
 
If you're going for the speed of the API's, my best guess would be to use InflateRect() which if you pass in 1 for both the x and y parameters will handle the addition/subtraction for you. E.g.(a very simple example):
Code:
Private Declare Function InflateRect Lib "user32" (lpRect As MapBound, ByVal x As Long, ByVal y As Long) As Long

Private Sub Command1_Click()
    
    Dim Intersect As MapBound
    Dim Rect1 As MapBound
    Dim Rect2 As MapBound

    Rect2.MinLongitude = 7550000
    Rect2.MaxLongitude = 7550000
    Rect2.MinLatitude = 4050000
    Rect2.MaxLatitude = 4050001

    If IsRectEmpty(Rect2) Then
        InflateRect Rect2, 1, 1
        Debug.Print Rect2.MinLongitude
        Debug.Print Rect2.MaxLongitude
        Debug.Print Rect2.MinLatitude
        Debug.Print Rect2.MaxLatitude
        Debug.Print IsRectEmpty(Rect2)
    End If
End Sub
Hope this helps

HarleyQuinn
---------------------------------
The most overlooked advantage to owning a computer is that if they foul up there's no law against wacking them around a little. - Joe Martin

Get the most out of Tek-Tips, read FAQ222-2244 before posting.
 
And just for the hell of it (not sure if it'll be any help to anyone someday [wink]), here's a quick speed comparison for three of the ways (there will be others, no doubt faster one's as well that George is probably already using [wink]) to set the properties to the values described by George.
Code:
Private Sub Command123_Click()
    Dim Rect1 As MapBound
    Dim Rect2 As MapBound
    Dim Start As Single
    Dim i As Long
        
    SetRect Rect1, 7500000, 4000000, 7600000, 4100000
    
    Start = Timer
    
    For i = 0 To 10000000
    
    SetRect Rect2, 7550000 + i, 4050000, 7550000 + i, 4050001
    
    If IsRectEmpty(Rect2) Then
        InflateRect Rect2, 1, 1
    End If
    
    Next i
    
    Debug.Print i & " using InflateRect in " & Format((Timer - Start) * 1000, "0.000") & " milliseconds"
    
    Start = Timer
    
    For i = 0 To 10000000
    
    SetRect Rect2, 7550000 + i, 4050000, 7550000 + i, 4050001
    
    If IsRectEmpty(Rect2) Then
        SetRect Rect2, 7550000 - 1, 4050000 - 1, 7550000 + 1, 4050001 + 1
    End If
    
    Next i
    
    Debug.Print i & " using SetRect in " & Format((Timer - Start) * 1000, "0.000") & " milliseconds"
    
    Start = Timer
    
    For i = 0 To 10000000
    
    SetRect Rect2, 7550000 + i, 4050000, 7550000 + i, 4050001
    
    If IsRectEmpty(Rect2) Then
        Rect2.MinLongitude = Rect2.MinLongitude - 1
        Rect2.MaxLongitude = Rect2.MaxLongitude + 1
        Rect2.MinLatitude = Rect2.MinLatitude - 1
        Rect2.MaxLatitude = Rect2.MaxLatitude + 1
    End If
    
    Next i
    
    Debug.Print i & " using Rect2 Properties in " & Format((Timer - Start) * 1000, "0.000") & " milliseconds"
    
End Sub
And a sample output:
10000001 using InflateRect in 765.625 milliseconds
10000001 using SetRect in 828.125 milliseconds
10000001 using Rect2 Properties in 1187.500 milliseconds
Again, don't know if this will help anyone but I'd been writing it so I wanted to share [wink]

HarleyQuinn
---------------------------------
The most overlooked advantage to owning a computer is that if they foul up there's no law against wacking them around a little. - Joe Martin

Get the most out of Tek-Tips, read FAQ222-2244 before posting.
 
As for the mentioned performance hit using IsRectEmpty(), for methods 1 & 3 it seems to consistantly add 200 (give or take) milliseconds for a 10,000,000 loop cycle, while only seeming to 100 (give or take) milliseconds to method 2 (strangely).

HarleyQuinn
---------------------------------
The most overlooked advantage to owning a computer is that if they foul up there's no law against wacking them around a little. - Joe Martin

Get the most out of Tek-Tips, read FAQ222-2244 before posting.
 
>Most of the lines I am testing

There are also occassions when the bounding rectangle of a line can intersect another rectangle without any part of the line itself passing through that rectangle. In which case you'd get a TRUE when you really want a FALSE

So I'd work with regions and paths rather than rects, since we can pretty simply check for the intersection between a complex region and a rectangle using the CombineRgn API call. You'd need to look at creating a path (BeginPath/EndPath) and then using WidenPath (widening to 3 units of your selected scale) and then PathToRegion to get the complex region for the path
 
There are also occasions when the bounding rectangle of a line can intersect another rectangle without any part of the line itself passing through that rectangle. In which case you'd get a TRUE when you really want a FALSE

A false positive would require a little extra processing (drawing something off-screen for example). I'm less concerned about the false positives. False negatives are the real problem.

That being said... I'll do a little research regarding these API calls. If this prevent the false negatives, it's worth it.

Thanks.

-George

"The great things about standards is that there are so many to choose from." - Fortune Cookie Wisdom
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top