Cocoa for Scientists (Part XV): Continuing 3D Visualization
Author: Drew McCormack
Website: http://www.maccoremac.com
Last time, we started a project to build a simple 3D visualization app using Cocoa and VTK. This time, we are going to finish off the source code for that project by adding some controller code. Next time, we will define the interface in Interface Builder.
Adding a Simple Model
The application we are developing, Animoltion, performs an animated visualization of three atoms. To give you a feel for where we are headed, the interface should look something like the screenshot below when finished.

We will fix the number of atoms visualized to 3. (Extending this to an arbitrary number of atoms is left as an exercise.) Each atom is defined by a position in space, a radius, and a velocity. We will use a simple C struct to store this data, rather than a full-blown class.
To add a file for the Atom struct code, follow this procedure:
- Open the Animoltion Xcode project that you used last time, or download it and open it in Xcode.
- Select the Classes group in the Groups & Files list on the left, and choose File > New File…
- Select ‘Empty File in Project’ as the file type, and click Next button.
- Type ‘Atom.h’ as the file name, and click the Finish button.
Now select the Atom.h file in the Groups & Files tree so that you can edit it in the editor on the right. Add the following code:
typedef struct _Atom {
float coords[3];
float velocity[3];
float radius;
} Atom;
// Initialize an Atom
inline Atom MakeAtom(
float c1, float c2, float c3,
float v1, float v2, float v3,
float radius ) {
Atom newAtom;
newAtom.coords[0] = c1;
newAtom.coords[1] = c2;
newAtom.coords[2] = c3;
newAtom.velocity[0] = v1;
newAtom.velocity[1] = v2;
newAtom.velocity[2] = v3;
newAtom.radius = radius;
return newAtom;
}
An inline function has been included to create a new Atom instance. This is merely a convenience.
Adding a Controller Class
With the model layer finished, we now need a controller class to mediate between the interface elements, and the model instances. You could just have your controller derive from NSObject, but we will use an NSDocument subclass. NSDocument is, as the name suggests, a class that represents documents. A document-based application, like Animoltion, can have multiple documents open at a time, and can create new ones. Typically, you would use NSDocument to store information on file, but in Animoltion we don’t need this functionality. Instead, we just use NSDocument as a convenient means of supporting multiple open windows, with different content in each.
When we created our project last time, we chose the Document-based Application option. This Xcode template generates an NSDocument subclass stub for you, and calls it ‘MyDocument’. We need to change that name in various places in the project, and in Interface Builder. The first step is simply to do a textual search and replace. Go to the Project view in the Xcode window (first tab if you are using the All-in-One layout), and click the Project Find tab on the right. (If you can’t see the Project Find tab, it could be that you need to drag the split view down to reveal it.)

