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 >
C/C++ Source or Header  |  1998-07-07  |  19KB  |  660 lines

  1. /*
  2.   ImageSequenceViewer.cxx
  3.  
  4.     When the image sequence viewer is created, the display object passed
  5.     in is copied and saved.  Display panels are then created based on
  6.     the copy.  When panels are added or removed to the viewer, all
  7.     current panels are destroyed and new copies are made using the
  8.     original display object copy.  This simplifies the calculation
  9.     required to properly position, orient, and scale the new panels.
  10.  
  11.     Internally, when an image sequence is loaded the viewer creates an
  12.     array of hidden objects equal in size to the number of images in the
  13.     sequence.  This is done to cache the textures, so they won't have to
  14.     be loaded when they're applied to the display panels.
  15.  
  16. */
  17.  
  18. #include "ImageSequenceViewer.h"
  19.  
  20. #include "v3dFont.h"
  21.  
  22. ///////////////
  23. // CREATORS //
  24. /////////////
  25.  
  26. // Create a new image sequence viewer with the specified
  27. // number of panels.  The panels will be copies of the
  28. // specified diplay object.
  29. ImageSequenceViewer::ImageSequenceViewer (v3dObject *display_object,
  30.     int num_panels, bool show_image_numbers)
  31.     : num_panels_ (num_panels),
  32.     num_images_ (0), display_object_cache_ (0), image_textures_ (0),
  33.     is_playing_ (false), is_reverse_playing_ (false),
  34.     filename_prefix_ (0), filename_suffix_ (0), bounding_box_ (0),
  35.     cycle_delay_ (0), show_image_numbers_ (show_image_numbers),
  36.     series_number_ (0), current_cycle_ (0)
  37. {
  38.     display_object_ = (v3dObject *)display_object->Copy();
  39.     display_object_->Hide();
  40.     create_panels(num_panels_);
  41.     if (show_image_numbers_)
  42.         create_numbers(num_panels);
  43.     num_panels_ = num_panels;
  44. }
  45.  
  46. ImageSequenceViewer::~ImageSequenceViewer()
  47. {
  48.     Stop();
  49.  
  50.     free (filename_prefix_);
  51.     free (filename_suffix_);
  52.  
  53.     free_texture_cache();
  54.     delete display_object_;
  55.  
  56.     for (int i = 0; i < num_panels_; ++i) {
  57.         delete display_objects_[i];
  58.         delete image_numbers_[i];
  59.     }
  60. }
  61.  
  62. ///////////////////
  63. // MANIPULATORS //
  64. /////////////////
  65.  
  66. // Load a new sequence of images into the viewer.
  67. bool
  68. ImageSequenceViewer::LoadNewSequence(
  69.     char *filename_prefix, char *filename_suffix,
  70.     int first_image_num, int last_image_num,
  71.     bool use_transparency, int series_num)
  72. {
  73.     first_image_num_ = first_image_num;
  74.     last_image_num_ = last_image_num;
  75.     use_transparency_ = use_transparency;
  76.     free (filename_prefix_);
  77.     free (filename_suffix_);
  78.     filename_prefix_ = strdup (filename_prefix);
  79.     filename_suffix_ = strdup (filename_suffix);
  80.     free_texture_cache ();
  81.     num_images_ = last_image_num_ - first_image_num_ + 1;
  82.     if (!load_images())
  83.         return false;
  84.     GoToImage(0);
  85.     if (show_image_numbers_) {
  86.         create_numbers(num_panels_);
  87.         position_image_numbers();
  88.         series_number_->ShowNumber(series_num);
  89.     }
  90.  
  91.     return true;
  92. }
  93.  
  94. // Set the number of panels used to display the sequence.
  95. bool
  96. ImageSequenceViewer::SetPanelNum(int num_panels)
  97. {
  98.     if (num_panels <= 0)
  99.         return false;
  100.  
  101.     create_panels(num_panels);
  102.     if (show_image_numbers_) {
  103.         create_numbers(num_panels);
  104.         num_panels_ = num_panels;
  105.         position_image_numbers();
  106.     }
  107.  
  108.     GoToImage(current_image_num_);
  109.  
  110.     return true;
  111. }
  112.  
  113. // Set the number of cycles to wait before displaying the next image in
  114. // the sequence.
  115. bool
  116. ImageSequenceViewer::SetDelay(int delay)
  117. {
  118.     if (delay < 0)
  119.         return false;
  120.  
  121.     cycle_delay_ = delay;
  122.     current_cycle_ = delay;
  123. }
  124.  
  125. // Show the next image in the sequence.
  126. int
  127. ImageSequenceViewer::NextImage()
  128. {
  129.     if (++current_image_num_ >= num_images_)
  130.         current_image_num_ = 0;
  131.     show_images(current_image_num_);
  132.     return current_image_num_;
  133. }
  134.  
  135. // Show the previous image in the sequence.
  136. int
  137. ImageSequenceViewer::PreviousImage()
  138. {
  139.     if (--current_image_num_ < 0)
  140.         current_image_num_ = num_images_ - 1;
  141.     show_images(current_image_num_);
  142.     return current_image_num_;
  143. }
  144.  
  145. // Jump to the requested image number in the sequence.
  146. // Returns false if requested number is out of range.
  147. bool
  148. ImageSequenceViewer::GoToImage(int image_num)
  149. {
  150.     if ((image_num < 0) || (image_num >= num_images_))
  151.         return false;
  152.  
  153.     current_image_num_ = image_num;
  154.     show_images(current_image_num_);
  155.     return true;
  156. }
  157.  
  158. // Play the images one after the other.
  159. void
  160. ImageSequenceViewer::Play()
  161. {
  162.     if (is_playing_)
  163.         return;
  164.     if (is_reverse_playing_)
  165.         Stop();
  166.  
  167.         // Register the animation event handler with the universe's
  168.     // update event.
  169.     RegisterEventHandler(universe_, v3dUniverse::UNIVERSE_SIMULATION_LOOP,
  170.         &animate_sequence_callback, 0);
  171.     is_playing_ = true;
  172. }
  173.  
  174. // Play the images one after the other in reverse order.
  175. void
  176. ImageSequenceViewer::ReversePlay()
  177. {
  178.     if (is_reverse_playing_)
  179.         return;
  180.     if (is_playing_)
  181.         Stop();
  182.  
  183.         // Register the animation event handler with the universe's
  184.     // update event.
  185.     RegisterEventHandler(universe_, v3dUniverse::UNIVERSE_SIMULATION_LOOP,
  186.         &animate_reverse_sequence_callback, 0);
  187.     is_reverse_playing_ = true;
  188. }
  189.  
  190. // Stop the animation.
  191. void
  192. ImageSequenceViewer::Stop()
  193. {
  194.         // Unregister the animation event handler from the universe's
  195.     // update event.
  196.     if (is_playing_) {
  197.         UnregisterEventHandler(universe_,
  198.             v3dUniverse::UNIVERSE_SIMULATION_LOOP,
  199.             &animate_sequence_callback);
  200.         is_playing_ = false;
  201.     } else if (is_reverse_playing_) {
  202.         UnregisterEventHandler(universe_,
  203.             v3dUniverse::UNIVERSE_SIMULATION_LOOP,
  204.             &animate_reverse_sequence_callback);
  205.         is_reverse_playing_ = false;
  206.     }
  207. }
  208.  
  209. // Toggles between playing and stopping.
  210. void
  211. ImageSequenceViewer::TogglePlay()
  212. {
  213.     if (IsPlaying())
  214.         Stop();
  215.     else
  216.         Play();
  217. }
  218.  
  219. // Toggles between reverse playing and stopping.
  220. void
  221. ImageSequenceViewer::ToggleReversePlay()
  222. {
  223.     if (IsReversePlaying())
  224.         Stop();
  225.     else
  226.         ReversePlay();
  227. }
  228.  
  229. // Returns a copy of the display object with the current texture mapped on it.
  230. v3dObject *
  231. ImageSequenceViewer::CopyCurrentImage()
  232. {
  233.     v3dObject *current_copy = (v3dObject *)display_objects_[0]->Copy();
  234.     image_textures_[current_image_num_]->ApplyToObject(*current_copy);
  235.     return current_copy;
  236. }
  237.  
  238. // Stretch the image sequence viewer about the given point by the given factors.
  239. void
  240. ImageSequenceViewer::Scale(const v3dVector &factors, const v3dPos& point)
  241. {
  242.     bounding_box_->Scale(factors, point);
  243.     display_object_->Scale(factors, point);
  244.     if (show_image_numbers_) {
  245.         series_number_->Scale(factors, point);
  246.         for (int i = 0; i < num_panels_; ++i) {
  247.             display_objects_[i]->Scale(factors, point);
  248.             image_numbers_[i]->Scale(factors, point);
  249.             total_numbers_[i]->Scale(factors, point);
  250.         }
  251.     } else
  252.         for (int i = 0; i < num_panels_; ++i)
  253.             display_objects_[i]->Scale(factors, point);
  254. }
  255.  
  256. ////////////////
  257. // HIDE/SHOW //
  258. //////////////
  259. void
  260. ImageSequenceViewer::Hide()
  261. {
  262.     // Hide the display panels.
  263.     for (DisplayObjectArray::iterator it = display_objects_.begin();
  264.             it != display_objects_.end(); ++it)
  265.         (*it)->Hide();
  266.  
  267.     // If image numbers are shown, then hide those, too.
  268.     if (show_image_numbers_) {
  269.         for (int i = 0; i < image_numbers_.size(); ++i) {
  270.             image_numbers_[i]->Hide();
  271.             total_numbers_[i]->Hide();
  272.         }
  273.         series_number_->Hide();
  274.     }
  275.     hidden_ = true;
  276. }
  277.  
  278. void
  279. ImageSequenceViewer::Show()
  280. {
  281.     // Show the display panels.
  282.     for (DisplayObjectArray::iterator it = display_objects_.begin();
  283.             it != display_objects_.end(); ++it)
  284.         (*it)->Show();
  285.  
  286.     // If image numbers are shown, then hide those, too.
  287.     if (show_image_numbers_) {
  288.         for (int i = 0; i < image_numbers_.size(); ++i) {
  289.             image_numbers_[i]->Show();
  290.             total_numbers_[i]->Show();
  291.         }
  292.         series_number_->Show();
  293.     }
  294.     hidden_ = false;
  295. }
  296.  
  297. void
  298. ImageSequenceViewer::Disable()
  299. {
  300.     // Disable the display panels.
  301.     for (DisplayObjectArray::iterator it = display_objects_.begin();
  302.             it != display_objects_.end(); ++it)
  303.         (*it)->Disable();
  304.  
  305.     // If image numbers are shown, then hide those, too.
  306.     if (show_image_numbers_) {
  307.         for (int i = 0; i < image_numbers_.size(); ++i) {
  308.             image_numbers_[i]->Disable();
  309.             total_numbers_[i]->Disable();
  310.         }
  311.         series_number_->Disable();
  312.     }
  313.     disabled_ = true;
  314. }
  315.  
  316. void
  317. ImageSequenceViewer::Enable()
  318. {
  319.     // Enable the display panels.
  320.     for (DisplayObjectArray::iterator it = display_objects_.begin();
  321.             it != display_objects_.end(); ++it)
  322.         (*it)->Enable();
  323.  
  324.     // If image numbers are shown, then hide those, too.
  325.     if (show_image_numbers_) {
  326.         for (int i = 0; i < image_numbers_.size(); ++i) {
  327.             image_numbers_[i]->Enable();
  328.             total_numbers_[i]->Enable();
  329.         }
  330.         series_number_->Enable();
  331.     }
  332.     disabled_ = false;
  333. }
  334.  
  335. ////////////////////////
  336. // PRIVATE FUNCTIONS //
  337. //////////////////////
  338.  
  339. // Create the sequence of panels that will be used to display the images.
  340. // All current panels are destroyed and a new array of panels is created
  341. // by copying the original display panel.  This is done to simplify
  342. // calculations for size, position, and orientation.
  343. void
  344. ImageSequenceViewer::create_panels(int num_panels)
  345. {
  346.     // Set the sizes of the various parts of the image sequence viewer.
  347.     v3dDim panel_size = display_object_->GetSize();
  348.     v3dFont a_font("rcfont3d.nff");
  349.     panel_spacing_ = panel_size.x() / 20.0;
  350.     v3dDim total_size;
  351.     if (show_image_numbers_) {
  352.         // Image and series numbers are 1/5th the size of panel width.
  353.         image_num_size_ = panel_size.x() / 5.0;
  354.         image_num_spacing_ = panel_spacing_;
  355.         if (series_number_ == 0) {
  356.             series_number_ = new v3dNumber(1, &a_font);
  357.         }
  358.         series_number_->Scale(image_num_size_ / series_number_->GetSize().y());
  359.         series_num_size_ = series_number_->GetSize().x();
  360.         series_num_spacing_ = panel_size.x() / 5.0;
  361.  
  362.         // Calculate the size of the entire viewer, taking into
  363.         // account the number and size of the panels, the size of the
  364.         // image and series numbers, and the spacing between the
  365.         // panels and numbers.
  366.         total_size = v3dDim(panel_size.x() * num_panels +
  367.             panel_spacing_ * (num_panels - 1) +
  368.             series_num_size_ + series_num_spacing_,
  369.             panel_size.y() + image_num_size_ + image_num_spacing_,
  370.             panel_size.z() + panel_spacing_);
  371.     } else {
  372.         // Calculate the size of the entire viewer, taking into
  373.         // account the number and size of the panels and the spacing
  374.         // between them.
  375.         total_size = v3dDim(panel_size.x() * num_panels +
  376.             panel_spacing_ * (num_panels - 1),
  377.             panel_size.y(),
  378.             panel_size.z() + panel_spacing_);
  379.     }
  380.  
  381.     if (num_panels > display_objects_.size()) {
  382.         // Too few panels exist, so create some.
  383.         display_object_->Show();
  384.         for (int i = num_panels - display_objects_.size(); i > 0; --i) {
  385.             display_objects_.push_back((v3dObject *)display_object_->Copy());
  386.             if (show_image_numbers_) {
  387.                 v3dNumber *a_num = new v3dNumber(3, &a_font);
  388.                 a_num->Scale(image_num_size_ / a_num->GetSize().y());
  389.                 image_numbers_.push_back(a_num);
  390.             }
  391.         }
  392.         display_object_->Hide();
  393.     } else {
  394.         // Too many panels exist, so delete a few.
  395.         for (int i = display_objects_.size() - num_panels; i > 0; --i) {
  396.             delete display_objects_[display_objects_.size() - 1];
  397.             display_objects_.pop_back();
  398.             if (show_image_numbers_) {
  399.                 delete image_numbers_[image_numbers_.size() - 1];
  400.                 image_numbers_.pop_back();
  401.             }
  402.         }
  403.     }
  404.  
  405.     // Resize the bounding box root object and set the panels to their
  406.     // places within that object.
  407.     v3dPos old_pos;
  408.     v3dOrient old_orient;
  409.     bool restore_transform = false;
  410.     if (bounding_box_) {
  411.         // If the sequence viewer already exists, then store its
  412.         // current position and orientation so it can be returned
  413.         // to that spot after the new object is created.
  414.         old_pos = bounding_box_->GetAbsolutePosition();
  415.         old_orient = bounding_box_->GetAbsoluteOrientation();
  416.         bounding_box_->RemoveAll();
  417.         imp_ = 0;
  418.         delete bounding_box_;
  419.         restore_transform = true;
  420.     }
  421.  
  422.  
  423.     bounding_box_ = new v3dBlockObject(total_size);
  424.     v3dPos panel_pos;
  425.     // The panel position changes depending on whether or not the images
  426.     // numbers are shown.
  427.     if (show_image_numbers_) {
  428.         panel_pos = v3dPos(-(panel_size.x() * (num_panels - 1) +
  429.             panel_spacing_ * (num_panels - 1) +
  430.             series_num_size_ + series_num_spacing_) / 2.0,
  431.             image_num_size_ + image_num_spacing_, 0);
  432.     } else {
  433.         panel_pos = v3dPos(-(panel_size.x() * (num_panels - 1) +
  434.             panel_spacing_ * (num_panels - 1)) / 2.0,
  435.             0, 0);
  436.     }
  437.     v3dOrient panel_orient = bounding_box_->GetAbsoluteOrientation();
  438.  
  439.     // Attach the panels and numbers to the bounding box.
  440.     if (show_image_numbers_) {
  441.         bounding_box_->Add(series_number_);
  442.         series_number_->Move(panel_pos - v3dPos(
  443.             (panel_size.x() +
  444.             series_num_size_ + series_num_spacing_) / 2.0, 0, 0));
  445.         series_number_->SetAbsoluteOrientation(panel_orient);
  446.     }
  447.     for (int i = 0; i < num_panels; ++i) {
  448.         bounding_box_->Add(display_objects_[i]);
  449.         display_objects_[i]->Move(panel_pos);
  450.         display_objects_[i]->SetAbsoluteOrientation(panel_orient);
  451.         panel_pos += v3dPos(panel_size.x() + panel_spacing_, 0, 0);
  452.     }
  453.     imp_ = bounding_box_->GetImplement();
  454.     bounding_box_->Hide();
  455.  
  456.     // If this isn't the first time a bounding box has been created,
  457.     // then move the newly-made bounding box (and all objects attached
  458.     // to it) to the old bounding box position.
  459.     if (restore_transform) {
  460.         bounding_box_->MoveAbsolute(old_pos);
  461.         bounding_box_->SetAbsoluteOrientation(old_orient);
  462.     }
  463. }
  464.  
  465. // Create the total numbers for this sequence of images.
  466. void
  467. ImageSequenceViewer::create_numbers(int num_panels)
  468. {
  469.     // Delete all the old total image number text objects.
  470.     while (total_numbers_.size()) {
  471.         delete total_numbers_.back();
  472.         total_numbers_.pop_back();
  473.     }
  474.  
  475.     char total_image_str[4];
  476.     sprintf (total_image_str, "/%d", num_images_);
  477.     v3dFont a_font("rcfont3d.nff");
  478.     float num_size = image_numbers_[0]->GetSize().y();
  479.     for (int i = 0; i < num_panels; ++i) {
  480.         v3dTextObject *a_text = new v3dTextObject(&a_font, total_image_str);
  481.         a_text->Scale((num_size / a_text->GetSize().y()) * .95);
  482.         total_numbers_.push_back(a_text);
  483.     }
  484. }
  485.  
  486. // Load the sequence of images into memory.
  487. bool
  488. ImageSequenceViewer::load_images()
  489. {
  490.     // Create all the placeholder objects that will be used to cache the
  491.     // images.
  492.     display_object_cache_ = new v3dObject *[num_images_];
  493.     image_textures_ = new v3dTexture * [num_images_];
  494.  
  495.     // Load all of the textures and apply them to the objects.
  496.     char filename[255];
  497.     for (int i = 0; i < num_images_; ++i) {
  498.         sprintf(filename, "%s%d%s", filename_prefix_,
  499.             i + first_image_num_, filename_suffix_);
  500.         image_textures_[i] = new v3dTexture(filename);
  501.         image_textures_[i]->SetTransparency(use_transparency_);
  502.         display_object_cache_[i] = (v3dObject *)display_object_->Copy();
  503.         image_textures_[i]->ApplyToObject(*(display_object_cache_[i]));
  504.         display_object_cache_[i]->Hide();
  505.     }
  506.  
  507.     current_image_num_ = 0;
  508.     return true;
  509. }
  510.  
  511. // Free the texture cache.
  512. void
  513. ImageSequenceViewer::free_texture_cache()
  514. {
  515.     // Free all the objects associated with the previous image set.
  516.     for (int i = 0; i < num_images_; ++i) {
  517.         delete display_object_cache_[i];
  518.         delete image_textures_[i];
  519.     }
  520.     delete [] image_textures_;
  521.     delete [] display_object_cache_;
  522. }
  523.  
  524. // Position the panels for a newly loaded series.
  525. void
  526. ImageSequenceViewer::position_panels()
  527. {
  528.     v3dDim panel_size = display_object_->GetSize();
  529.     int num_panels = display_objects_.size();
  530.     v3dPos panel_pos;
  531.     if (show_image_numbers_) {
  532.         panel_pos = v3dPos(-(panel_size.x() * (num_panels - 1) +
  533.             panel_spacing_ * (num_panels - 1) +
  534.             series_num_size_ + series_num_spacing_) / 2.0,
  535.             image_num_size_ + image_num_spacing_, 0);
  536.     } else {
  537.         panel_pos = v3dPos(-(panel_size.x() * (num_panels - 1) +
  538.             panel_spacing_ * (num_panels - 1)) / 2.0,
  539.             0, 0);
  540.     }
  541.     v3dOrient panel_orient = bounding_box_->GetAbsoluteOrientation();
  542.     v3dDim num_size = image_numbers_[0]->GetSize();
  543.     if (show_image_numbers_) {
  544.         bounding_box_->Add(series_number_);
  545.         series_number_->Move(panel_pos - v3dPos(
  546.             (panel_size.x() +
  547.             series_num_size_ + series_num_spacing_) / 2.0, 0, 0));
  548.         series_number_->SetAbsoluteOrientation(panel_orient);
  549.     }
  550.     for (int i = 0; i < num_panels; ++i) {
  551.         bounding_box_->Add(display_objects_[i]);
  552.         display_objects_[i]->Move(panel_pos);
  553.         display_objects_[i]->SetAbsoluteOrientation(panel_orient);
  554.         panel_pos += v3dPos(panel_size.x() + panel_spacing_, 0, 0);
  555.     }
  556. }
  557.  
  558. // Position the image numbers for a newly loaded series.
  559. void
  560. ImageSequenceViewer::position_image_numbers()
  561. {
  562.     // Set the sizes of the various parts of the image sequence viewer.
  563.     v3dDim panel_size = display_object_->GetSize();
  564.     panel_spacing_ = panel_size.x() / 20.0;
  565.     image_num_size_ = panel_size.x() / 5.0;
  566.     image_num_spacing_ = panel_spacing_;
  567.     series_num_size_ = series_number_->GetSize().x();
  568.     series_num_spacing_ = panel_size.x() / 5.0;
  569.  
  570.     v3dDim total_size = v3dDim(panel_size.x() * num_panels_ +
  571.         panel_spacing_ * (num_panels_ - 1) +
  572.         series_num_size_ + series_num_spacing_,
  573.         panel_size.y() + image_num_size_ + image_num_spacing_,
  574.         panel_size.z() + panel_spacing_);
  575.  
  576.     v3dPos panel_pos = v3dPos(-(panel_size.x() * (num_panels_ - 1) +
  577.         panel_spacing_ * (num_panels_ - 1) +
  578.         series_num_size_ + series_num_spacing_) / 2.0,
  579.         image_num_size_ + image_num_spacing_, 0);
  580.     v3dOrient panel_orient = bounding_box_->GetAbsoluteOrientation();
  581.  
  582.     char total_image_num[4];
  583.     sprintf (total_image_num, " %d", num_images_);
  584.     v3dDim num_size = image_numbers_[0]->GetSize();
  585.     v3dDim tnum_size = total_numbers_[0]->GetSize();
  586.     for (int i = 0; i < image_numbers_.size(); ++i) {
  587.         bounding_box_->Add(total_numbers_[i]);
  588.         total_numbers_[i]->MoveToOrigin();
  589.         total_numbers_[i]->Move(panel_pos + v3dDir (
  590.             (panel_size.x() - tnum_size.x()) / 2.0,
  591.             - (panel_size.y() + image_num_size_) / 2.0 -
  592.             image_num_spacing_, 0));
  593.         total_numbers_[i]->SetAbsoluteOrientation(panel_orient);
  594.  
  595.         bounding_box_->Add(image_numbers_[i]);
  596.         image_numbers_[i]->Move(panel_pos + v3dDir (
  597.             (panel_size.x() - num_size.x()) / 2.0 - tnum_size.x(),
  598.             - (panel_size.y() + image_num_size_) / 2.0 -
  599.             image_num_spacing_, 0));
  600.         image_numbers_[i]->SetAbsoluteOrientation(panel_orient);
  601.  
  602.         panel_pos += v3dPos(panel_size.x() + panel_spacing_, 0, 0);
  603.     }
  604.  
  605.  
  606. }
  607.  
  608. // Show a sequence starting with the index specified.
  609. void
  610. ImageSequenceViewer::show_images(int first_image_num)
  611. {
  612.     int i = 0;
  613.     if (show_image_numbers_) {
  614.         for (DisplayObjectArray::iterator it = display_objects_.begin();
  615.                 it != display_objects_.end(); ++it, ++i) {
  616.             image_textures_[(first_image_num + i) % num_images_]->
  617.                 ApplyToObject(**it);
  618.             image_numbers_[i]->ShowNumber((first_image_num + i) % num_images_ + 1);
  619.         }
  620.     } else {
  621.         for (DisplayObjectArray::iterator it = display_objects_.begin();
  622.                 it != display_objects_.end(); ++it, ++i) {
  623.             image_textures_[(first_image_num + i) % num_images_]->
  624.                 ApplyToObject(**it);
  625.         }
  626.     }
  627. }
  628.  
  629. // Functions that animate the sequence of images.
  630. void
  631. ImageSequenceViewer::animate_sequence_callback(const v3dEventMessage *message)
  632. {
  633.     ((ImageSequenceViewer *)message->GetEventReceiver())->animate_sequence();
  634. }
  635.  
  636. void
  637. ImageSequenceViewer::animate_sequence()
  638. {
  639.     if (--current_cycle_ < 0) {
  640.         NextImage();
  641.         current_cycle_ = cycle_delay_;
  642.     }
  643. }
  644.  
  645. void
  646. ImageSequenceViewer::animate_reverse_sequence_callback(const v3dEventMessage *message)
  647. {
  648.     ((ImageSequenceViewer *)message->GetEventReceiver())->animate_reverse_sequence();
  649. }
  650.  
  651. void
  652. ImageSequenceViewer::animate_reverse_sequence()
  653. {
  654.     if (--current_cycle_ < 0) {
  655.         PreviousImage();
  656.         current_cycle_ = cycle_delay_;
  657.     }
  658. }
  659.  
  660.