macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Developing Visualization Applications with Cocoa and VTK
Pages: 1, 2, 3

Introduction to the Cocoa in VTK

VTK is a cross-platform framework and a good thing too. But this means that things are not necessarily done in the way a Cocoa programmer might expect. So before writing code specific to our molecular viewer, we will need a few general classes to adapt VTK more to the Cocoa way of doing things.



In order to achieve portability, VTK is based on a system of abstract factories. Usually a VTK programmer would call the "New" method of the VTK class vtkRenderWindow, in order to create a new window to render in. A concrete subclass of vtkRenderWindow, in our case vtkCocoaRenderWindow, is created by New and returned; but the programmer doesn't need to know which subclass is used: she just interacts with the window via the methods of the abstract vtkRenderWindow class. In this way the code can be made portable. If the program is compiled and run on a different architecture, the New method will simply instantiate a different concrete subclass, and since all interaction with the window occurs via the methods of the abstract vtkRenderWindow class, the code will work without modification. Elegant, isn't it?

This is excellent object-oriented design and highly admirable, but we want to be a bit less admirable here. We want to be able to do things in the Cocoa way, creating a custom view in Interface Builder, dragging it into an NSWindow, connecting outlets and setting actions, the way we know and love. To achieve this, we have to change the implementation of a class called vtkCocoaWindow; thanks to the dynamism of Objective-C, we can do this without subclassing, simply by using a category.

Diagram.
UML diagram for the Cocoa-related classes in VTK. Inheritance is represented by an arrow, which points to the superclass. Other connectors represent associations between classes. Cocoa classes are enclosed in red boxes, VTK classes in blue boxes, and classes written here in green boxes

But first I probably should explain what vtkCocoaWindow is. vtkCocoaWindow is an Objective-C subclass of NSWindow. When the "New" method of the abstract vtkRenderWindow class is called, an instance of vtkCocoaRenderWindow gets created. The vtkCocoaRenderWindow, which is a C++ object, needs to be able to draw with the Cocoa framework, so it creates a Cocoa object for this purpose: vtkCocoaWindow. The vtkCocoaWindow has a single NSOpenGLView in it, of the subclass vtkCocoaGLView, and this is where VTK renders using OpenGL.

Phew, that was a mouthful. If this is a bit overwhelming, check out the VTK Cocoa Classes figure, which tells the same thousand words a little more concisely. And if that doesn't help, have a quick lie down and then proceed with the rest of the article, completely ignoring what you just read. If you don't care how it works, just that it does, you don't need to understand this explanation.

vtkCocoaWindow and vtkCocoaGLView are designed to work in close collaboration. If vtkCocoaWindow changes size, it resizes its vtkCocoaGLView as well. This is the way VTK expects things to be done. But what we would prefer is to have a single NSView subclass, which we can drop in any window in Interface Builder, and have everything work. So we basically want to keep vtkCocoaGLView and get rid of vtkCocoaWindow. Unfortunately, vtkCocoaWindow does some important stuff, so we can't get rid of it completely. But we can hide it in the background. We will change the methods of vtkCocoaWindow that don't fit with our aim to use vtkCocoaGLView as a standalone class by creating a category.

Categorizing vtkCocoaWindow

Due to the dynamism of Objective-C, it is possible to actually change the implementation of a class without subclassing, even without having access to the source code. We do have the source code in this case, but it would be nice not to have to change the official source of VTK, just extend it a bit. For this purpose, we will use a category.

Begin by selecting "New File..." from the File menu, and give it the name "vtkCocoaWindowModifications". Move the header and implementation files created to the Classes group. Now include the following code in vtkCocoaWindowModifications.h

#import <AppKit/AppKit.h>
#import "vtkCocoaWindow.h"

@interface vtkCocoaWindow (CocoaWindowModifications)
- (void)setvtkCocoaGLView:(vtkCocoaGLView *)thevtkCocoaGLView;
- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)proposedFrameSize;
- (BOOL)windowShouldZoom:(NSWindow *)sender toFrame:(NSRect)newFrame;
@end

As you can see, this is a category of the vtkCocoaWindow class called CocoaWindowModifications. Now add the implementation for these methods to the vtkCocoaWindowModifications.m file:

@implementation vtkCocoaWindow (CocoaWindowModifications)

// This has been overloaded to prevent the window making the vtkCocoaGLView its contentView.
- (void)setvtkCocoaGLView:(vtkCocoaGLView *)thevtkCocoaGLView {
myvtkCocoaGLView = thevtkCocoaGLView;
}

// This method, and the next one, have been changed to prevent them trying to
// to resize the render window.
- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)proposedFrameSize {
    return proposedFrameSize;
}

- (BOOL)windowShouldZoom:(NSWindow *)sender toFrame:(NSRect)newFrame {
    return YES;
}

@end

As indicated by the in-code comments, the implementation of the setvtkCocoaGLView: has been modified so that it does not attempt to set the vtkCocoaGLView as its content view. We don't want this here because our vtkCocoaGLView should behave like any other NSView. We want to be able to use the view in any NSWindow, or as the subview of any other NSView, and not be restricted to using it only as the content view of a vtkCocoaWindow.

The other two methods have been modified so that they no longer send messages to resize and position the vtkCocoaRenderWindow with which the vtkCocoaWindow is associated. Our objective is to transform the vtkCocoaWindow from a view object, which appears on the screen as a UI element, into a controller object, which is invisible to the user and whose role is simply to mediate between the vtkCocoaRenderWindow and vtkCocoaGLView objects. Because it is no longer a view object, we don't care about its size, and we don't want it to send geometry-related messages to the vtkCocoaRenderWindow.

Pages: 1, 2, 3

Next Pagearrow