Making Shapes More Efficient: Using Fewer Shapes & Groups
You may have read our recent post on Cisco IP Telephone Shapes, and perhaps downloaded the shapes. While give-aways and freebies are nice, we at Visio Guy like to go further. We like look behind-the-scenes and talk about Visio development-related issues in order to give our readers a deeper understanding.
Before we released the Cisco IP Telephone shapes, we performed a number of tweaks to make them more efficient and better behaved. This article discusses a few of those operations–the whys and hows of making your shapes more efficient, and some tricks for ensuring proper resize behavior!
The Cisco Button Shapes
The shapes we’re talking about provide a nice way of documenting customizations to the Cisco IP Telephone software. This very nicely-drawn set of shapes were submitted by a Visio Guy reader, and look like this:
click to view larger image
A Developer’s Check-list
Not being content to leave well-enough alone, we examined the shapes, and ran them through a common release checklist to get them ready for distribution to our readers. This check-list aimed to acheive the following:
- Eliminate unnecessary grouping (ie: nested groups)
- Combine shapes with like-formatting into single (sub) shapes
- Ensure proper resize behavior
Items 1 and 2 had to do with the file-size efficiency, and item 3 had to do with maintaining graphical integrity when the shapes are resized. Today we’ll discuss items 1 and 2. Tomorrow we’ll cover item 3.
These shapes are well-built and precisely drawn, but when considering file-size, there exists a conflict between what’s good for the developer and what’s good for the end-user. This conflict is between ease-of-modification and shape-efficiency.
By using lots of groups and keeping shapes separate, it is easier for the designer to update and modify the shapes. But more groups and more shapes means more ShapeSheets, which means larger file sizes.
Two methods are used to reduce file size: getting rid of groups, and combining like-formatted shapes.
Flattening Group Structure
Getting rid of groups is a simple matter. Just select a shape and open the group-editing window and ungroup everything voraciously! More precisely, you can follow these steps:
- Open the group window via Edit > Open Group.
Note: if the shape is an instance of a master, then the menu item will show Open Master-name.
- Select all shapes, (shortcut: Ctrl + A)
- Ungroup a bunch of times by repeatedly pressing Ctrl + Shift + U.
While this isn’t really precise, but you can ungroup six or eight times really quickly with the keyboard shortcut. Hopefully you won’t have more levels of grouping than that!
You can detect shapes that have nested groups in a number of ways. One way is to look at the Drawing Explorer window, which you can shoe via the menu items: View > Drawing Explorer Window. If a shape has sub-shape-nodes that can be expanded, then the shape has extra levels of nesting that might not be necessary. Here’s an example of a shape with several levels of group-nesting:
After ungrouping all of the sub-shapes, Sheet.96 from above looks like this:
Now that is much simpler. You can see why we call it “flattening the group structure”!
Of course, there were too many shapes to analyze by hand. So I wrote some VBA code to detect shapes with sub-groups. The code draws a red rectangle around any shape that contains sub-groups. You can easily convert it to VB.NET, if that is the environment you are working with:
Sub MarkShapesWithSubGroups() Dim shp As Visio.Shape For Each shp In ActivePage.Shapes If m_shapeHasSubGroups(shp) Then Call m_markShape(shp) End If Next End Sub Private Function m_shapeHasSubGroups(ByRef shp As Visio.Shape) As Boolean Dim s As Visio.Shape For Each s In shp.Shapes '// Check the sub-shape's shape count. Greater than 0 '// means that it is a group: If s.Shapes.Count > 0 Then m_shapeHasSubGroups = True Exit Function End If Next m_shapeHasSubGroups = False End Function Private Sub m_markShape(ByVal shp As Visio.Shape) '// Draws a 'highlight' rectangle around a Visio shape: Dim l As Double, t As Double, r As Double, b As Double Dim rect As Visio.Shape Dim flags As Integer '// Get the bounds of shp, so we know how big to draw '// the rectangle: flags = Visio.VisBoundingBoxArgs.visBBoxUprightWH Call shp.BoundingBox(flags, l, b, r, t) rect = shp.ContainingPage.DrawRectangle(l, b, r, t) '// Give the shape no fill and a red outline: rect.Cells("Geometry1.NoFill").ResultIU = 1 rect.Cells("LineColor").Formula = "RGB(255,0,0)" End Sub
We could write code to automatically ungroup all sub-groups, but there are cases where sub-groups are legitimate.
For instance, sometimes sub-elements need to be rotated and translated. It’s far easier to position such shapes by placing them in a sub-group that can easily be rotated and translated, all at once. Without sub-groups, you’d have to enter trigonometric formulas all over the place to get the sub-shapes properly positions.
Now that we’ve flattened out the groups, it’s time to combine shapes. You can combine shapes using Visio’s Boolean Operations. Combining shapes creates a single shape, with a single ShapeSheet, but with multiple geometry sections. You can do a quick test to se what we’re talking about:
- Draw two rectangles
- Select them both
- Choose: Shape > Operations > Combine
You now have one single shape
- Choose: Window > Show ShapeSheet
Note that there are two Geometry sections with five rows each.
Combining shapes reduced the file size, because only one ShapeSheet (and all of its overhead) is required for multiple geometries. But all the shapes need to have the same line and fill formatting for this to make sense, since one shape can only have one set of formatting attributes.
In the case of our Cisco shapes, the fill formatting is almost always white, but some shapes have thicker lines than others. We can look at the Navigation button and compare the sub-shapes before and after combination:
We can see that the number of sub-shapes is dramatically reduced, AND we uncovered an extra square that wasn’t needed!
Before & After Statistics
Now that we’ve flattened out the sub-grouping and combined sub-shapes, let’s have a look at the sub-shape count of our Cisco shapes:
File-size-wise, after all the ungrouping and combining, the file was only reduced by 10 KB. (172 KB to 162 KB). So the shapes were pretty clean from the start, and you’ve got to wonder if I used my time wisely.
But I’ve seen other situations where shapes were very heavy with nested groups and separate shapes. Addressing items #1 and #2 on the checklist resulted in significantly smaller file sizes!
Also, in a practical application, these shapes will likely be converted to masters and instanced many times. For every copy of a shape on a page, we’ll have space savings. If several hundred shapes are on a page, the file-size savings might be more significant than we see here.
Combining Isn’t Always Appropriate
We’ve hinted that combining isn’t always feasible. It is instructive to point out reasons NOT to combine shapes:
- Different line formatting
- Different fill formatting
- Each shape has its own text
- Z-order requirements
The Z-order problem can be seen by looking at the green headphones. While the headphone elements are all black, they are built in such a way that combination isn’t possible for all elements. Each earpiece is made of two shapes: a green piece that obscures the under-lying headband, and a black piece that lays on top:
The shape on the right shows the structure by changing the colors: the blue earpiece elements lay on top of the black headband. The red bits are then on top of the blue. In each case, the left and right elements were combined together, but the whole mess couldn’t be because of the coloring and the z-ordering.
When making shapes smaller and faster, we run into a developer-versus-user quandry. In one state, the shapes are easier for the developer to create, modify and update. But that state is usually less efficient for the end-user.
The ideal situation would be to have source shapes that can easily be modified, then pass them through some sort of automation treatment that simplifies the shapes for release. We’ve seen some sample code that points us in this direction.
Unfortunately, some of the operations require human judgement (or way too much programming). Z-order issues are among these.
In another article, , we’ll continue with the Cisco series by examining what happens when the shapes get resized, and how we can improve that behavior. You can read about it here: Smart LineWeight: Bigger Shape, Thicker Lines.