home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.hitl.washington.edu
/
ftp.hitl.washington.edu.tar
/
ftp.hitl.washington.edu
/
pub
/
people
/
peter
/
ER
/
ImageSequenceViewer.cxx
< prev
next >
Wrap
C/C++ Source or Header
|
1998-07-07
|
19KB
|
660 lines
/*
ImageSequenceViewer.cxx
When the image sequence viewer is created, the display object passed
in is copied and saved. Display panels are then created based on
the copy. When panels are added or removed to the viewer, all
current panels are destroyed and new copies are made using the
original display object copy. This simplifies the calculation
required to properly position, orient, and scale the new panels.
Internally, when an image sequence is loaded the viewer creates an
array of hidden objects equal in size to the number of images in the
sequence. This is done to cache the textures, so they won't have to
be loaded when they're applied to the display panels.
*/
#include "ImageSequenceViewer.h"
#include "v3dFont.h"
///////////////
// CREATORS //
/////////////
// Create a new image sequence viewer with the specified
// number of panels. The panels will be copies of the
// specified diplay object.
ImageSequenceViewer::ImageSequenceViewer (v3dObject *display_object,
int num_panels, bool show_image_numbers)
: num_panels_ (num_panels),
num_images_ (0), display_object_cache_ (0), image_textures_ (0),
is_playing_ (false), is_reverse_playing_ (false),
filename_prefix_ (0), filename_suffix_ (0), bounding_box_ (0),
cycle_delay_ (0), show_image_numbers_ (show_image_numbers),
series_number_ (0), current_cycle_ (0)
{
display_object_ = (v3dObject *)display_object->Copy();
display_object_->Hide();
create_panels(num_panels_);
if (show_image_numbers_)
create_numbers(num_panels);
num_panels_ = num_panels;
}
ImageSequenceViewer::~ImageSequenceViewer()
{
Stop();
free (filename_prefix_);
free (filename_suffix_);
free_texture_cache();
delete display_object_;
for (int i = 0; i < num_panels_; ++i) {
delete display_objects_[i];
delete image_numbers_[i];
}
}
///////////////////
// MANIPULATORS //
/////////////////
// Load a new sequence of images into the viewer.
bool
ImageSequenceViewer::LoadNewSequence(
char *filename_prefix, char *filename_suffix,
int first_image_num, int last_image_num,
bool use_transparency, int series_num)
{
first_image_num_ = first_image_num;
last_image_num_ = last_image_num;
use_transparency_ = use_transparency;
free (filename_prefix_);
free (filename_suffix_);
filename_prefix_ = strdup (filename_prefix);
filename_suffix_ = strdup (filename_suffix);
free_texture_cache ();
num_images_ = last_image_num_ - first_image_num_ + 1;
if (!load_images())
return false;
GoToImage(0);
if (show_image_numbers_) {
create_numbers(num_panels_);
position_image_numbers();
series_number_->ShowNumber(series_num);
}
return true;
}
// Set the number of panels used to display the sequence.
bool
ImageSequenceViewer::SetPanelNum(int num_panels)
{
if (num_panels <= 0)
return false;
create_panels(num_panels);
if (show_image_numbers_) {
create_numbers(num_panels);
num_panels_ = num_panels;
position_image_numbers();
}
GoToImage(current_image_num_);
return true;
}
// Set the number of cycles to wait before displaying the next image in
// the sequence.
bool
ImageSequenceViewer::SetDelay(int delay)
{
if (delay < 0)
return false;
cycle_delay_ = delay;
current_cycle_ = delay;
}
// Show the next image in the sequence.
int
ImageSequenceViewer::NextImage()
{
if (++current_image_num_ >= num_images_)
current_image_num_ = 0;
show_images(current_image_num_);
return current_image_num_;
}
// Show the previous image in the sequence.
int
ImageSequenceViewer::PreviousImage()
{
if (--current_image_num_ < 0)
current_image_num_ = num_images_ - 1;
show_images(current_image_num_);
return current_image_num_;
}
// Jump to the requested image number in the sequence.
// Returns false if requested number is out of range.
bool
ImageSequenceViewer::GoToImage(int image_num)
{
if ((image_num < 0) || (image_num >= num_images_))
return false;
current_image_num_ = image_num;
show_images(current_image_num_);
return true;
}
// Play the images one after the other.
void
ImageSequenceViewer::Play()
{
if (is_playing_)
return;
if (is_reverse_playing_)
Stop();
// Register the animation event handler with the universe's
// update event.
RegisterEventHandler(universe_, v3dUniverse::UNIVERSE_SIMULATION_LOOP,
&animate_sequence_callback, 0);
is_playing_ = true;
}
// Play the images one after the other in reverse order.
void
ImageSequenceViewer::ReversePlay()
{
if (is_reverse_playing_)
return;
if (is_playing_)
Stop();
// Register the animation event handler with the universe's
// update event.
RegisterEventHandler(universe_, v3dUniverse::UNIVERSE_SIMULATION_LOOP,
&animate_reverse_sequence_callback, 0);
is_reverse_playing_ = true;
}
// Stop the animation.
void
ImageSequenceViewer::Stop()
{
// Unregister the animation event handler from the universe's
// update event.
if (is_playing_) {
UnregisterEventHandler(universe_,
v3dUniverse::UNIVERSE_SIMULATION_LOOP,
&animate_sequence_callback);
is_playing_ = false;
} else if (is_reverse_playing_) {
UnregisterEventHandler(universe_,
v3dUniverse::UNIVERSE_SIMULATION_LOOP,
&animate_reverse_sequence_callback);
is_reverse_playing_ = false;
}
}
// Toggles between playing and stopping.
void
ImageSequenceViewer::TogglePlay()
{
if (IsPlaying())
Stop();
else
Play();
}
// Toggles between reverse playing and stopping.
void
ImageSequenceViewer::ToggleReversePlay()
{
if (IsReversePlaying())
Stop();
else
ReversePlay();
}
// Returns a copy of the display object with the current texture mapped on it.
v3dObject *
ImageSequenceViewer::CopyCurrentImage()
{
v3dObject *current_copy = (v3dObject *)display_objects_[0]->Copy();
image_textures_[current_image_num_]->ApplyToObject(*current_copy);
return current_copy;
}
// Stretch the image sequence viewer about the given point by the given factors.
void
ImageSequenceViewer::Scale(const v3dVector &factors, const v3dPos& point)
{
bounding_box_->Scale(factors, point);
display_object_->Scale(factors, point);
if (show_image_numbers_) {
series_number_->Scale(factors, point);
for (int i = 0; i < num_panels_; ++i) {
display_objects_[i]->Scale(factors, point);
image_numbers_[i]->Scale(factors, point);
total_numbers_[i]->Scale(factors, point);
}
} else
for (int i = 0; i < num_panels_; ++i)
display_objects_[i]->Scale(factors, point);
}
////////////////
// HIDE/SHOW //
//////////////
void
ImageSequenceViewer::Hide()
{
// Hide the display panels.
for (DisplayObjectArray::iterator it = display_objects_.begin();
it != display_objects_.end(); ++it)
(*it)->Hide();
// If image numbers are shown, then hide those, too.
if (show_image_numbers_) {
for (int i = 0; i < image_numbers_.size(); ++i) {
image_numbers_[i]->Hide();
total_numbers_[i]->Hide();
}
series_number_->Hide();
}
hidden_ = true;
}
void
ImageSequenceViewer::Show()
{
// Show the display panels.
for (DisplayObjectArray::iterator it = display_objects_.begin();
it != display_objects_.end(); ++it)
(*it)->Show();
// If image numbers are shown, then hide those, too.
if (show_image_numbers_) {
for (int i = 0; i < image_numbers_.size(); ++i) {
image_numbers_[i]->Show();
total_numbers_[i]->Show();
}
series_number_->Show();
}
hidden_ = false;
}
void
ImageSequenceViewer::Disable()
{
// Disable the display panels.
for (DisplayObjectArray::iterator it = display_objects_.begin();
it != display_objects_.end(); ++it)
(*it)->Disable();
// If image numbers are shown, then hide those, too.
if (show_image_numbers_) {
for (int i = 0; i < image_numbers_.size(); ++i) {
image_numbers_[i]->Disable();
total_numbers_[i]->Disable();
}
series_number_->Disable();
}
disabled_ = true;
}
void
ImageSequenceViewer::Enable()
{
// Enable the display panels.
for (DisplayObjectArray::iterator it = display_objects_.begin();
it != display_objects_.end(); ++it)
(*it)->Enable();
// If image numbers are shown, then hide those, too.
if (show_image_numbers_) {
for (int i = 0; i < image_numbers_.size(); ++i) {
image_numbers_[i]->Enable();
total_numbers_[i]->Enable();
}
series_number_->Enable();
}
disabled_ = false;
}
////////////////////////
// PRIVATE FUNCTIONS //
//////////////////////
// Create the sequence of panels that will be used to display the images.
// All current panels are destroyed and a new array of panels is created
// by copying the original display panel. This is done to simplify
// calculations for size, position, and orientation.
void
ImageSequenceViewer::create_panels(int num_panels)
{
// Set the sizes of the various parts of the image sequence viewer.
v3dDim panel_size = display_object_->GetSize();
v3dFont a_font("rcfont3d.nff");
panel_spacing_ = panel_size.x() / 20.0;
v3dDim total_size;
if (show_image_numbers_) {
// Image and series numbers are 1/5th the size of panel width.
image_num_size_ = panel_size.x() / 5.0;
image_num_spacing_ = panel_spacing_;
if (series_number_ == 0) {
series_number_ = new v3dNumber(1, &a_font);
}
series_number_->Scale(image_num_size_ / series_number_->GetSize().y());
series_num_size_ = series_number_->GetSize().x();
series_num_spacing_ = panel_size.x() / 5.0;
// Calculate the size of the entire viewer, taking into
// account the number and size of the panels, the size of the
// image and series numbers, and the spacing between the
// panels and numbers.
total_size = v3dDim(panel_size.x() * num_panels +
panel_spacing_ * (num_panels - 1) +
series_num_size_ + series_num_spacing_,
panel_size.y() + image_num_size_ + image_num_spacing_,
panel_size.z() + panel_spacing_);
} else {
// Calculate the size of the entire viewer, taking into
// account the number and size of the panels and the spacing
// between them.
total_size = v3dDim(panel_size.x() * num_panels +
panel_spacing_ * (num_panels - 1),
panel_size.y(),
panel_size.z() + panel_spacing_);
}
if (num_panels > display_objects_.size()) {
// Too few panels exist, so create some.
display_object_->Show();
for (int i = num_panels - display_objects_.size(); i > 0; --i) {
display_objects_.push_back((v3dObject *)display_object_->Copy());
if (show_image_numbers_) {
v3dNumber *a_num = new v3dNumber(3, &a_font);
a_num->Scale(image_num_size_ / a_num->GetSize().y());
image_numbers_.push_back(a_num);
}
}
display_object_->Hide();
} else {
// Too many panels exist, so delete a few.
for (int i = display_objects_.size() - num_panels; i > 0; --i) {
delete display_objects_[display_objects_.size() - 1];
display_objects_.pop_back();
if (show_image_numbers_) {
delete image_numbers_[image_numbers_.size() - 1];
image_numbers_.pop_back();
}
}
}
// Resize the bounding box root object and set the panels to their
// places within that object.
v3dPos old_pos;
v3dOrient old_orient;
bool restore_transform = false;
if (bounding_box_) {
// If the sequence viewer already exists, then store its
// current position and orientation so it can be returned
// to that spot after the new object is created.
old_pos = bounding_box_->GetAbsolutePosition();
old_orient = bounding_box_->GetAbsoluteOrientation();
bounding_box_->RemoveAll();
imp_ = 0;
delete bounding_box_;
restore_transform = true;
}
bounding_box_ = new v3dBlockObject(total_size);
v3dPos panel_pos;
// The panel position changes depending on whether or not the images
// numbers are shown.
if (show_image_numbers_) {
panel_pos = v3dPos(-(panel_size.x() * (num_panels - 1) +
panel_spacing_ * (num_panels - 1) +
series_num_size_ + series_num_spacing_) / 2.0,
image_num_size_ + image_num_spacing_, 0);
} else {
panel_pos = v3dPos(-(panel_size.x() * (num_panels - 1) +
panel_spacing_ * (num_panels - 1)) / 2.0,
0, 0);
}
v3dOrient panel_orient = bounding_box_->GetAbsoluteOrientation();
// Attach the panels and numbers to the bounding box.
if (show_image_numbers_) {
bounding_box_->Add(series_number_);
series_number_->Move(panel_pos - v3dPos(
(panel_size.x() +
series_num_size_ + series_num_spacing_) / 2.0, 0, 0));
series_number_->SetAbsoluteOrientation(panel_orient);
}
for (int i = 0; i < num_panels; ++i) {
bounding_box_->Add(display_objects_[i]);
display_objects_[i]->Move(panel_pos);
display_objects_[i]->SetAbsoluteOrientation(panel_orient);
panel_pos += v3dPos(panel_size.x() + panel_spacing_, 0, 0);
}
imp_ = bounding_box_->GetImplement();
bounding_box_->Hide();
// If this isn't the first time a bounding box has been created,
// then move the newly-made bounding box (and all objects attached
// to it) to the old bounding box position.
if (restore_transform) {
bounding_box_->MoveAbsolute(old_pos);
bounding_box_->SetAbsoluteOrientation(old_orient);
}
}
// Create the total numbers for this sequence of images.
void
ImageSequenceViewer::create_numbers(int num_panels)
{
// Delete all the old total image number text objects.
while (total_numbers_.size()) {
delete total_numbers_.back();
total_numbers_.pop_back();
}
char total_image_str[4];
sprintf (total_image_str, "/%d", num_images_);
v3dFont a_font("rcfont3d.nff");
float num_size = image_numbers_[0]->GetSize().y();
for (int i = 0; i < num_panels; ++i) {
v3dTextObject *a_text = new v3dTextObject(&a_font, total_image_str);
a_text->Scale((num_size / a_text->GetSize().y()) * .95);
total_numbers_.push_back(a_text);
}
}
// Load the sequence of images into memory.
bool
ImageSequenceViewer::load_images()
{
// Create all the placeholder objects that will be used to cache the
// images.
display_object_cache_ = new v3dObject *[num_images_];
image_textures_ = new v3dTexture * [num_images_];
// Load all of the textures and apply them to the objects.
char filename[255];
for (int i = 0; i < num_images_; ++i) {
sprintf(filename, "%s%d%s", filename_prefix_,
i + first_image_num_, filename_suffix_);
image_textures_[i] = new v3dTexture(filename);
image_textures_[i]->SetTransparency(use_transparency_);
display_object_cache_[i] = (v3dObject *)display_object_->Copy();
image_textures_[i]->ApplyToObject(*(display_object_cache_[i]));
display_object_cache_[i]->Hide();
}
current_image_num_ = 0;
return true;
}
// Free the texture cache.
void
ImageSequenceViewer::free_texture_cache()
{
// Free all the objects associated with the previous image set.
for (int i = 0; i < num_images_; ++i) {
delete display_object_cache_[i];
delete image_textures_[i];
}
delete [] image_textures_;
delete [] display_object_cache_;
}
// Position the panels for a newly loaded series.
void
ImageSequenceViewer::position_panels()
{
v3dDim panel_size = display_object_->GetSize();
int num_panels = display_objects_.size();
v3dPos panel_pos;
if (show_image_numbers_) {
panel_pos = v3dPos(-(panel_size.x() * (num_panels - 1) +
panel_spacing_ * (num_panels - 1) +
series_num_size_ + series_num_spacing_) / 2.0,
image_num_size_ + image_num_spacing_, 0);
} else {
panel_pos = v3dPos(-(panel_size.x() * (num_panels - 1) +
panel_spacing_ * (num_panels - 1)) / 2.0,
0, 0);
}
v3dOrient panel_orient = bounding_box_->GetAbsoluteOrientation();
v3dDim num_size = image_numbers_[0]->GetSize();
if (show_image_numbers_) {
bounding_box_->Add(series_number_);
series_number_->Move(panel_pos - v3dPos(
(panel_size.x() +
series_num_size_ + series_num_spacing_) / 2.0, 0, 0));
series_number_->SetAbsoluteOrientation(panel_orient);
}
for (int i = 0; i < num_panels; ++i) {
bounding_box_->Add(display_objects_[i]);
display_objects_[i]->Move(panel_pos);
display_objects_[i]->SetAbsoluteOrientation(panel_orient);
panel_pos += v3dPos(panel_size.x() + panel_spacing_, 0, 0);
}
}
// Position the image numbers for a newly loaded series.
void
ImageSequenceViewer::position_image_numbers()
{
// Set the sizes of the various parts of the image sequence viewer.
v3dDim panel_size = display_object_->GetSize();
panel_spacing_ = panel_size.x() / 20.0;
image_num_size_ = panel_size.x() / 5.0;
image_num_spacing_ = panel_spacing_;
series_num_size_ = series_number_->GetSize().x();
series_num_spacing_ = panel_size.x() / 5.0;
v3dDim total_size = v3dDim(panel_size.x() * num_panels_ +
panel_spacing_ * (num_panels_ - 1) +
series_num_size_ + series_num_spacing_,
panel_size.y() + image_num_size_ + image_num_spacing_,
panel_size.z() + panel_spacing_);
v3dPos panel_pos = v3dPos(-(panel_size.x() * (num_panels_ - 1) +
panel_spacing_ * (num_panels_ - 1) +
series_num_size_ + series_num_spacing_) / 2.0,
image_num_size_ + image_num_spacing_, 0);
v3dOrient panel_orient = bounding_box_->GetAbsoluteOrientation();
char total_image_num[4];
sprintf (total_image_num, " %d", num_images_);
v3dDim num_size = image_numbers_[0]->GetSize();
v3dDim tnum_size = total_numbers_[0]->GetSize();
for (int i = 0; i < image_numbers_.size(); ++i) {
bounding_box_->Add(total_numbers_[i]);
total_numbers_[i]->MoveToOrigin();
total_numbers_[i]->Move(panel_pos + v3dDir (
(panel_size.x() - tnum_size.x()) / 2.0,
- (panel_size.y() + image_num_size_) / 2.0 -
image_num_spacing_, 0));
total_numbers_[i]->SetAbsoluteOrientation(panel_orient);
bounding_box_->Add(image_numbers_[i]);
image_numbers_[i]->Move(panel_pos + v3dDir (
(panel_size.x() - num_size.x()) / 2.0 - tnum_size.x(),
- (panel_size.y() + image_num_size_) / 2.0 -
image_num_spacing_, 0));
image_numbers_[i]->SetAbsoluteOrientation(panel_orient);
panel_pos += v3dPos(panel_size.x() + panel_spacing_, 0, 0);
}
}
// Show a sequence starting with the index specified.
void
ImageSequenceViewer::show_images(int first_image_num)
{
int i = 0;
if (show_image_numbers_) {
for (DisplayObjectArray::iterator it = display_objects_.begin();
it != display_objects_.end(); ++it, ++i) {
image_textures_[(first_image_num + i) % num_images_]->
ApplyToObject(**it);
image_numbers_[i]->ShowNumber((first_image_num + i) % num_images_ + 1);
}
} else {
for (DisplayObjectArray::iterator it = display_objects_.begin();
it != display_objects_.end(); ++it, ++i) {
image_textures_[(first_image_num + i) % num_images_]->
ApplyToObject(**it);
}
}
}
// Functions that animate the sequence of images.
void
ImageSequenceViewer::animate_sequence_callback(const v3dEventMessage *message)
{
((ImageSequenceViewer *)message->GetEventReceiver())->animate_sequence();
}
void
ImageSequenceViewer::animate_sequence()
{
if (--current_cycle_ < 0) {
NextImage();
current_cycle_ = cycle_delay_;
}
}
void
ImageSequenceViewer::animate_reverse_sequence_callback(const v3dEventMessage *message)
{
((ImageSequenceViewer *)message->GetEventReceiver())->animate_reverse_sequence();
}
void
ImageSequenceViewer::animate_reverse_sequence()
{
if (--current_cycle_ < 0) {
PreviousImage();
current_cycle_ = cycle_delay_;
}
}