In the first part of this post about using bitwise functions in Visio, I looked at the theory of how these functions work. In this part I’m going to put them to work to try and build a flexible shape.
A base shape
To get started I’ve built a generic style network switch shape as an example of how to change different elements. The elements I’m going to change happen to be colour and visibility, but they could just as easily be any other ShapeSheet cell that you want to effect.
As it stands, the shape has a number of different states that can be toggled from Shape Data and the context menu:
‘On’ (Front)
‘Off’ (Front)
‘Off’ (Back)
The key User cells look like this:
Note, those final three Socket sizing cells just allow the Socket sub-shapes to centre themselves correctly dependent on the socket count. I’m not going to go into how that works as it’s not the focus of this post.
As you can see from the above cells, I’ve implemented a couple of index cells (PowerStateIdx and DisplayFaceIdx) using the LOOKUP function, which read the associated Shape Data cells. The other boolean visibility cells (‘…Hidden’) look to these index cells to define their respective values and the sub-shapes, in turn, look at these cells to get theirs.
As an aside, I tend to name these visibility type cells as ‘hidden’ rather than ‘visible’ as most of the cells that will use their values (eg like Geometry1.NoShow) are negative in nature, and I find it just makes reading these cells little kinder to the eyes. Matching a negative value to TRUE, wrapped in a NOT function feels harder work somehow.
Extending the shape
So this is a fairly standard approach, but let’s suppose that you’ve been asked to extend this shape by allowing it to represent one of three products, where:
- Product ‘A’ has one rear socket
- Product ‘B’ has two rear sockets
- Product ‘C’ has three rear sockets
Here you could add a Shape Data row to hold the product name and a further User cell to hold the selected product index. Once you’ve got that, other cells (eg socket count) can reference this to derive their value. The result of these changes can be seen below:
But hold on – now you’ve been asked to extend the shape again, this time to include various LED configurations for the front panel and this is very likely to change, so here’s a possible candidate for bitwise functions.
If you add a further User cell to hold the LED flag value for the different products, then you can use another INDEX function to relate the correct flag value to each product:
You can then wire up the respective sub-shape geometries to point to the parent flags cell, using BITAND to compare the LED flags with value for each geometry:
So now, as long as you’ve set your flags to be the sum of the LEDs you want to include, then your shape should function correctly. Here’s a shot of the shape when set as Product ‘B’ where User.LEDFlags = 120 (8+16+32+64):
So you’ve now added your LED functionality and you can easily accommodate any future product spec changes if required by editing the User.LEDFlags formula.
It’s worth noting that this isn’t only way to solve the LED problem, but I think it’s a reasonably clear and flexible approach.
Multiple options
Your final task in building this shape is to allow the user to set various other product options that can be applied independently to all of the three products and to include a reset facility, which turns all options off.
There are three options to be included:
- Antenna
- Feet
- Speaker
Once you’ve added the associated sub-shapes, you can add another User cell named User.OptionFlags and the sub-shapes can look to this cell to determine their visibility in the same way you did for the LEDs.
Having a single cell means you can add and remove the various flags, both from within the ShapeSheet and via code. Not only that, but if this were a Shape Data row, you could use it in Link Data to Shapes and tie the options (or any other functionality) to a DataRecordset.
In this instance, though, you can add the above functionality via actions in the shape’s context menu. There’s an action row to be added for each of the three options and since you’ll want to toggle the option on and off, you can use an IF function in the formula parameter of the SETF function.
So the pertinent cells are set as follows:- first off the Actions.optSpeaker.Checked cell looks at the Option flags to see if it contains the Speaker flag:
=BITAND(User.OptionFlags,4)>0
then the Actions.optSpeaker.Action cell pushes one of two new values (formulae) back into the User.OptionFlags cell – the first removes the (4) flag from User.OptionFlags and the second adds it:
=SETF(GetRef(User.OptionFlags),
IF(Actions.optSpeaker.Checked,
BITAND(GetVal(User.OptionFlags),BITNOT(4)),
BITOR(GetVal(User.OptionFlags),4)))
The Antenna and Feet options work in exactly the same way and there’s a final Reset option that just clears all of the flags in one hit:
=SETF(GetRef(User.OptionFlags),0)
Worthwhile?
As per the LED functionality you could implement this without using bitwise functions, but you’d probably end up using chained SETFs if you wanted to set multiple options in one operation. That said, if you only have a few options then setting them in a more conventional manner might well be simpler. But, as the number of options grow so does, I believe, the usefulness of using these functions.
Numbers, names and cell references
One last issue that’s worth considering here is how clear and easy to understand your formulae are. For example, if you look a formula such as:
=BITAND(User.OptionFlags,4)>0
it’s not clear what you’re comparing User.OptionFlags with. Did ‘4’ mean Speaker, or was that Antenna? If you’re in the same ShapeSheet then you can scroll up to the definition that you might have jotted down in the Prompt cell, but if you’re in the ShapeSheet of a separate shape then you’ve lost the meaning and your comprehension of how the shape works is only as good as your memory.
This, of course, isn’t a problem that’s peculiar to bitwise functions as you can meet the same issue if you’re trying to reference an index in a list.
To deal with this problem, one option might be to give the flags meaning by giving them names. For example, given a couple of list cells:
User.Names = "Antenna;Feet;Speaker"
User.NameFlags = "1;2;4"
…you could lookup the correct flag using a string name:
=BITAND(User.OptionFlags,
INDEX(LOOKUP("Speaker",User.Names),
User.NameFlags))>0
I wouldn’t waste too much time trying to read that formula – it works, but it’s pretty horrid isn’t it? Not only does it use a string, which is more than likely going to change, but all the index and lookup functions make the whole formula much less easy to read.
Another option I’ve been trying out lately is to create named user cells to hold the individual flag values. For example, to solve the same problem you start by adding three user cells:
User.flgAntenna = 1
User.flgFeet = 2
User.flgSpeaker = 4
…the BITAND formula then looks a little more readable:
=BITAND(User.OptionFlags,User.flgSpeaker)>0
Above and beyond the main goal of making the formula easier to understand, this approach has a couple extra benefits:- a) if you decide to change the flg… cell name, then all of the dependent cells that use that name will be updated automatically and, b) you can easily see where those dependent cells are using the Formula Tracing window in the ShapeSheet.
So, in quick conclusion, bitwise functions might not be the perfect fit for all shapes, but if you find yourself faced with designing a shape a lot of different options, and particular non-mutually exclusive ones, then bitwise functions might help out.
If you want to download the above bitwise shape example, then you can get it in .vsdx and .vsd formats here:
Download Bitwise Switch Examples.zip (52.0K)