• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar

Visio Guy

Smart graphics for visual people




  • Home
  • Hire Me
    • Hire Me
    • Résumé
  • Products
    • Products
    • Bubble Revision Shape
    • Layers to Pages Utility
    • Rack Unit Dimension Line
    • Radial Elements Tool with up to 100 Wedges
    • Text on a Circle Visio SmartShape
  • Index
    • Articles by Date
    • YouTube – VisioGuy
    • Download Information
    • Suggestion Box
    • Shop
    • Visio Art
    • Visio Links
    • – Visio Shapes & Stencils
    • – Visio Templates & Drawings
  • About
    • About
    • Donate
    • GitHub
    • jsFiddle
    • Reading List
    • Subscribe & Follow
      • – E-mail
      • – facebook
      • – Twitter
      • – RSS
    • Privacy Policy
  • Discussion Forum
You are here: Home / Development / Code / LengthIU Bug Workaround Code

LengthIU Bug Workaround Code

October 20, 2007 By Visio Guy 10 Comments

Read Full Article

Visio 2003 has a defect in the LengthIU property of a Shape object. This article discusses the bug, describes a work-around, and offers Visual Basic code to help you get back on track!

Note: LengthIU for open paths works properly in Visio 2007 and 2010. This article concerns a problem with Visio 2003.

LengthIU: Just What We Were Looking for!

If you’ve gotten far enough into the Visio Object Model to have discovered the LengthIU shape property, then you are likely quite proud of yourself, and surely ecstatic that you don’t have to calculate path-lengths using any…um…er…MATH

But if you’re using Visio 2003, you might have encountered some strange results which have caused you to distrust this property…and start searching for your trig books. The problem is that the LengthIU property returns 0 for paths that are not closed.

LengthIU Background

LengthIU is supposed to conveniently returns the total length of the geometry paths in a single shape. If a shape has multiple geometry sections, then the lengths of each section are totaled together.

LengthIU doesn’t iterate through the shapes in a group, however. So don’t be shocked if you get 0 as the LengthIU of a grouped shape. This is not the bug that we’re talking about. For groups, we would just iterate through the sub-shapes and sum all the individual lengths together.

The result that LengthIU returns is always in inches, which is the Internal Unit system that Visio uses for all calculations. Since this IU number is in the scale of the drawing, you could use the result to find out the amount of cabling required for a new networking project, or the distances to nearest fire escapes in a building plan.

A simple example would be to take a 1-inch square and query LengthIU. Of course, we would get 4 as the result. Slightly more complicated, we might draw two such squares, and combine them using: Shape > Operations > Combine. This gives a single shape with two paths. The length of these paths adds up to 8.

LengthIU Examples

As you can see in the illustrations, you can easily test using a snippet of VBA code. First, select a shape in the drawing window. Then hit Alt + F11 to bring up the VBA editing environment. In the VBA Editor’s Immediate window, you can then type this line and hit return:

? Visio.ActiveWindow.Selection(1).LengthIU

You should see your result on the next line.

Bugsville: An Open and Closed Issue

However, if we’re measuring distances, we’re probably not measuring closed polygons like circles or rectangles. More likely, we’re measuring connectors or multi-segmented lines, free-form curves, or, god-forbid, NURBS. These shapes are open; they’re non-closed; they have no insides; there ends don’t meet. And that’s where the trouble starts.

In Visio 2003, open paths return a LengthIU = 0.

As you can see; 0, 0, and 0! This is simply a bug. Microsoft has confirmed the bug, but I don’t know if there is any explanation other than “oops!”

Don’t Give Up, There’s Hope!

To get LengthIU to work on open paths, we need to first close them.

It turns out that each Geometry section conveniently has a NoFill section that allows us to do this! Simply setting GeometryN.NoFill = 0 causes a shape to be closed, and LengthIU to return a value other than 0.

But the LengthIU result that we’ll get will then be too long: it will contain the distance from the last vertex to the first vertex, which doesn’t really exist on the path.

So the work-around algorithm goes something like this:

  • Determine if a shape has any GeometryN.NoFill = 1 sections
  • If not, then just use LengthIU as normal
  • For each non-closed geometry section in a shape…
    • Close the geometry section using NoFill = 0
    • Calculate the distance between the first and last vertex of the section with help from Mr. Pythagoras: SQRT( (x2-x1)^2 + (y2-y1)^2 )
    • Increment the sum of all of these distances
  • Loop to next geometry section
  • Get LengthIU for the altered shape
  • Subtract the first-vertex-last-vertex distance sum from that value
  • Restore the NoFill cells to their original state

Closing Points

  • The LengthIU bug applies to Visio 2003
  • Visio 2003 Service Pack 3 (LINK!!) does not fix this problem
  • I haven’t tested on Visio 2002 (Edit: it’s a Visio 2003-only bug)
  • Visio 2007 has fixed the LengthIU problem (so upgrade!) AMAZON LINK
  • There is no ShapeSheet function for calculating LengthIU, it’s a code-only feature. Of course you can always try to derive a trigonometric formula and put it in a User-defined cell…

The Cure

Below is a listing of the code for the CLengthIuShape class. You can paste the whole thing into a VBA Class Module with the name “CLengthIuShape”.

Note: when cut-and-pasting this code, you might have to do a search and replace on the apostrophe character–to replace the fancy, tilted apostrophe with the simple vertical one, so that VB will understand it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
'// Class: CLengthIuShape
Option Explicit On
 
'// Constants for ShapeSheet sections, rows, cells:
Private Const Sec_FirstGeo = Visio.VisSectionIndices.visSectionFirstComponent
Private Const Row_FirstVertex = Visio.VisRowIndices.visRowVertex
Private Const Row_Component% = Visio.VisRowIndices.visRowComponent
Private Const Cell_NoFill% = Visio.VisCellIndices.visCompNoFill
Private Const Cell_X = Visio.VisCellIndices.visX
Private Const Cell_Y = Visio.VisCellIndices.visY
 
'// Internal class variables:
Private m_visShp As Visio.Shape
Private m_collNonClosedGeoSects As Collection
Private m_extraClosedDistances As Double
Private m_iGeoCt As Integer
Public Function LengthIU(ByVal visShp As Visio.Shape) As Double
 
    m_visShp = visShp
 
    '// Test for zero geometry sections:
    m_iGeoCt = m_visShp.GeometryCount
 
    If m_iGeoCt = 0 Then
 
        LengthIU = 0
 
        Exit Function
 
    End If
 
    '// Get the closed and non-closed geometry sections:
    Call m_getNonClosedSects()
 
    '// If there are no non-closed geometry sections, then
    '// LengthIU should function as normal:
 
    If m_collNonClosedGeoSects.Count = 0 Then
 
        LengthIU = m_visShp.LengthIU
 
        Exit Function
 
    End If
 
    '// If we get this far, then we have non-closed geometry...
    Dim lUndoScopeId As Long
    Dim varNonClosedSec As Object
 
    m_extraClosedDistances = 0
 
    '// Since we have non-closed geometry, we will have to temporarily
    '// set the NoFill cells to 1 to get a LengthIU reading. We will
    '// want to undo these actions after we've got our calculation, so
    '// we begin an UndoScope here:
 
    lUndoScopeId = m_visShp.Document.BeginUndoScope("LengthIU Fix")
 
    For Each varNonClosedSec In m_collNonClosedGeoSects
 
        Call m_alterNonClosedGeoSect(CInt(varNonClosedSec))
 
    Next
 
    LengthIU = m_visShp.LengthIU - m_extraClosedDistances
 
    '// Now, end the UndoScope, and throw away the changes that we made:
    Call m_visShp.Document.EndUndoScope(lUndoScopeId, False)
 
End Function
 
Private Sub m_alterNonClosedGeoSect(ByVal iSec As Integer)
 
    Dim xR1 As Double, yR1 As Double, xRN As Double, yRN As Double
    Dim iLastVertex As Integer
    Dim mag As Double
 
    iLastVertex = m_visShp.RowCount(iSec) - 1
 
    '// Get the values for the first and last vertices:
    xR1 = m_visShp.CellsSRC(iSec, Row_FirstVertex, Cell_X).ResultIU
    yR1 = m_visShp.CellsSRC(iSec, Row_FirstVertex, Cell_Y).ResultIU
    xRN = m_visShp.CellsSRC(iSec, iLastVertex, Cell_X).ResultIU
    yRN = m_visShp.CellsSRC(iSec, iLastVertex, Cell_Y).ResultIU
 
    '// Close the row:
    m_visShp.CellsSRC(iSec, Row_Component, Cell_NoFill).ResultIU = 0
 
    '// Calculate the distance between the first and last point:
    mag = Math.Sqr((xRN - xR1) ^ 2 + (yRN - yR1) ^ 2)
 
    '// Add this distance to the sum of all the manipulated distances:
    m_extraClosedDistances = m_extraClosedDistances + mag
 
End Sub
 
Private Sub m_getNonClosedSects()
 
    Dim i As Integer
    Dim iSec As Integer
 
    m_collNonClosedGeoSects = New Collection
 
    '// Find the non-closed geometry sections:
    For i = 0 To m_iGeoCt - 1
 
        iSec = Sec_FirstGeo + i
 
        If m_visShp.CellsSRC(iSec, Row_Component, Cell_NoFill).ResultIU <> 0 Then
 
            Call m_collNonClosedGeoSects.Add(iSec)
 
        End If
 
    Next
 
End Sub
 
Private Sub Class_Terminate()
 
    m_visShp = Nothing
    m_collNonClosedGeoSects = Nothing
 
End Sub
'// Class: CLengthIuShape
Option Explicit On

'// Constants for ShapeSheet sections, rows, cells:
Private Const Sec_FirstGeo = Visio.VisSectionIndices.visSectionFirstComponent
Private Const Row_FirstVertex = Visio.VisRowIndices.visRowVertex
Private Const Row_Component% = Visio.VisRowIndices.visRowComponent
Private Const Cell_NoFill% = Visio.VisCellIndices.visCompNoFill
Private Const Cell_X = Visio.VisCellIndices.visX
Private Const Cell_Y = Visio.VisCellIndices.visY

'// Internal class variables:
Private m_visShp As Visio.Shape
Private m_collNonClosedGeoSects As Collection
Private m_extraClosedDistances As Double
Private m_iGeoCt As Integer
Public Function LengthIU(ByVal visShp As Visio.Shape) As Double

    m_visShp = visShp

    '// Test for zero geometry sections:
    m_iGeoCt = m_visShp.GeometryCount

    If m_iGeoCt = 0 Then

        LengthIU = 0

        Exit Function

    End If

    '// Get the closed and non-closed geometry sections:
    Call m_getNonClosedSects()

    '// If there are no non-closed geometry sections, then
    '// LengthIU should function as normal:

    If m_collNonClosedGeoSects.Count = 0 Then

        LengthIU = m_visShp.LengthIU

        Exit Function

    End If

    '// If we get this far, then we have non-closed geometry...
    Dim lUndoScopeId As Long
    Dim varNonClosedSec As Object

    m_extraClosedDistances = 0

    '// Since we have non-closed geometry, we will have to temporarily
    '// set the NoFill cells to 1 to get a LengthIU reading. We will
    '// want to undo these actions after we've got our calculation, so
    '// we begin an UndoScope here:

    lUndoScopeId = m_visShp.Document.BeginUndoScope("LengthIU Fix")

    For Each varNonClosedSec In m_collNonClosedGeoSects

        Call m_alterNonClosedGeoSect(CInt(varNonClosedSec))

    Next

    LengthIU = m_visShp.LengthIU - m_extraClosedDistances

    '// Now, end the UndoScope, and throw away the changes that we made:
    Call m_visShp.Document.EndUndoScope(lUndoScopeId, False)

End Function

