Design-Drawing Home  
Drawing Program
ISSN 1441-5585

Search...

Home
Articles
Software Catalog
Book Store
About
Advertising
Newsletter

 

 

Whar’ Did I Land?
A wee exposé on Visio’s "HitTest" Functionality

David A Edson

Ceuid Mile Failte an’ welcome back again. In this article I'll discuss a new bit of functionality which came about with Visio 4.5 that can work minor miracles for ye. This is the "HitTest" function available through VBA. Now I suppose I should begin this by describing just what the HitTest function is’ and what it can do for you.

The HitTest function is a Visio API call, which can test any shape that has been dropped, either by dragging from a stencil, copied, or simply moved. This test will return an integer, which tells you if the shape has been dropped;

  • outside the boundary of a target shape,
  • on the boundary of a target shape, or
  • inside the filled area of a 2-D shape, or on the line of a 1-D shape.

The target can be either a single shape, or can be analyzed within a loop to look at a collection of shapes.

Why then, would you need this functionality? Weel lads an’ lassies…. Here’s but a few of the potential uses for this function:

  • Perhaps you are generating a Process and Instrumentation Diagram and you want to perform an analysis when a particular valve is dropped on a given pipeline.
  • Suppose you are designing Printed-Circuit-Board layouts and you place transistors along a given circuit run and need to analyze the changed resistance based upon the new component.
  • You might need to query a map to determine the location of a emergency response vehicle when placed on a given city street.
  • You may wish to fill in the director’s name upon dropping the subordinate’s shape in an orginisational charting situation.

These, and many more examples, are but a few of the reasons why this technology is both helpful and important. So let’s, then, have a wee look at the function itself; its syntax and its required arguments:

IntegerReturnValue = object.HitTest(x_test_position, y_test_position, tolerance)

Where:

Object is the given target shape being tested

X_test_position is the x ordinate of the location in Page Units being analysed

Y_test_position is the y abscissa of the location in Page Units being analysed

Tolerance is the distance away from the target x,y coordinate in internal units for a cushion

IntegerReturnValue is an integer evaluating to one of the 3 following possibilities:

visHitOutside (0), visHitOnBoundary (1) or visHitInside (2)

Note that the object is an obtainable shape or group shape that has been dropped, copied or moved. The x, y and tolerance values are real numbers expressed in Visio’s internal units (inches).

Note, also, that the object is a single object, not a collection. To effectively use this functionality, you will need to test against all potential objects that could be at the given x and y coordinate specified. It is therefore quite easy to test against all of the shapes in a given drawing. You simply obtain the drawings Shapes collection:

Set AllShapes = Visio.ActivePage.Shapes

and obtain the count property:

Set FullCount = AllShapes.Count

and then loop through the shapes obtaining the results of each test until you arrive at the desired result, taking action at that time. The code would look something like the following:

Public Sub WhereDidILand ()
Dim AllShapes As Visio.Shapes

‘the collection of all shapes on the page
Dim Source As Visio.Shape
‘a shape to be tested on its drop
Dim S_PinX As Visio.Cell
‘ the test shapes PinX cell
Dim S_PinY As Visio.Cell
‘ the test shapes PinY cell
Dim FullCount As Integer
‘ the number of shapes on the page
Dim Counter As Integer
‘ a counter for the loop
Dim IntRetVal As Integer
‘ a holder for the function return value
Dim MessageText As String

‘ a string for a user message
Set AllShapes = Visio.ActivePage.Shapes
‘ Get all of the shapes on the active page of the document

FullCount = AllShapes.Count
‘ Find out how many of them exist in this document on this page
Set Source = Visio.ActiveWindow.Selection.Item(1)
‘ get the currently selected shape, i.e. the shape just dropped

For Counter = 1 To FullCount
‘ begin a loop to step through all of the shapes
IntRetVal = AllShapes.Item(Counter).HitTest(S_PinX, S_PinY, 0.125)
‘ perform the actual HitTest using a "fudge-Factor" of 1/8 inch
‘ note we are looking at each shape on the page as the analysis object
‘ and using the test shapes PinX and PinY as the locator

Select Case IntRetVal
‘ begin a select case construct
Case 0
‘ if HitTest returned the integer zero
‘ then the subject was dropped outside the boundary of the target shape
MessageText = "Outside the Target Shape."
Case 1
‘ if HitTest returned the integer one
‘ then the subject was dropped on the boundary of the target shape
MessageText = "On the Boundary of the Target Shape."
Case 2
‘ if HitTest returned the integer two
‘ then the subject was dropped inside the filled area of the target shape
MessageText = "In the Filled Area of the Target Shape."
End Select

‘ end the select case construct

