It happened twice these past two weeks alone: I met clients discussing a new model and what they’d like to see in it. And both times, they asked for (quote!) “sexy” model animation and “intuitive” navigation, independently of each other. Apart from the choice of words, it seems people are tired of trying to figure out laboriously how to work with a model. This week, we examine two tricks to help them: we will create a navigation ribbon and figure out what on earth that weird agent in the corner is doing…
It is all there already
Have you ever opened an example model in AnyLogic? If not, open the “Help” menu and click on “Example Models”. Many models feature a unified user interface designed by AnyLogic developers. Check the “Airport with Two Terminals” model below. It has a blue ribbon at the top that allows users to navigate various parts of the model.
That ribbon is always visible, stays at the same position (no matter where you pan to) and provides a good visual clue as to what you are currently looking at. Have a play with that model navigation and check the various options.
How did they do it?
Let’s explore how this is done. The AnyLogic developers did not use magic but native AnyLogic objects and a bit of code. Hence, you can adapt what they provide and amend it to your own needs.
If you click on the ribbon on the Main (within the AnyLogic development environment), check the properties of that group of objects:
These properties relate to all graphical objects that belong to this group “groupMainMenu”, i.e. the rectangles and text objects that make up this group (more on these below). So what does that cryptic code for the X & Y position mean? Essentially, it continually calculates the difference between the top left corner of what the user can see and the top left corner of this group object. So whenever the user pans around the model (using the right mouse), our blue ribbon will stay at the top left corner of the screen. A neat little trick worth remembering.
The divisions done in the “Scale X/Y” fields ensure that the ribbon will not change its size when the user zooms into the model. The division essentially enforces that the ribbon zoom factor is always the reverse of what the user does, hence appearing to not change in size.
If you click on the “groupMainMenu” group once more, you should see the properties of individual rectangles that make up our ribbon, as below:
Here, the developers define what happens if a user actually clicks onto that rectangle area. They call a function called “setView” which can be found on the Main as well. Because this example model uses (expensive) 3D animation, the function is a little more complicated than need be. Let’s skip it for now and use a simpler solution as below:
Now, we tell AnyLogic to move the screen to a pre-defined object named “view3D” of type “View Area”. You can drag any number of these objects from the “Presentation” library. We will cover this object in more detail next time. For now, suffice to see that wherever you put a “View Area” object and call the “navigateTo()” method on it, the screen will jump to that location.
What is that guy doing?
So you got some nice ribbon and your client furiously navigates through various parts of the model. Well done. However, often you find this weird agent doing unexpected things. “What is he doing” you ask. Wouldn’t it be nice to just click on the little rebel and directly jump to its internals to see what is going on? Ask no more…
Below is an agent animated via a little black man (drawn as a PolyLine from the Presentation library). A bit of gibberish in the properties do the magic, as usual:
Each object from the Presentation library (and many others) have a property “on click”. The code here is executed if you click on an individual agent’s animation in the model. The code here moves the view of the user to this specific agent. This is done by providing the agent you clicked on to the setPresentable-method. In order to get the agent you just clicked on, we retrieve the index of the agent you clicked on (getIndex()) from the population of agents that this agent is part of (in this case it is the population “myPopulation” living one level up in “get_Main”.