Private Sub m_alterNonClosedGeoSect(ByVal iSec As Integer)

    Dim xR1 As Double, yR1 As Double, xRN As Double, yRN As Double
    Dim iLastVertex As Integer
    Dim mag As Double

    iLastVertex = m_visShp.RowCount(iSec) - 1

    '// Get the values for the first and last vertices:
    xR1 = m_visShp.CellsSRC(iSec, Row_FirstVertex, Cell_X).ResultIU
    yR1 = m_visShp.CellsSRC(iSec, Row_FirstVertex, Cell_Y).ResultIU
    xRN = m_visShp.CellsSRC(iSec, iLastVertex, Cell_X).ResultIU
    yRN = m_visShp.CellsSRC(iSec, iLastVertex, Cell_Y).ResultIU

    '// Close the row:
    m_visShp.CellsSRC(iSec, Row_Component, Cell_NoFill).ResultIU = 0

    '// Calculate the distance between the first and last point:
    mag = Math.Sqr((xRN - xR1) ^ 2 + (yRN - yR1) ^ 2)

    '// Add this distance to the sum of all the manipulated distances:
    m_extraClosedDistances = m_extraClosedDistances + mag

End Sub

Private Sub m_getNonClosedSects()

    Dim i As Integer
    Dim iSec As Integer

    m_collNonClosedGeoSects = New Collection

    '// Find the non-closed geometry sections:
    For i = 0 To m_iGeoCt - 1

        iSec = Sec_FirstGeo + i

        If m_visShp.CellsSRC(iSec, Row_Component, Cell_NoFill).ResultIU <> 0 Then

            Call m_collNonClosedGeoSects.Add(iSec)

        End If

    Next

End Sub

Private Sub Class_Terminate()

    m_visShp = Nothing
    m_collNonClosedGeoSects = Nothing

End Sub

You can also paste the code into a VB.Net class module, but you’ll need to clean up a few items first:

  1. Add a reference to the Visio type library to your project
  2. Wrap the code in a Public Class CLengthIuShape – End Class block
  3. Add the line “Imports Visio = Microsoft.Office.Interop.Visio” to the beginning of the file
  4. Remove the “Option Explicit” line
  5. Define the const types with ” As Short” before the equals sign
  6. Replace “Math.Sqr” with “System.Math.Sqrt” in the m_alterNonClosedGeoSect subroutine

You can test the class against a selected shape using the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Public Sub TestSelectedShape()
 
    '// See if a shape is selected:
    If Visio.ActiveWindow.Selection.Count = 0 Then Exit Sub
 
    '// Get the selected shape:
    Dim shp As Visio.Shape
    shp = Visio.ActiveWindow.Selection(1)
 
    '// Use the new class to calcualte the
    '// length of the shape's paths:
    Dim liuShp As New CLengthIuShape
    Dim ln As Double
    ln = liuShp.LengthIU(shp) Debug.Print("Length of shape = " & ln & " inches.")
 
End Sub
Public Sub TestSelectedShape()

    '// See if a shape is selected:
    If Visio.ActiveWindow.Selection.Count = 0 Then Exit Sub

    '// Get the selected shape:
    Dim shp As Visio.Shape
    shp = Visio.ActiveWindow.Selection(1)

    '// Use the new class to calcualte the
    '// length of the shape's paths:
    Dim liuShp As New CLengthIuShape
    Dim ln As Double
    ln = liuShp.LengthIU(shp) Debug.Print("Length of shape = " & ln & " inches.")

End Sub
  • Tweet
  • More
  • Pocket
  • Share on Tumblr
  • Print
  • Email

Related posts:

  1. Create Visio Flowcharts Programmatically
  2. The Hidden World of Visio Shapes
  3. Visio and Xml Conference Resources
  4. Customized Visio HTML Export
  5. Text to the Bottom of the Shape

Filed Under: Code Tagged With: Bugs, Code, Length, Polygons, Programming

Previous Post: « Circular Text Generator (version 1)
Next Post: Logitech VX Revolution Mouse Shape »

