One of my subscribers asked me if I can make an article about zooming containers using viz3 script. I realized that there is rather poor documentation about implementing that kind of things. So I decided to create this short tutorial about dragging and zooming containers to fill this gap.

Goal

Our goal is to create a viz3 script which will allow user to drag certain container and zoom it in/out. While dragging is rather easy to implement, zooming is a little bit more complicated. But don’t worry – it’s a very little bit :-). The first step in our work here is to decide how dragging and zooming should happen. In this tutorial I’ll show one of many approaches to this problem. Some of them may be more efficient, some may be simpler to implement or more universal. I decided to show you this particular one because it shows almost every aspect of our problem.

Lets start with the globals

The script we’re constructing will behave as follows:

  1. it will have three modes: mode 0 – dragging, mode 1 – zooming in and mode 2 – zooming out
  2. user will be able to change script mode
  3. it will have “Active Container” parameter exposed which will be the container to drag or zoom
  4. when in drag mode user will be able to drag “Active Container” using mouse (or touchscreen)
  5. when in one of zooming modes (1 or 2) user will be able to zoom “Active Container” by pressing it and holding
  6. when in one of zooming modes (1 or 2) user will be able to zoom into any area of “Active Container”

Having said that let’s have a look at global variables that will be used in our script.

' following parameter will contain reference
' to selected container
Dim active_cont as Container
' following parameter will define
' how fast zooming will take place
Dim zoom_speed as Double
' following will be the upper boundry
' of container's scale
Dim max_scale as Double
' following will be the lower boundry
' of container's scale
Dim min_scale as Double
' following parameter will determine
' in which mode script is currently working
Dim mode as Integer = 0

' following variable will indicate
' if container should be zoomed
Dim zoom_enabled as Boolean = false
' following variable will indicate
' if container should be dragged
Dim drag_enabled as Boolean = false

' following is a constant
Dim reference as Double = 1.0
' following will indicate if zooming or dragging
' can be calculated
Dim center_picked as Boolean = false

Most of these globals speak for themselves. One thing worth mentioning though is the last variable – “center_picked”. To understand what exactly it is for, we need to go through some of the theory.

A little bit of theory

In viz|artist when we change containers scale, the container expands/contracts in directions defined by its center. By default container’s center point is set in the middle. So when we’re changing its scale, for example zooming it in, it expands in all directions equaly. That behaviour is good and expected in most of the cases. But when we want to allow user to zoom into some part of the container it becomes very annoying. Luckily, once we understand how scaling works, it becomes obvious we have to manipulate container’s center to achieve certain effect.

Figure below ilustrates how we should change containers center point while scaling it.

Positioning containers center

Positioning containers center

Repositioning of center point

Actually implementation is very simple. Lets construct “PickCenter” function, which will move container’s center point.

Function PickCenter(mouse_position as Vertex) as Boolean
  Dim off_x = active_cont.position.x - (-active_cont.center.x * active_cont.scaling.x)
  Dim off_y = active_cont.position.y - (-active_cont.center.y * active_cont.scaling.y)
  Dim cx = (mouse_position.x - off_x) / active_cont.scaling.x
  Dim cy = (mouse_position.y - off_y) / active_cont.scaling.y  

  SendCommand("#" & active_cont.VizId & "*TRANSFORMATION*CENTER SET " & CStr(cx) & " " & CStr(cy) & " 0.0")
  PickCenter = True
End Function

As you may have noticed I used SendCommand method to set center point position. You may have also noticed that in second and third line I aquired center point x and y coordinate from container using scripting object. The question is why didn’t I assign new values using scripting object and instead used SendCommand method? The answer is quite disturbing: I do not know why, but when I assign center point position using scripting object the container sort of jumps while zooming in. This unexpected behaviour doesn’t occur when I do this using SendCommand method. I really don’t know why it happens but it happens. Maybe accessing scripting object is slower than using viz commands? Who knows.

UPDATE: Thanks to Florian Brandl from vizrt who pointed me that there is a SetCenterPositionLocked() method now for a Container object I suggest a small update for the code above. Instead of line with SendCommand function we can now put this:

	Dim cVert as Vertex
	cVert.X = cx
	cVert.Y = cy
	cVert.Z = 0.0

	active_cont.SetCenterPositionLocked(cVert)