To replace ‘MyDocument’ with ‘MoleculeDocument’:
- Enter ‘MyDocument’ in the Find field, and make sure the popup button is set to Project, to just search in the project files.
- Click the Find button.
- Enter ‘MoleculeDocument’ in the Replace field.
- Click the Replace button, and when prompted if you really want to replace the text, click the Replace button.
That will replace any occurrences of ‘MyDocument’ in the text, but doesn’t change file names, so
- Open the Classes group in the Groups & Files list view.
- Option click on MyDocument.h and MyDocument.m to change their names to MoleculeDocument.h and MoleculeDocument.mm. (Note again the .mm extension for Objective-C++)
- Repeat this for the MyDocument.nib file, which is in the Resources group.
We can now add the source code for the MoleculeDocument class. Add the following in the header file:
#import <Cocoa/Cocoa.h>
#import "Atom.h"
#define id Id
#import "vtkSphereSource.h"
#import "vtkDataSetMapper.h"
#undef id
#define NUMBER_OF_ATOMS 3
#define NUMBER_OF_BONDS 3
@class BasicVTKView;
@interface MoleculeDocument : NSDocument
{
IBOutlet BasicVTKView *vtkView;
IBOutlet NSButton *playStateButton;
BOOL isPlaying;
vtkSphereSource *sphereSource;
vtkDataSetMapper *sphereMapper;
NSTimer *animationTimer;
Atom atoms[NUMBER_OF_ATOMS];
float stiffnessFactor;
}
-(IBAction)togglePlayState:(id)sender;
-(IBAction)updateStiffness:(id)sender;
-(void)addSphereActorWithRadius:(float)radius
red:(float)r green:(float)g blue:(float)b alpha:(float)a;
-(void)updateAtomPositions;
-(void)updateActors;
-(void)displayNextFrame;
@end
The interface includes a few outlets to items in the interface; we will connect those up a bit later in Interface Builder. There is also an NSTimer, to animating the visualization; an array of atoms; a parameter for the stiffness of the ‘springs’ between the atoms (ie stiffnessFactor); and some VTK objects (ie vtkSphereSource and vtkDataSetMapper). The latter will be used to supply the data to draw spheres for the atoms.
The methods include some actions for the interface, to play and pause the animation, and to update the spring stiffness parameter, as well as methods related to adding actors (eg spheres) to the VTK view, and updating the positions of the actors as time passes.
The implementation of MoleculeDocument is quite extensive, but much of it is simply elementary physics related to propagating the positions of the atoms — assuming harmonic springs between each — as a function of time.
#import "MoleculeDocument.h"
#import "BasicVTKView.h"
#import "Atom.h"
#define id Id
#import "vtkProperty.h"
#import "vtkActor.h"
#import "vtkPolyData.h"
#import "vtkInteractorStyleSwitch.h"
#import "vtkCocoaRenderWindowInteractor.h"
#import "vtkSmartPointer.h"
#undef id
static const float ANIMATION_TIME_STEP = 0.03;
static const float PROPAGATION_TIME_STEP = 0.20;
static const unsigned SPHERE_RESOLUTION = 16;
@implementation MoleculeDocument
-(void)setupvtkView
{
vtkSmartPointer<vtkInteractorStyleSwitch> intStyle =
vtkSmartPointer<vtkInteractorStyleSwitch>::New();
intStyle->SetCurrentStyleToTrackballCamera();
[vtkView getInteractor]->SetInteractorStyle(intStyle);
sphereSource = vtkSphereSource::New();
sphereSource->SetThetaResolution(SPHERE_RESOLUTION);
sphereSource->SetPhiResolution(SPHERE_RESOLUTION);
sphereMapper = vtkDataSetMapper::New();
sphereMapper->SetInput(sphereSource->GetOutput());
isPlaying = NO;
[vtkView setNeedsDisplay:YES];
}
#pragma mark -
-(void)dealloc {
[animationTimer invalidate];
sphereSource->Delete();
sphereMapper->Delete();
[super dealloc];
}
-(NSString *)windowNibName
{
return @"MoleculeDocument";
}
-(NSData *)dataRepresentationOfType:(NSString *)aType
{
return nil;
}
- (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)aType
{
return YES;
}
-(void)awakeFromNib {
[self setupvtkView];
animationTimer = [NSTimer scheduledTimerWithTimeInterval:ANIMATION_TIME_STEP
target:self
selector:@selector(displayNextFrame)
userInfo:nil
repeats:YES];
stiffnessFactor = 1.0;
// Initialize atom positions
atoms[0] = MakeAtom( 0.0, 0.0, 0.0, 0.3, 0.0, 0.0, 0.5 );
[self addSphereActorWithRadius:atoms[0].radius
red:0.7 green:0.0 blue:0.0 alpha:1.0];
atoms[1] = MakeAtom( 1.0, 0.0, 0.0, -0.3, 0.0, 0.2, 0.7 );
[self addSphereActorWithRadius:atoms[1].radius
red:0.0 green:0.7 blue:0.0 alpha:1.0];
atoms[2] = MakeAtom( 0.0, 1.0, 0.0, 0.0, 0.0, -0.2, 0.3 );
[self addSphereActorWithRadius:atoms[2].radius
red:0.0 green:0.0 blue:0.7 alpha:0.5];
[self updateActors];
[vtkView setNeedsDisplay:YES];
}
-(IBAction)togglePlayState:(id)sender {
isPlaying = !isPlaying;
}
-(IBAction)updateStiffness:(id)sender {
stiffnessFactor = [sender floatValue];
}
-(void)addSphereActorWithRadius:(float)radius
red:(float)r green:(float)g blue:(float)b alpha:(float)a{
vtkActor *sphereActor = vtkActor::New();
sphereActor->SetMapper(sphereMapper);
sphereActor->GetProperty()->SetColor(r, g, b);
sphereActor->GetProperty()->SetOpacity(a);
sphereActor->GetProperty()->SetInterpolation( VTK_GOURAUD );
sphereActor->SetScale(radius, radius, radius);
[vtkView getRenderer]->AddActor(sphereActor);
sphereActor->Delete();
}
-(void)displayNextFrame {
[self updateAtomPositions];
[self updateActors];
[vtkView setNeedsDisplay:YES];
}
-(void)updateAtomPositions {
if ( !isPlaying) return;
float bondEquilibriumLengths[NUMBER_OF_BONDS] = { 1.0, 1.5, 2.0};
float bondStiffnesses[NUMBER_OF_BONDS];
bondStiffnesses[0] = 0.5 * stiffnessFactor;
bondStiffnesses[1] = 1.0 * stiffnessFactor;
bondStiffnesses[2] = 0.1 * stiffnessFactor;
int bondConnectivityFirstAtom[NUMBER_OF_BONDS] = { 0, 0, 1 };
int bondConnectivitySecondAtom[NUMBER_OF_BONDS] = { 1, 2, 2 };
// Calculate the coordinate differences and bond lengths for each bond.
float bondCoordDifferences[NUMBER_OF_BONDS][3];
float bondLengths[NUMBER_OF_BONDS] = { 0.0, 0.0, 0.0 };
unsigned coordIndex, bondIndex;
for ( bondIndex = 0; bondIndex < NUMBER_OF_BONDS; ++bondIndex ) {
unsigned firstAtomIndex = bondConnectivityFirstAtom[bondIndex];
unsigned secondAtomIndex = bondConnectivitySecondAtom[bondIndex];
for ( coordIndex = 0; coordIndex < 3; ++coordIndex ) {
bondCoordDifferences[bondIndex][coordIndex] =
atoms[firstAtomIndex].coords[coordIndex] - atoms[secondAtomIndex].coords[coordIndex];
bondLengths[bondIndex] += pow( bondCoordDifferences[bondIndex][coordIndex], 2 );
}
bondLengths[bondIndex] = sqrt( bondLengths[bondIndex] );
}
// Update coordinates and velocities.
float temp;
for ( bondIndex = 0; bondIndex < NUMBER_OF_BONDS; ++bondIndex ) {
unsigned firstAtomIndex = bondConnectivityFirstAtom[bondIndex];
unsigned secondAtomIndex = bondConnectivitySecondAtom[bondIndex];
temp = -PROPAGATION_TIME_STEP * bondStiffnesses[bondIndex] *
( bondLengths[bondIndex] - bondEquilibriumLengths[bondIndex] ) /
bondLengths[bondIndex];
for ( coordIndex = 0; coordIndex < 3; ++coordIndex ) {
atoms[firstAtomIndex].velocity[coordIndex] +=
temp * bondCoordDifferences[bondIndex][coordIndex];
atoms[secondAtomIndex].velocity[coordIndex] -=
temp * bondCoordDifferences[bondIndex][coordIndex];
}
}
unsigned atomIndex;
for ( atomIndex = 0; atomIndex < NUMBER_OF_ATOMS; ++atomIndex ) {
for ( coordIndex = 0; coordIndex < 3; ++coordIndex ) {
atoms[atomIndex].coords[coordIndex] +=
PROPAGATION_TIME_STEP * atoms[atomIndex].velocity[coordIndex];
}
}
}
-(void)updateActors {
vtkRenderer *renderer = [vtkView getRenderer];
vtkActor *actor;
vtkActorCollection *coll = renderer->GetActors();
int actorIndex;
for ( actorIndex = 0; actorIndex < coll->GetNumberOfItems(); ++actorIndex) {
actor = (vtkActor *)coll->GetItemAsObject(actorIndex);
actor->SetPosition(
atoms[actorIndex].coords[0],
atoms[actorIndex].coords[1],
atoms[actorIndex].coords[2]);
}
}
@end
Dissecting MoleculeDocument
Much of the interesting code is in the setupvtkView method.
-(void)setupvtkView
{
vtkSmartPointer<vtkInteractorStyleSwitch> intStyle = vtkSmartPointer<vtkInteractorStyleSwitch>::New();
intStyle->SetCurrentStyleToTrackballCamera();
[vtkView getInteractor]->SetInteractorStyle(intStyle);
sphereSource = vtkSphereSource::New();
sphereSource->SetThetaResolution(SPHERE_RESOLUTION);
sphereSource->SetPhiResolution(SPHERE_RESOLUTION);
sphereMapper = vtkDataSetMapper::New();
sphereMapper->SetInput(sphereSource->GetOutput());
isPlaying = NO;
[vtkView setNeedsDisplay:YES];
}
VTK has a number of different options for how the user interacts with the visualization; you set the one you want using a vtkInteractorStyleSwitch. In the first few lines of this method, a new vtkInteractorStyleSwitch is created, is set to use a trackball-type interaction, and then passed to the vtkInteractor object belonging to the VTK view.
If you are wondering about what that vtkSmartPointer is all about, don’t worry too much: it is a bit like calling autorelease in Cocoa; it will automatically result in the retain count of the vtkInteractorStyleSwitch being reduced by one when the method completes. If the C++ template notation scares you, you can simply remove the vtkSmartPointer and call Delete at the end of the routine on the vtkInteractorStyleSwitch object intStyle.
The next code block in the setupvtkView method creates a vtkSphereSource, which supplies the data coordinates used to draw a sphere out of polygons. The vtkDataSetMapper object maps the coordinates of this sphere to 3D graphics primitives like polygons for rendering in the 3D VTK view. The method finishes by calling the Cocoa setNeedsDisplay: method, which tells the VTK view to redraw itself when convenient.
The model Atom objects are created in the awakeFromNib method.
-(void)awakeFromNib {
[self setupvtkView];
animationTimer = [NSTimer scheduledTimerWithTimeInterval:ANIMATION_TIME_STEP
target:self
selector:@selector(displayNextFrame)
userInfo:nil
repeats:YES];
stiffnessFactor = 1.0;
// Initialize atom positions
atoms[0] = MakeAtom( 0.0, 0.0, 0.0, 0.3, 0.0, 0.0, 0.5 );
[self addSphereActorWithRadius:atoms[0].radius red:0.7 green:0.0 blue:0.0 alpha:1.0];
atoms[1] = MakeAtom( 1.0, 0.0, 0.0, -0.3, 0.0, 0.2, 0.7 );
[self addSphereActorWithRadius:atoms[1].radius red:0.0 green:0.7 blue:0.0 alpha:1.0];
atoms[2] = MakeAtom( 0.0, 1.0, 0.0, 0.0, 0.0, -0.2, 0.3 );
[self addSphereActorWithRadius:atoms[2].radius red:0.0 green:0.0 blue:0.7 alpha:0.5];
[self updateActors];
[vtkView setNeedsDisplay:YES];
}
A spherical actor is created for each one, to represent the atom in the 3D VTK view. These are added using the addSphereActorWithRadius:red:green:blue:alpha method. As the name suggests, this adds a sphere to the view, with the radius and the RGBA values passed. The code to do that looks like this
-(void)addSphereActorWithRadius:(float)radius red:(float)r green:(float)g blue:(float)b alpha:(float)a{
vtkActor *sphereActor = vtkActor::New();
sphereActor->SetMapper(sphereMapper);
sphereActor->GetProperty()->SetColor(r, g, b);
sphereActor->GetProperty()->SetOpacity(a);
sphereActor->GetProperty()->SetInterpolation( VTK_GOURAUD );
sphereActor->SetScale(radius, radius, radius);
[vtkView getRenderer]->AddActor(sphereActor);
sphereActor->Delete();
}
By setting the mapper of the new vtkActor to the vtkDataSetMapper that was created earlier, the actor will be a sphere shape. This method uses the RGBA values passed to set the properties of the actor, and finally adds it to the VTK view’s vtkRenderer, which renders it to the screen.
The one important aspect of the MoleculeDocument class that we have not yet touched upon is animation. An NSTimer is started in awakeFromNib to invoke a particular method at regular time intervals:
animationTimer = [NSTimer scheduledTimerWithTimeInterval:ANIMATION_TIME_STEP
target:self
selector:@selector(displayNextFrame)
userInfo:nil
repeats:YES];
The target: argument is the object that will be messaged when the timer fires, and the selector: argument is the method actually invoked. repeats: tells the NSTimer to keep firing until it is invalidated (in dealloc), rather than just firing once and stopping.
When the timer fires, it invokes displayNextFrame method:
-(void)displayNextFrame {
[self updateAtomPositions];
[self updateActors];
[vtkView setNeedsDisplay:YES];
}
This updates the positions of the atoms, matches the positions of the actors to the Atoms, and finally marks the view as needing a redraw. The updateAtomPositions method is quite long, but has more to do with high-school physics than VTK, so I won’t discuss it here. updateActors sets the position of the spherical actors with the SetPosition method, after first retrieving them from the renderer.
-(void)updateActors {
vtkRenderer *renderer = [vtkView getRenderer];
vtkActor *actor;
vtkActorCollection *coll = renderer->GetActors();
int actorIndex;
for ( actorIndex = 0; actorIndex < coll->GetNumberOfItems(); ++actorIndex) {
actor = (vtkActor *)coll->GetItemAsObject(actorIndex);
actor->SetPosition(
atoms[actorIndex].coords[0],
atoms[actorIndex].coords[1],
atoms[actorIndex].coords[2]);
}
}
Build and Go to Next Week
This tutorial is already getting quite extensive, so we’ll leave it here for now, and continue on next time. You should be able to build again to make sure everything has gone well. But before you do, you should turn off Zero Link, because I found that it caused problems.
- Double click the Animoltion project icon at the root of the Groups & Files list view.
- In the Info window, select the Build tab.
- Choose Debug in the Configuration popup button.
- Locate ZeroLink in the settings, and make sure it is unchecked. (You can use the filter box to narrow your search for ZeroLink.)
The source code to this point can be downloaded here. In the next, and last (promise!), tutorial, we’ll delve into Interface Builder and get Animoltion animating.