Reader Interactions

Comments

  1. al edlund says

    October 22, 2007 at 4:18 pm

    Chris,
    this was one that showed up in 2003,
    al

  2. Visio Guy says

    October 22, 2007 at 5:30 pm

    Thanks Al!

    It’s a 2003-only bug folks!

  3. Bryan says

    October 31, 2007 at 12:50 am

    I have been looking for something like this for Visio 2003, but when I created the class, as per your directions, I kept getting errors, both compile time and run-time, the most common of which is: ‘Object variable or With block variable not set.

  4. Mark Nelson (MS) says

    November 2, 2007 at 5:55 am

    Hi Chris,

    Note that starting in Visio 2003, the LengthIU and AreaIU properties take an argument indicating whether sub-shapes should be included. Also note that there are special exclusions for Data Graphic callouts as mentioned in the Visio 2007 help topics.

    http://msdn2.microsoft.com/en-us/library/ms196113.aspx

    Mark

  5. Visio Guy says

    November 6, 2007 at 10:05 pm

    Bryan,

    Are your quote marks the simple ones or the 66 99 quote marks? I noticed a problem when cutting from the blog and pasting in the VBA editor…

    – Chris

  6. Visio Guy says

    November 6, 2007 at 10:08 pm

    Mark

    Thanks for the tip on the arguments, don’t know how I missed those!

    And the info in the link is cool. Foks, it looks like that in general, Data Graphic sub-shapes won’t be included in the LengthIU and AreaIU calculations! See Mark’s link above for more info!

    – Chris

  7. John says

    March 5, 2009 at 7:56 pm

    This looks great, but I can’t quite get it to work. I get the old error 91, ‘Object variable or with block variable not set’ error here:

    shp = Visio.ActiveWindow.Selection(1)

    A few debugs show that there is an instance, and it can be assigned to shp if that is a Variant. But no joy if “Dim shp As Visio.Shape”

    Any tips appreciated.

  8. neilkeron says

    July 30, 2009 at 2:58 pm

    John
    I cant quite get it to work either.
    If I change the statement shp = visio.activewindow.selction(1) to Set shp =visio.activewindow.selection(1), I get past the first error but then I get error 91 at ln = liuShp.LengthIU(shp), presumably because something has gone wrong in the function. If anyone can throw any light on this I would be grateful.

    Regards
    Neil Keron

  9. stodds says

    March 2, 2011 at 1:02 pm

    Greetings,

    Were the issues raised here ever sorted? I am having trouble getting this to work too and not having used VB before ain’t helping!

    Cheers

    Steve

  10. Visio Guy says

    April 12, 2012 at 10:46 am

    LengthIU on open paths works properly in Visio 2010.

Leave a Reply Cancel reply

Primary Sidebar

Buy Text on a Circle Shape
Article — Video — Purchase

Categories

Buy my book!

Meta

  • Log in
  • Entries feed
  • Comments feed
  • WordPress.org

Tag Cloud

A/V Artistic Effects BPM Code Connectors Control Handles Countries Custom Patterns Custom Properties Data Graphics Data Linking Data Visualization David Edson David Parker Fill Format Formulas Functions Geometry Gradient Images Links Maps Multi-shapes Network Programming repeating shapes Resources Right-Click Actions Scale Shape Data ShapeSheet ShapeSheet Formulas ShapeSheet Functions SharePoint shiny SmartShapes Sport Sports Text Themes Tools Transparency User-defined Cells Visio 2007 Visio SmartShapes

Top Posts & Pages

  • - Visio Shapes & Stencils
  • - Visio Templates & Drawings
  • Dynamic Updating Org Charts in Visio!
  • Bubble Revision Shapes
  • Free Visio People Shapes
  • Amazon AWS Visio Shapes
  • Automatic Chevron Process Shape
  • Text to the Bottom of the Shape
  • Go 3D with Free Isometric Piping Shapes for Visio
  • Link Fields to Subshape Text

www.visguy.com - Visio Guy - since 2006