Once we have our function we need to put it somewhere. Since we want to zoom when user presses mouse button (touch screen) the obvious choice would be MouseDown event handler. I recomend using 6DOF events. Lets take a look how I made it.

Sub OnButtonDown6DOF(button As Integer, pos As Vertex, rot As Vertex)
  If (active_cont <> Null) Then
    If (active_cont.ContainsMouseCursor() And (center_picked = False)) Then
	'now calculate the center and start the scaling
	center_picked = PickCenter(pos)    

        If (mode = 1) or (mode = 2) Then
          zoom_enabled = True
	End If
    End If
  End If
End Sub

Sub OnButtonUp6DOF(button As Integer, pos As Vertex, rot As Vertex)
  zoom_enabled = false
  center_picked = false
End Sub

Notice the OnButtonUp6DOF event handler. It stops the zooming once mouse button is not pressed any more.

Zoom implementation

Changing containers scale is very simple. All we have to do is calculate scale value and assign it to container. We can do this in several ways, I’m going to show you one of them. This is how I’m defining new scale for zooming in:

new_scale = current_scale * (zoom_speed + reference)

and for zooming out:

new_scale = current_scale / (zoom_speed + reference)

reference should always be equal to 1. The zoom_speed parameter on the other hand should be a floating point number determining how fast container’s scale will change in time. In my experiance best results can be achieved when zoom_speed is somewhere between 0.01 and 0.1.

When changing scale we should remember to check If new scale is within boundries defined by min_zoom and max_zoom. Lets take a look at implementation.

Sub OnExecPerField()
  If (active_cont <> Null) Then

    ' zooming in
    If (mode = 1 And center_picked And zoom_enabled) Then
      If active_cont.scaling.x * ( zoom_speed + reference) <= max_scale Then
        active_cont.scaling.x *= ( zoom_speed + reference)
        active_cont.scaling.y *= ( zoom_speed + reference)
      End If
    End If

    ' zooming out
    If (mode = 2 And center_picked And zoom_enabled) Then
      If active_cont.scaling.x / (zoom_speed+reference) >= min_scale Then
        active_cont.scaling.x /= (zoom_speed+reference)
        active_cont.scaling.y /= (zoom_speed+reference)
      End If
    End If
  End If
End Sub

Drag implementation

We have successfully implemented zooming functionality. Dragging is even simpler to implement. As you probably suspect everything regarding dragging will be placed in OnMouseMove event handler. And you are right. But first lets modify OnButtonDown and OnButtonUp events to enable and disable dragging.

Sub OnButtonDown6DOF(button As Integer, pos As Vertex, rot As Vertex)
  If (active_cont <> Null) Then
	  If (active_cont.ContainsMouseCursor() And (center_picked = False)) Then
		'now calculate the center and start the scaling or zooming
		center_picked = PickCenter(pos)

	  If (mode = 0) Then
	      drag_enabled = true
    ElseIf (mode = 1) or (mode = 2) Then
	      zoom_enabled = True
	    End If
	  End If
  End If
End Sub

Sub OnButtonUp6DOF(button As Integer, pos As Vertex, rot As Vertex)
  drag_enabled = false
  zoom_enabled = false
  center_picked = false
End Sub

When we talk about dragging we mean changing containers position to current mouse position. Since mouse move events provide us with mouse position, implementation is really simple.

Sub OnMove6DOF(button As Integer, pos As Vertex, rot As Vertex)
  If drag_enabled And center_picked And (active_cont <> null) Then
    active_cont.position.x = pos.x
    active_cont.position.y = pos.y
  ElseIf zoom_enabled Then
    ' in addition also calculate new center point position for zooming
    center_picked = PickCenter(pos)
  End If
End Sub

And that’s basically it :-).

Conclusion

We have created a script that allows users to drag and zoom specific container. One thing I haven’t mentioned before is that this script will only work if placed on a container which is directly or indirectly parenting “Active Container”. If “Active Container” is placed somewhere else in scene tree – it won’t work. As an attachement to this article I put archived scene in Download section, so you can play with it. Have fun and I hope you find it usefull. It’ll be great if you put some comments telling me if you liked it or not, what could be made differently or just asking some questions about this article. See you next time :-).

GHTime Code(s): ebe4c b3826