Comments
why VTK?
I can't exactly say that I work with visualization, but I certainly do use complex 3D graphics to visualize and animate a number of physical and chemical phenomena. I'm already comfortable with OpenGL and GLSL. I would have done this same excersize with several GLUT Spheres and use similar methods for translation, and animation. I'm trying to figure out if there is anything that VTK can offer that is more dificult in OpenGL. What would be the advantage of using VTK? Learning curve? Speed of development?
thanks for this entire series.
Justin Mitchell
Why VTK?
Hi Justin,
Of course, I have chosen a very simple example, one that is quite simple to do in OpenGL, since it only works with spheres. But VTK has much more, and it is nearly all about visualizing data. OpenGL just gives you the primitives to draw with; VTK tells you what to draw. It's like the difference between a 2D graphics engine like Quartz 2D, and a plotting framework. The plotting framework sits on top of the drawing engine.
Just to get you a bit of an idea of some of the things you can do with VTK, take the example of a scalar field or density. With maybe 20 lines of VTK code, you can generate an isosurface or volume rendering of the data, complete with user interaction (rotating, translating etc). With another 20 lines or so, you could add a cut plane to it. The point is not that you can't do this with OpenGL yourself, but it will take you thousands of line. Effectively you will be implementing the same sorts of algorithms (eg marching cubes) that are already built into VTK.
Hope that clarifies things.
Drew
---------------------------
Drew McCormack
http://www.maccoremac.com
http://www.macanics.net
http://www.macresearch.org
Re: Why VTK? Ah ha.
AAahhh. That does clarify. Sounds like a much more useful tool than I thought.
Justin
cool tut!
thanks for the tut. can't wait for the next one!
i have a question of a different nature though.
you know the inspector window of keynote or the system preference window...
when you push a button, a new view comes up. how is that done?
i don't suppose it's a "hide everything not related if this button is pushed".
if it's so, drawing the interface in IB is going to be really messy.
Tab View
Hi Ken,
You are best off using an NSTabView. You can put a different view in each tab.
If you want to customize the buttons, like in keynote, just create an NSTabView with the labels hidden. Then connect up your buttons such that when you press one, you select the appropriate tab in the tab view.
Hope that helps,
Drew
---------------------------
Drew McCormack
http://www.maccoremac.com
http://www.macanics.net
http://www.macresearch.org
VTK Tutorial still working for XCode 2.4 on Intel Macs?
It is great to see tutorial's like this so those of us who aren't professional programmers can take advantage of libraries like VTK. Thanks!
I am having some problems getting the second and third sections of this tutorial to run though. The first one ran without problems.
I get a number of linker warnings like:
/usr/bin/ld: warning /Users/AV/Develop/VTK/VTKBin/lib/libvtkCommon.a archive's cputype (7, architecture i386) does not match cputype (18) for specified -arch flag: ppc (can't load from it)
Followed by the error:
/usr/bin/ld: Undefined symbols:
vtkProperty::SetColor(double, double, double)
vtkRenderer::AddActor(vtkProp*)
vtkRenderer::GetActors()
vtkCollection::GetItemAsObject(int)
vtkCollection::GetNumberOfItems()
vtkSphereSource::New()
...
/Users/AV/Develop/Animoltion/build/Animoltion.build/Release/Animoltion.build/Objects-normal/ppc/BasicVTKView.o reference to undefined .objc_class_name_vtkCocoaGLView
/Users/AV/Develop/Animoltion/build/Animoltion.build/Release/Animoltion.build/Objects-normal/ppc/BasicVTKView.o reference to undefined vtkRenderer::New()
/Users/AV/Develop/Animoltion/build/Animoltion.build/Release/Animoltion.build/Objects-normal/ppc/BasicVTKView.o reference to undefined vtkRenderWindow::New()
/Users/AV/Develop/Animoltion/build/Animoltion.build/Release/Animoltion.build/Objects-normal/ppc/BasicVTKView.o reference to undefined vtkRenderWindowInteractor::SetRenderWindow(vtkRenderWindow*)
/Users/AV/Develop/Animoltion/build/Animoltion.build/Release/Animoltion.build/Objects-normal/ppc/BasicVTKView.o reference to undefined vtkRenderWindowInteractor::New()
collect2: ld returned 1 exit status
Any suggestions?
AV
Build Config?
It seems you are trying to build a universal build, but the VTK libraries you have are only for intel mac. If you build using the Debug configuration, it should only build for i386 and work fine. If you build the Release build, it will try to build for ppc as well, and this will fail. You can remove PPC from the release build by double clicking on the Animoltion target, clicking on the Build tab in the inspector, and double clicking the Architecture setting. You should get a sheet with Intel and PPC available. Uncheck PPC and try again.
Drew
---------------------------
Drew McCormack
http://www.maccoremac.com
http://www.macanics.net
http://www.macresearch.org
building with XCode 3.1 under Leopard
Just wanted to point out that to build the current project code under Leopard, I found I had to set the Base SDK in the project's "info" panel to 10.5
I had built VTK under 10.5, and when the Animoltion project is set to use 10.4, you get some link errors on build.
Otherwise it all seems to work.
Jim Meiss
stops at [vtkView getInteractor]->SetInteractorStyle(intStyle);
hi drew,
it's been a while. i went through the tutorial, but the program gets stuck at
[vtkView getInteractor]->SetInteractorStyle(intStyle);
in setupvtkView.
i'm using 10.5.5 and vtk5.2. it compiles fine. just doesn't seem to run...
build and run
did you manage to get it to build and run?
multiple actors
you added the positions of the atoms with actor->SetPosition(...)
but if you have multiple actors of different types, lines and spheres, how would you add them to the renderer? the vtk documentation is pretty daunting and i just looking for a simple example that showed how to do this. most examples are just of one actor. even for multiple actors, i couldn't understand how all the positions for the actors are set.... kinda stumped. please help. thanks
Re: Multiple actors
I'm not completely sure what the problem is. We have multiple actors in this example. You add each to the renderer separately, and set their position individually.
You are right that the VTK docs are extensive. They are useful, but only when you already know what you are looking for.
If you are serious about VTK, I highly recommend forking out for 'The VTK User's Guide'. Very helpful for these types of questions.
Drew
---------------------------
Drew McCormack
http://www.maccoremac.com
http://www.macanics.net
http://www.macresearch.org
how do you position a line?
i have think considering getting the book, but i'm a little put off by the negative comments. however, everyone seems to agree that that's the only book available.
well, a simple question about multiple actors... i tried to extend your tutorial and draw lines between each atom.
-(void)addLineActorWithRed:(float)r green:(float)g blue:(float)b alpha:(float)a{
vtkActor *lineActor = vtkActor::New();
lineActor->SetMapper(lineMapper);
lineActor->GetProperty()->SetColor(r, g, b);
lineActor->GetProperty()->SetOpacity(a);
[vtkView getRenderer]->AddActor(lineActor);
lineActor->Delete();
}
...
lineSource->SetPoint1(0, 0, 0);
lineSource->SetPoint2(1,1,1);
lineMapper->SetInput(lineSource->GetOutput());
[self addLineActorWithRed:0 green:0 blue:1 alpha:1];
lineSource->SetPoint1(1, 1, 1);
lineSource->SetPoint2(2,2,2);
lineMapper->SetInput(lineSource->GetOutput());
[self addLineActorWithRed:0 green:0 blue:1 alpha:1];
i tried doing this outside/after the
for ( actorIndex = 0; actorIndex < coll->GetNumberOfItems(); ++actorIndex) {loop. but only that 1,1,1 to 2,2,2 line got rendered. the first line was not rendered.
How would i go about drawing 2 lines and... how do i position a line?
thanks.
i'm writing a simple code that reads nodes and connectivity from a file and renders it to the screen. just can't seem to get the lines to work. i don't mind sharing my source when i'm done. could be a decent answer to your exercise too.
Lines
About the book: don't believe the commentary, because it is well worth having. It gives you the overview you need, and lets you see what is possible.
In terms of lines, one way to do this is to create tubes. For an example of this, see this VTK sample code (in Python). Hopefully that will show how you can make tubes between the balls.
Drew
---------------------------
Drew McCormack
http://www.maccoremac.com
http://www.macanics.net
http://www.macresearch.org
BasicVTKView
I hope these discussions are still active even though the tutorial was written some time ago... I have been playing around with VTK for some time on with Linux using C++ and Qt. Having recently bought my first Mac (Snow Leopard + XCode 3.2) I am learning Objective C and Cocoa so these tutorials have been helpful. I am having a problem with the current tutorial in that *vtkView in MoleculeDocument never seems to be instantiated. When the application is run vtkViewer has a value of 0 in setupvtkView and so
[vtkView getInteractor]results in a run time error (in debug gdb halts execution with EXC_BAD_ACCESS). I have double checked the source and am pretty sure I haven't missed anything. As an Objective C newbie, apologies if this is a stupid question with an obvious answer. Any suggestions?Thanks.
building in XCode Version 3.0
I was having problems building this tutorial but fixed such problems by doing the following:
1)In XCode's menu goto Project\Edit Project Settings and select Build tab (right of "General" tab).
2)Scroll down to the Linking section and set the "C++ Standard Library Type" to "Dynamic". Check the checkbox for the Link With Standard Libraries" field.
3)Then Scroll down further and verify that the Search Paths fields are correct in the "Search Paths" section. For example, for me the "Header Search Paths" field was properly set to "/Users/%myusername%/Develop/VTKBin/include/vtk-5.4" but the "Library Search Paths" field was empty. After settings it to be "/Users/%username%/Develop/VTKBin/lib" (where %myusername% would be the user directory in which you built the VTK libaries with cmake), and rebuilding the project, things compiled successfully.
Also if you're using XCode version 3.0 as opposed to the version of XCode that these tutorials seem to have been written for (an older version of XCode), then you probably realize that in XCode version 3.0, you can't drag source files into the Interface Builder. Instead you drag an Object (a blue cube icon) from the library into the Interface Builder window and set its class to be one of the subclasses you created in your project.
For example in the project you'd double click on MoleculeDocument.nib in the Resources folder to launch Interface Builder. In Interface Builder's menu goto Tools/Library and launch the Library. In the Library dialog window with the Objects tab selected (the default selection), navigate to the "Objects and Controllers" item in the Objects Treeview and drag one of the blue cube icons labelled Object into the window titled "MoleculeDocument.nib". Select this newly placed object (blue cube) inside of the "MoleculeDocument.nib" window and open up the Inspector dialog via "Tools\Inspector" menu item in the Interface Builder's menu bar. Goto the "Identity" tab in the Inspector dialog. In the "Class" field within the "Class Identity" section, type in the class name -- i.e. "MoleculeDocument" in this case. For this tutorial you'll also need to drag an Object (blue cube) from the library and set its Class field to "BasicVTKView". Then associate the outlets and actions as described in the tutorial.
Hope this helps.