MsgBox "You have just Landed " & MessageText
‘ concatinate and display the message to the user
Next Counter
‘ end of the for Next loop
End Sub
‘end of the subroutine

Now here ye at least see hoo it is implemented. Howe’re there are several things less than optimal in the above code. Firstly, this example tests against every single one o’ the wee shapes that live on the Document’s page. This could be painstakingly slow if there weare several thousand shapes on the page. Secondly, because it tests against ALL shapes on the page, it tests against itsel’. This will return a "False Positive" in the testing. I assume tha’ ye dinnae want this tae happen. It is akin to the home fertility test showin’ a wee plus sign. Ye dinnae want it to happen unless ye truly are expectin’ it, aye?

Weel we can take steps to insure that the test shape itself is eliminated from the selection as follows:

Public Sub WhereDidILand ()
Dim AllShapes As Visio.Shapes
Dim Source As Visio.Shape
Dim S_Name As String

‘ The name of the Source Shape
Dim S_PinX As Visio.Cell
Dim S_PinY As Visio.Cell
Dim Target As Visio.Shape

‘ the target shape to be analysed
Dim FullCount As Integer
Dim Counter As Integer
Dim IntRetVal As Integer
Dim MessageText As String

Set AllShapes = Visio.ActivePage.Shapes
FullCount = AllShapes.Count
Set Source = Visio.ActiveWindow.Selection.Item(1)
S_Name = Source.Name

‘ get the name of the source shape
S_Name = Source.Name

For Counter = 1 To FullCount
Set Target = AllShapes.Item(Counter)
If Target.Name <> S_Name

‘ validate that we are not looking ate the source shape
IntRetVal = Target.HitTest(S_PinX, S_PinY, 0.125)

Select Case IntRetVal
Case 0
MessageText = "Outside the Target Shape."
Case 1
MessageText = "On the Boundary of the Target Shape."
Case 2
MessageText = "In Filled Area of the Target Shape."
End Select

MsgBox "You have just Landed " & MessageText
End If

‘ end the If test
Next Counter
End Sub

Now as tae the issue of having to test against all o’ the shapes in a given Document’s page… It is best to build either a collection or an array of the shapes that you wish to test against, and THEN run the hit test against THAT selection/collection. This will greatly diminish the number of iterations through the loop and truly enhance the speed and functionality of your solution. One method of making this possible is to add a user-defined cell to each of the shapes that you wish to be members of the target selection/collection and then on Document Open build the selection/collection one time and use this public selection/collection as your test criteria loop. The code fragment below illustrates one methodology for implementing this:

Public Sub GetTShapes()
'Build a list of shapes that are target shapes to reduce
'hit test searching when source shapes are dropped
Dim i As Integer
Dim iCt As Integer
Dim iFoundShapes As Integer
Dim shp As Visio.Shape
Dim pg As Visio.Page
Set pg = Visio.ActiveDocument.Pages.Item(1)

‘Scan page 1 for shapes that are target shapes
iCt = pg.Shapes.Count
If iCt < 1 Then Exit Sub
ReDim TLShapes(1 To iCt)
iFoundShapes = 0

For i = 1 To iCt
Set shp = pg.Shapes.Item(i)
If shp.CellExists("User.Class", 1) Then

‘this presumes that shapes exist with a user-defined cell called ``User.Class``
If shp.Cells("User.Class").Formula = """Valid Target""" Then
'We really do have a target shape!
‘ this presumes a user-defined cell on each target shape with
‘ the value ``Valid Target`` in that cell
iFoundShapes = iFoundShapes + 1
TLShapes(iFoundShapes) = shp.Name
End If
End If
Next i

'Collapse the array:
If iFoundShapes > 0 Then ReDim Preserve TLShapes(1 To iFoundShapes)

MsgBox "Retrieved " & UBound(TLShapes) & " Shapes"

End Sub

So… armed with this wee bit o’ knowledge, you should be able to set up a HitTest of your own and find numerous uses for this functionality. Keep tae mind that you are not going to be simply displaying message boxes, but rather, you will be using the properly located shape to potentially drill doon tae other cells within the target shape’s shapesheet and pass that information off tae the subject’s shapesheet in the form of Custom Property Cells, User-Defined Cells, potentially even changes in Geometry or formatting.

As a’ways enjoy the process of making your shapes and solutions mir an’ mir powerfu’.

"Haste ye back."

Dave "The Auld Scotsman" Edson

 

 
Rate this article...
Hmmm  OK  Good  Yes! Brilliant
Your a friend about this article.

Copyright © 1998-2007 DBM & others | Disclaimer | Privacy | Re-publication | Trademarks | Webmaster | Home