Constraining Angle with the BOUND ShapeSheet Function
Cori who writes the blog Elliptical… wrote this nice little piece on constraining a shape’s angle so that it can only be rotated in 90-degree increments. I thought I’d relay the information to you here, and explain a bit more about the BOUND ShapeSheet function.
Although you can use the keyboard shortcuts Ctrl+R and Ctrl+L to rotate shapes in 90-degree increments clockwise and counterclockwise, this does nothing to actually constrain the allowable values.
Enter the BOUND ShapeSheet function
The shape at right is a compass dial shape that can only point to north, south, east or west. When you grab the shape’s green, lollipop rotation handle, it will snap nicely to one of these directions. The BOUND function makes this possible.
Even if a user tries to foil the constraint by typing an odd value into the Size & Position’s Angle field, the shape will still be rotated to the nearest 90-degree increment.
The magic is located in the shape’s Angle cell, located in the Shape Transform section of this shape’s ShapeSheet.
When you look at the Angle cell, you might be shocked. This is what you’ll see:
Angle = BOUND(0 deg, 0, 0, 0 deg, 0 deg, 0, 90 deg, 90 deg, 0, 180 deg, 180 deg, 0, 270 deg, 270 deg)
Anybody still there? It’s not so formidable if you break it down. Let’s add some line breaks to make it more digestible:
0, 0 deg, 0 deg,
0, 90 deg, 90 deg,
0, 180 deg, 180 deg,
0, 270 deg, 270 deg)
You can see right away that there are four triplets of information that correspond to our four allowable angles: 0 deg, 90 deg, 180 deg and 270 deg. So the west-constraining triplet would be:
0, 180 deg, 180 deg
The second two arguments could be described as the from-value and the to-value. Since we are constraining to specific points, these values are the same for each compass direction. In this example, we have from 180 deg and to 180 deg, which means: west.
The 0 that you see at the beginning of each triplet is an ignore parameter. If you set this to 1, then the constraint will be turned off. You could put various IF conditions or true/false flags in each individual ignore parameter to make a seriously whacky shape! Or you could reference all of the ignore flags to, say a User.debug cell, which would allow you to turn off the constraints to perhaps make development of the shape easier.
You’ll notice that the BOUND function doesn’t get deleted when you rotate the shape, and that GUARD is not used to accomplish this. BOUND is part of a new class of functions, first introduced in Visio 2003 that allow better coexistence between parameter-input and user-interaction. When you rotate the shape, the angle to which you rotate will be stored in the very first argument. So if I rotate the shape to -75.9 deg, the function will display as follows:
BOUND(-75.9 deg., …
The rest of the function will then work with that value to decide which constraint to use. So the user-interface generated angle is written inside of the function, without blasting the entire formula away! This is a a really cool concept that enables a lot of new possibilities for your shapes. The tyranny of GUARD is over!
We’ve almost forgotten the second argument, just after the value place-holder. This argument specifies the constraint type. The type can be one of three values: 0 = inclusive, 1 = exclusive, 2 = disabled. In our compass example, we have used 0, for inclusive constraints. Exclusive means “places where the value can’t go”, which adds interesting possibilites, and Disabled allows you to turn off the whole BOUND function. This could also be cool for debugging a shape.
If you’re interested in how the N, S, E, W shows in the text, have a look at the User-defined Cells section in the ShapeSheet. There, you’ll see two cells:
User.directions = “E;N;W;S”
User.direction = INDEX( INT( ANG360(Angle) / 90 deg ), User.directions )
The first cell is just a list of text to display. The second figures out which of those elements to use. By dividing the 360-degree-normalized angle by 90 degrees, the INT-ing it, we get a value of 0, 1, 2 or 3. This value is used to index one of the elements in the directions list.
The User.direction cell is then linked to the shape’s text using the Insert > Field dialog. Some gyrations were also performed in the Text Transform section to keep the text right-side-up as the shape rotates. Notably:
TxtAngle = -Angle
This causes the text to anti-rotate relative to the shape. A more complete formula is TxtAngle = IF(BITXOR(FlipX, FlipY), 1, -1)*Angle. This calculate the proper anti-angle even when the shape flips. But we could write many articles on text behavior, and probably will, so I’ll leave that for another time.
Hopefully you’ll find the BOUND function as fun and interesting as I have. Here’s the Visio file for download:
We talked about how BOUND doesn’t get overwritten by user-interface actions. Here’s some links to the MSDN Visio 2003 SDK Documentation for ShapeSheet functions that support this “don’t overwrite me” behavior: