home *** CD-ROM | disk | FTP | other *** search
/ ftp.hitl.washington.edu / ftp.hitl.washington.edu.tar / ftp.hitl.washington.edu / pub / people / peter / ER / ECG_plot.cxx < prev    next >
C/C++ Source or Header  |  1998-07-07  |  25KB  |  934 lines

  1. /*
  2.  ECG_plot.cc
  3. */
  4.  
  5. #include "ECG_plot.h"
  6.  
  7. #include <iostream.h>
  8. //#include <wtpp.h>
  9.  
  10. #include "v3dSystem.h"
  11.  
  12. ///////////////////////////
  13. //    ECGPlot        //
  14. /////////////////////////
  15.  
  16. //#define FRAMERATE    // Print the frame rate at certain intervals.
  17.  
  18. const int ECGPlot::QRS_Store_Num = 4;
  19.                 // Number of QRS times to use in heart rate.
  20. const int ECGPlot::Waveform_Tess = 3;    // Tesselation of the waveform objects.
  21. const int ECGPlot::Lead_Object_Tess = 4;
  22.                     // Tesselation of the lead object.
  23.  
  24. const int ECGPlot::Default_Num_Increments = 80;
  25.                 // Default number of plot increments on an ECG plot object.
  26. const int ECGPlot::Minimum_Num_Increments = 10;
  27.                 // Minimum number of plot increments on an ECG plot object.
  28.  
  29. // Initial colors for the various parts of an ECG plot object.
  30. const v3dColor ECGPlot::ECG_plot_background_color = v3dColor (0, 0, 255);
  31. const v3dColor ECGPlot::ECG_plot_waveform_color = v3dColor (255, 255, 255);
  32. const v3dColor ECGPlot::ECG_plot_frame_color = v3dColor (255, 255, 255);
  33. const v3dColor ECGPlot::ECG_plot_lead_object_color = v3dColor (0, 255, 0);
  34.  
  35. ///////////////
  36. // CREATORS //
  37. /////////////
  38.  
  39. ECGPlot::ECGPlot (const ECGPlot& plot)
  40. {
  41.     initialize (plot.ECG_driver_, plot.length_, plot.height_,
  42.         plot.depth_, plot.num_increments_);
  43.  
  44.     paused_ = plot.paused_;
  45.  
  46.     current_x_ = plot.current_x_;
  47.     last_x_ = plot.last_x_;
  48.     last_y_ = plot.last_y_;
  49.     current_index_ = plot.current_index_;
  50.     hide_index_ = plot.hide_index_;
  51.     last_drv_index_ = plot.last_drv_index_;
  52.     QRS_store_index_ = plot.QRS_store_index_;
  53.     interval_diff_ = plot.interval_diff_;
  54.  
  55.     for (int i = 0; i < num_increments_; ++i)
  56.         plot_values_[i] = plot.plot_values_[i];
  57.  
  58.     for (int j = 0; j < QRS_Store_Num; ++j)
  59.         QRS_intervals_[j] = plot.QRS_intervals_[j];
  60.     heart_rate_ = plot.heart_rate_;
  61.  
  62.     MoveAbsolute (plot.GetAbsolutePosition());
  63.     SetAbsoluteOrientation (plot.GetAbsoluteOrientation());
  64. }
  65.  
  66. // ECGPlot::Copy()
  67. //    Creates a copy of the plot and returns a pointer to the new plot.
  68. inline v3dEntity*
  69. ECGPlot::Copy() const
  70. {
  71.     return new ECGPlot (*this);
  72. }
  73.  
  74. ///////////////////
  75. // MANIPULATORS //
  76. /////////////////
  77.  
  78. // ECGPlot::initialize
  79. //    Initializer for ECGPlot object, called by a ECGPlot constructor.
  80. //    Registers this object as a receiver of ECG data from an
  81. //    ECGDataSender object.
  82. // Parameters:
  83. //    float    length
  84. //            Horizontal extent of the ECG plot.
  85. //    float    height
  86. //            Vertical extent of the ECG plot.
  87. //    float    depth
  88. //            Thickness of the ECG object.
  89. //    int    num_increments
  90. //            Number of increments to show along the length of
  91. //            the plot.
  92. void
  93. ECGPlot::initialize (ECGDriver    *ECG_driver,
  94.     float    length, float height, float depth,
  95.     int    num_increments)
  96. {
  97.     length_ = length;
  98.     height_ = height;
  99.     depth_ = depth;
  100.     num_increments_ = num_increments;
  101.     waveform_radius_ = height_ / 100;
  102.  
  103.     // Calculate plot variables based on the client's specifications.
  104.     calculate_plot_variables();
  105.  
  106.     // Assign the ECG driver.
  107.     ECG_driver_ = 0;
  108.     SetECGDriver(ECG_driver);
  109.  
  110.     // Initialize variables used in determining plotting position.
  111.     current_x_ = increment_length_;
  112.     last_x_ = last_y_ = 0;
  113.     current_index_ = 0;
  114.     last_drv_index_ = 0;
  115.     QRS_store_index_ = 0;
  116.     interval_diff_ = 0;
  117.  
  118.     // Register this object's event handler with the ECG data sender.
  119.     RegisterEventHandler(ECG_driver_, ECG_DATA_UPDATE,
  120.         &ECG_update_callback, 0);
  121.     RegisterEventHandler(ECG_driver_, ECG_DATA_SWITCH,
  122.         &ECG_data_switch_callback, 0);
  123.  
  124.     paused_ = false;    // The ECG plot doesn't start out paused.
  125.  
  126.         // Create the background and frame.
  127.     create_background_and_frame ();
  128.  
  129.         // Create the heart rate display.
  130.     create_heart_rate_display ();
  131.     rate_display_->ShowRate (0);
  132.  
  133.     // Create the baseline and other necessary objects.
  134.     create_baseline();
  135.  
  136.     for (int i = 0; i < QRS_Store_Num; ++i)
  137.         QRS_intervals_[i] = 0;
  138. }
  139.  
  140. // ECGPlot::SetECGDriver
  141. //    Set the ECG driver that supplies the data to the ECG plot.
  142. void
  143. ECGPlot::SetECGDriver(ECGDriver *ECG_driver)
  144. {
  145.     if (!ECG_driver)
  146.         return;
  147.  
  148.     // If there is already an ECG driver set for this plot, then
  149.     // unregister the event handlers registered with that driver.
  150.     if (ECG_driver_) {
  151.         UnregisterEventHandler(ECG_driver_, ECG_DATA_UPDATE,
  152.             &ECG_update_callback);
  153.         UnregisterEventHandler(ECG_driver_, ECG_DATA_SWITCH,
  154.             &ECG_data_switch_callback);
  155.     }
  156.  
  157.     ECG_driver_ = ECG_driver;
  158.     RegisterEventHandler(ECG_driver_, ECG_DATA_UPDATE,
  159.         &ECG_update_callback, 0);
  160.     RegisterEventHandler(ECG_driver_, ECG_DATA_SWITCH,
  161.         &ECG_data_switch_callback, 0);
  162.     // Request the range of data from the driver, and calculate
  163.     // the range and scale to be used for the plot.
  164.     data_range_ = ECG_driver_->GetDataRange();
  165.     calculate_scale_variables();
  166. }
  167.  
  168. // ECGPlot::Reset
  169. //    Resets the ECG plot.  All increments are hidden and the lead object
  170. //    is returned to the origin.
  171. void
  172. ECGPlot::Reset()
  173. {
  174.     // Hide the plot line.
  175.     for (int i = 0; i < num_increments_; ++i) {
  176.         baseline_array_[i]->Disable();
  177.     }
  178.     plot_values_[0] = 0;
  179.     plot_values_[num_increments_-1] = 0;
  180.  
  181.     // Initialize variables used in determining plotting position.
  182.     current_x_ = increment_length_;
  183.     last_x_ = last_y_ = 0;
  184.     current_index_ = 0;
  185.     last_drv_index_ = 0;
  186.     QRS_store_index_ = 0;
  187.     interval_diff_ = 0;
  188.  
  189.     // Move the lead object to the origin.
  190.     lead_object_->Move (v3dPos(increment_length_ / 2, 0, 0), *origin_);
  191.  
  192.     // Set the heart rate to 0.
  193.     heart_rate_ = 0;
  194.     if (!IsHidden())
  195.         rate_display_->ShowRate (heart_rate_);
  196. }
  197.  
  198. // ECGPlot::Pause
  199. //    Pause the update of the ECG plot.  No new data will be shown until
  200. //    the plot is unpaused.
  201. void
  202. ECGPlot::Pause()
  203. {
  204.     paused_ = true;
  205. // ADD: pausing!
  206. }
  207.  
  208. // ECGPlot::Unpause
  209. //    Restart the update of the ECG plot.
  210. void
  211. ECGPlot::Unpause()
  212. {
  213.     paused_ = false;
  214. // ADD: unpausing!
  215. }
  216.  
  217. // ECGPlot::calculate_plot_variables
  218. //    Calculate plot variables based on the client's specifications.
  219. void
  220. ECGPlot::calculate_plot_variables()
  221. {
  222.     waveform_radius_ = height_ / 100;
  223.     hide_interval_ = num_increments_ / 8;
  224.     half_depth_ = depth_ / 2;
  225.     lead_object_radius_ = waveform_radius_ * 3;
  226.     frame_width_ = length_ > height_ ? length_ / 40 : height_ / 40;
  227.     background_depth_ = depth_ / 5;
  228.     rate_display_height_ = height_ / 5;
  229.     vert_buffer_ = frame_width_ * 2;
  230.     horiz_buffer_ = length_ / 100;
  231.     baseline_length_ = length_ - 2 * frame_width_ - 2 * horiz_buffer_; 
  232.     baseline_X_ = frame_width_ + horiz_buffer_;
  233.     baseline_Y_ = height_ / 2;
  234.     increment_length_ = baseline_length_ / num_increments_;
  235. }
  236.  
  237. // ECGPlot::calculate_scale_variables
  238. //    Calculate the vertical range and scale of the plot based on plot
  239. //    variables and the range of data sent by the ECG data sender.
  240. void
  241. ECGPlot::calculate_scale_variables()
  242. {
  243.     vert_plot_range_ = height_ - 2 * frame_width_ - 2 * vert_buffer_;
  244.     ECG_DATA_TYPE range = data_range_.maximum_value -
  245.                     data_range_.minimum_value;
  246.     if (range > 0)
  247.         vert_scale_ = vert_plot_range_ / range;
  248.     else
  249.         vert_scale_ = vert_plot_range_;
  250. }
  251.  
  252. // ECGPlot::create_background_and_frame
  253. //    Creates the bounding box, background and frame objects for the ECG plot.
  254. void
  255. ECGPlot::create_background_and_frame ()
  256. {
  257.     // Create the bounding box that will serve as the root of the object
  258.     // hierarchy for the component objects of the ECG.
  259.     bounding_box_ = new v3dBlockObject (length_, height_, depth_);
  260.     bounding_box_->Hide();
  261.  
  262.     // Create the ECG object background.
  263.     background_ = new v3dBlockObject (length_, height_, background_depth_);
  264.     background_->MoveAbsolute (v3dPos (0, 0,
  265.                     (depth_ - background_depth_ / 2)));
  266.  
  267.     // The background is initially hidden.
  268.     HideBackground();
  269.  
  270.     // Create the ECG object frame.
  271.     frame_bottom_ = new v3dBlockObject (length_, frame_width_, depth_);
  272.     frame_top_ = new v3dBlockObject (length_, frame_width_, depth_);
  273.     frame_left_ = new v3dBlockObject (frame_width_, height_, depth_);
  274.     frame_right_ = new v3dBlockObject (frame_width_, height_, depth_);
  275.  
  276.     frame_bottom_->MoveAbsolute (v3dPos (0, (height_ - frame_width_) / 2,
  277.         0));
  278.     frame_top_->MoveAbsolute (v3dVector (0, (height_ - frame_width_) / -2,
  279.         0));
  280.     frame_left_->MoveAbsolute (v3dVector ((length_ - frame_width_) / -2,
  281.         0, 0));
  282.     frame_right_->MoveAbsolute (v3dVector ((length_ - frame_width_) / 2,
  283.         0, 0));
  284.  
  285.     // Add the frame and the background to the bounding box.
  286.     bounding_box_->Add (background_);
  287.     bounding_box_->Add (frame_bottom_);
  288.     bounding_box_->Add (frame_top_);
  289.     bounding_box_->Add (frame_left_);
  290.     bounding_box_->Add (frame_right_);
  291.  
  292.     // Set the colors of the various elements.
  293.     background_->SetColor(ECG_plot_background_color);
  294.     frame_bottom_->SetColor(ECG_plot_frame_color);
  295.     frame_top_->SetColor(ECG_plot_frame_color);
  296.     frame_left_->SetColor(ECG_plot_frame_color);
  297.     frame_right_->SetColor(ECG_plot_frame_color);
  298.  
  299.     // Set the ECG's implementation object to the background's
  300.     // implementation object.
  301.     imp_ = bounding_box_->GetImplement();
  302.  
  303.     // The frame is initially shown.
  304.     ShowFrame();
  305. }
  306.  
  307. void
  308. ECGPlot::scale_background_and_frame(const v3dVector &factors,
  309.     const v3dPos& point)
  310. {
  311.     bounding_box_->Scale (factors, point);
  312.     background_->Scale (factors, point);
  313.     frame_top_->Scale (factors, point);
  314.     frame_bottom_->Scale (factors, point);
  315.     frame_left_->Scale (factors, point);
  316.     frame_right_->Scale (factors, point);
  317. }
  318.  
  319. void
  320. ECGPlot::delete_background_and_frame()
  321. {
  322. //    delete bounding_box_;
  323.     delete background_;
  324.     delete frame_left_;
  325.     delete frame_right_;
  326.     delete frame_top_;
  327.     delete frame_bottom_;
  328. }
  329.  
  330. // ECGPlot::create_heart_rate_display
  331. //    Creates the heart rate display and attaches it to the hierarchy.
  332. void
  333. ECGPlot::create_heart_rate_display ()
  334. {
  335.     rate_display_ = new ECGHeartRate("rcfont3d.nff");
  336.     bounding_box_->Add (rate_display_);
  337.  
  338.     rate_display_->Scale (rate_display_height_ /
  339.                     rate_display_->GetSize().y());
  340.  
  341.     // Move the rate display to the upper right of the plot.
  342.     v3dDim rsize = rate_display_->GetSize();
  343.     rate_display_->MoveAbsolute (v3dVector(
  344.                 (length_ - rsize.x()) / 2 - frame_width_,
  345.                 (rsize.y() - height_) / 2 + frame_width_, 0));
  346. }
  347.  
  348. // ECGPlot::scale_heart_rate_display
  349. //    Scales the heart rate display.
  350. void
  351. ECGPlot::scale_heart_rate_display (const v3dVector& factors,
  352.     const v3dPos& point)
  353. {
  354.     rate_display_->Scale (factors, point);
  355. }
  356.  
  357. void
  358. ECGPlot::hide_heart_rate_display()
  359. {
  360.     rate_display_->Hide();
  361. }
  362.  
  363. void
  364. ECGPlot::show_heart_rate_display()
  365. {
  366.     rate_display_->Show();
  367. }
  368.  
  369. // ECGPlot::delete_heart_rate_display
  370. //    Deletes the heart rate display.
  371. void
  372. ECGPlot::delete_heart_rate_display ()
  373. {
  374.     delete rate_display_;
  375. }
  376.  
  377. // ECGPlot::create_baseline
  378. //    Creates the ECG plot's baseline and all objects associated with it.
  379. void
  380. ECGPlot::create_baseline()
  381. {
  382.     // Create the baseline origin for the waveform.
  383. //    origin_ = new v3dSphereObject (waveform_radius_, Waveform_Tess,
  384. //        Waveform_Tess);
  385.     origin_ = new v3dBlockObject (1, 1, 1);
  386.     origin_->MoveAbsolute (v3dVector(baseline_X_ - length_ / 2,
  387.             -baseline_Y_ + height_ / 2, 0));
  388.     origin_->Disable();
  389.  
  390.     // Add the baseline origin to the background hierarchy.
  391.     bounding_box_->Add (origin_);
  392.  
  393.     // Create the lead object.
  394.     float    current_X = baseline_X_ - length_ / 2;
  395.  
  396.     lead_object_ = new v3dSphereObject (lead_object_radius_,
  397.         Lead_Object_Tess, Lead_Object_Tess);
  398.     origin_->Add (lead_object_);
  399.     lead_object_->Move (v3dPos(increment_length_ / 2, 0, 0), *origin_);
  400.     lead_object_->SetColor(ECG_plot_lead_object_color);
  401.  
  402.     // Create the individual baseline plot objects.
  403. //    baseline_array_ = (v3dLineObject **)(new (v3dLineObject *) [num_increments_]);
  404.     baseline_array_ = new v3dLineObject * [num_increments_];
  405.     plot_values_ = new ECG_DATA_TYPE [num_increments_];
  406. //    WTp3    align_vec = { 1, 0, 0 };
  407.     v3dVector    inc_vec (increment_length_, 0, 0);
  408.  
  409. //    v3dPos    from_pos (current_X, -baseline_Y_, half_depth_);
  410.     v3dPos    from_pos (current_X, 0, 0);
  411.  
  412.     for (int ci = 0; ci < num_increments_; ++ci) {
  413.         plot_values_[ci] = 0;
  414.         from_pos.x (current_X);
  415.         baseline_array_[ci]
  416.             = new v3dLineObject (from_pos, from_pos + inc_vec,
  417.                 waveform_radius_, Waveform_Tess);
  418. // CHANGE: this!
  419.         baseline_array_[ci]->SetPoints(*origin_, from_pos, from_pos + inc_vec);
  420. #if 0
  421.         baseline_array_[ci]
  422.             = new v3dLineObject (
  423.                 ((v3dEntity *)origin_)->GetAbsolutePosition(from_pos),
  424.                 ((v3dEntity *)origin_)->GetAbsolutePosition(from_pos + inc_vec),
  425.                 waveform_radius_, Waveform_Tess);
  426. #endif
  427.  
  428.         // Add a baseline plot object to the baseline origin.
  429.         origin_->Add (baseline_array_[ci]);
  430.  
  431.         baseline_array_[ci]->Disable();
  432.  
  433.         // Color the baseline objects.
  434.         baseline_array_[ci]->SetColor(ECG_plot_waveform_color);
  435.  
  436.         current_X += increment_length_;
  437.     }
  438.  
  439.     lead_object_->SetColor(ECG_plot_lead_object_color);
  440. }
  441.  
  442. void
  443. ECGPlot::delete_baseline()
  444. {
  445.     for (int ci = 0; ci < num_increments_; ci++)
  446.         delete baseline_array_[ci];
  447.     delete origin_;
  448.     delete lead_object_;
  449. }
  450.  
  451. // ECGPlot::ECG_update_callback
  452. //    Event handler callback function, notified every cycle to request
  453. //    the latest data from the ECG driver.
  454. void
  455. ECGPlot::ECG_update_callback (const v3dEventMessage *message)
  456. {
  457. #ifdef FRAMERATE
  458.     static int frame_count = 0;
  459.  
  460.     if (++frame_count == 40) {
  461.         cerr << "Frame rate: " << WTuniverse_framerate() << endl;
  462.         frame_count = 0;
  463.     }
  464. #endif    // FRAMERATE
  465.     ((ECGPlot *)message->GetEventReceiver())->plot_new_data ();
  466. }
  467.  
  468. // ECGPlot::ECG_data_switch_callback
  469. //    Event handler callback function, notified whenever the data stream
  470. //    of the ECG driver is switched.
  471. void
  472. ECGPlot::ECG_data_switch_callback (const v3dEventMessage *message)
  473. {
  474.     ((ECGPlot *)message->GetEventReceiver())->switch_data();
  475. }
  476.  
  477. // ECGPlot::plot_new_data
  478. //    Copies the latest data from the ECG driver, then plots any new
  479. //    data on the ECG.
  480. void
  481. ECGPlot::plot_new_data ()
  482. {
  483.     int current_drv_index = ECG_driver_->GetCurrentDataIndex();
  484.  
  485.     // If there's no new data to be displayed, then just return.
  486.     if (last_drv_index_ == current_drv_index)
  487.         return;
  488.  
  489.     int    new_index = current_index_;
  490.     bool    recalculate_hr = false;
  491.  
  492.     // Copy the data from the ECG driver.
  493.     do {
  494.         last_drv_index_ =
  495.             ECG_driver_->GetNextDataIndex(last_drv_index_);
  496.         ++interval_diff_;
  497.  
  498.         // If a QRS event occured then calculate the number of
  499.         // intervals since the last QRS event, store that number in
  500.         // a list of past intervals, and set the recaculate-heart-rate
  501.         // flag to true.
  502.         if (ECG_driver_->QRS_list[last_drv_index_] == 2) {
  503.                         QRS_intervals_[QRS_store_index_] = interval_diff_;
  504.                         QRS_store_index_ =  (QRS_store_index_ + 1) %
  505.                                                         QRS_Store_Num;
  506.                         interval_diff_ = 0;
  507.                         recalculate_hr = true;
  508.         }
  509.  
  510.         plot_values_[new_index++] =
  511.                 ECG_driver_->data_list[last_drv_index_];
  512.         if (new_index >= num_increments_)
  513.             new_index = 0;
  514.     } while (last_drv_index_ != current_drv_index);
  515.  
  516.     // Plot the new data.
  517.     if (!IsHidden())
  518.         plot_data (new_index);
  519.  
  520.     if (recalculate_hr) {
  521.         float    avg_interval = 0.0;
  522.         for (int i = 0; i < QRS_Store_Num; ++i)
  523.             avg_interval += QRS_intervals_[i];
  524.         avg_interval /= QRS_Store_Num;
  525.         if (avg_interval > 0)
  526.             heart_rate_ = 60000.0 / (avg_interval *
  527.                         ECGDriver::ms_Interval);
  528.         else
  529.             heart_rate_ = 0;
  530.         if (!IsHidden())
  531.             rate_display_->ShowRate (heart_rate_);
  532.     }
  533.  
  534.     current_index_ = new_index;
  535. }
  536.  
  537. // ECGPlot::plot_data
  538. //    Plots the ECG data on the waveform up to the new index specified.
  539. // Parameters:
  540. //    int new_index
  541. //        The last index of the plot data to be shown.
  542. void
  543. ECGPlot::plot_data (int new_index)
  544. {
  545.     float    this_y;
  546.     v3dPos    new_pos,
  547.         last_pos = v3dVector(last_x_, -(last_y_), 0);
  548.  
  549.     while (current_index_ != new_index) {
  550.         this_y = plot_values_[current_index_] * vert_scale_;
  551.         new_pos = v3dVector(current_x_, -(this_y), 0);
  552.  
  553.         hide_index_ = (current_index_ + hide_interval_)
  554.             % num_increments_;
  555.         baseline_array_[hide_index_]->Disable();
  556.  
  557. // cout << "data:" << this_y << " diff: " << diff_y << " ";
  558.  
  559.         baseline_array_[current_index_]->SetPoints(*origin_,
  560.                             last_pos, new_pos);
  561.         baseline_array_[current_index_]->Enable();
  562.  
  563.         // Color the plot.
  564.         baseline_array_[current_index_]->SetColor(
  565.                         ECG_plot_waveform_color);
  566.  
  567.         last_y_ = this_y;
  568.         last_pos = new_pos;
  569.         ++current_index_;
  570.         if (current_index_ >= num_increments_) {
  571.             current_index_ = 0;
  572.             current_x_ = increment_length_;
  573.             last_x_ = 0;
  574.             last_pos.x(0);
  575.         } else {
  576.             last_x_ = current_x_;
  577.             current_x_ += increment_length_;
  578.         }
  579.     }
  580.     lead_object_->Move(new_pos);
  581. }
  582.  
  583. void
  584. ECGPlot::scale_baseline (const v3dVector &factors, const v3dPos& point)
  585. {
  586.     origin_->Scale(factors, point);
  587.  
  588.     float current_x = increment_length_, last_x = 0;
  589.     float last_y = plot_values_[num_increments_ - 1] * vert_scale_;
  590.  
  591.     for (int i = 0; i < num_increments_; ++i) {
  592.         float    this_y = plot_values_[i] * vert_scale_;
  593. #if 0
  594.         v3dPos    new_pos = origin_->GetAbsolutePosition()
  595.                 + v3dVector(current_x, -(this_y), 0),
  596.             last_pos = origin_->GetAbsolutePosition()
  597.                 + v3dVector(last_x, -(last_y), 0);
  598. #endif
  599.         v3dPos    new_pos = v3dVector(current_x, -(this_y), 0),
  600.             last_pos = v3dVector(last_x, -(last_y), 0);
  601.  
  602.         delete baseline_array_[i];
  603.         baseline_array_[i]
  604.             = new v3dLineObject (last_pos, new_pos,
  605.                 waveform_radius_, Waveform_Tess);
  606.         origin_->Add (baseline_array_[i]);
  607. // CHANGE: this!
  608.         baseline_array_[i]->SetPoints(*origin_, last_pos, new_pos);
  609.  
  610. //        baseline_array_[i]->SetPoints(last_pos, new_pos);
  611.         last_x = current_x;
  612.         last_y = this_y;
  613.         current_x += increment_length_;
  614.     }
  615.  
  616.     if (IsHidden())
  617.         for (int i = 0; i < num_increments_; ++i)
  618.             baseline_array_[i]->Disable();
  619.  
  620.  
  621.     // Recalculate plotting position variables.
  622. //    last_x_ *= factors.x();
  623. //    current_x_ *= factors.x();
  624. //    if (current_index_ == 0)
  625. //        last_x_ = 0;
  626. //    else
  627.     last_x_ = current_index_ * increment_length_;
  628.     current_x_ = (current_index_+1) * increment_length_;
  629.     last_y_ *= factors.y();
  630. //    last_y_ = plot_values_ [current_index_] * vert_scale_;
  631.  
  632.     delete lead_object_;
  633.     lead_object_ = new v3dSphereObject (lead_object_radius_,
  634.         Lead_Object_Tess, Lead_Object_Tess);
  635.     origin_->Add(lead_object_);
  636.  
  637.     lead_object_->Move(v3dVector(last_x_, -(last_y_), 0));
  638.     lead_object_->SetColor(ECG_plot_lead_object_color);
  639. //    lead_object_->Move(origin_->GetAbsolutePosition()
  640. //                + v3dVector(last_x_, -(last_y_), 0));
  641.     if (IsHidden())
  642.         lead_object_->Hide();
  643. }
  644.  
  645. // ECGPlot::fix_hide_interval
  646. //    Re-hides objects along the hide interval to fix any errors introduced
  647. //    when the sweep speed was changed.
  648. void
  649. ECGPlot::fix_hide_interval ()
  650. {
  651.     hide_interval_ = num_increments_ / 8;
  652.  
  653.     int    current_index = current_index_;
  654.     for (int i = 0; i < hide_interval_; ++i, ++current_index) {
  655.         if (current_index >= num_increments_)
  656.             current_index = 0;
  657.         baseline_array_[current_index]->Disable();
  658.     }
  659. }
  660.  
  661. // ECGPlot::extend_baseline
  662. //    Extends the baseline to the number of increments specified.
  663. //    The old baseline array is copied to a new baseline array, and the
  664. //    new elements in the array are initialized with flat baseline
  665. //    elements.
  666. // Parameters:
  667. //    unsigned int    old_increment_num
  668. //        Old number of increments on the the baseline.
  669. //    unsigned int    new_increment_num
  670. //        New number of increments on the the baseline.
  671. void
  672. ECGPlot::extend_baseline (unsigned int old_increment_num,
  673.     unsigned int new_increment_num)
  674. {
  675.     ASSERT (new_increment_num >= old_increment_num);
  676.  
  677.     v3dLineObject    **new_baseline_array
  678. //        = (v3dLineObject **)(new (v3dLineObject *) [new_increment_num]);
  679.         = new v3dLineObject * [new_increment_num];
  680.     ECG_DATA_TYPE    *new_plot_values
  681.         = new ECG_DATA_TYPE [new_increment_num];
  682.  
  683.     // Copy the old baseline elements into the new array.
  684.     for (int i = 0; i < old_increment_num; ++i) {
  685.         new_baseline_array[i] = baseline_array_[i];
  686.         new_plot_values [i] = plot_values_ [i];
  687.     }
  688.     delete [] baseline_array_;
  689.     delete [] plot_values_;
  690.  
  691.     // Create new, flat baseline objects.
  692.     v3dVector    inc_vec (increment_length_, 0, 0);
  693.     float    current_X = old_increment_num * increment_length_;
  694.  
  695.     v3dPos    from_pos (current_X, 0, 0);
  696. //    v3dPos    from_pos (current_X, -baseline_Y_, half_depth_);
  697.  
  698.     for (int ci = old_increment_num; ci < new_increment_num; ++ci) {
  699.         new_plot_values[ci] = 0;
  700.         from_pos.x (current_X);
  701. #if 0
  702.         new_baseline_array[ci]
  703.             = new v3dLineObject (
  704.                 ((v3dEntity *)origin_)->GetAbsolutePosition(from_pos),
  705.                 ((v3dEntity *)origin_)->GetAbsolutePosition(from_pos + inc_vec),
  706.                 waveform_radius_, Waveform_Tess);
  707. #endif
  708.         new_baseline_array[ci]
  709.             = new v3dLineObject (from_pos, from_pos + inc_vec,
  710.                 waveform_radius_, Waveform_Tess);
  711. // CHANGE: this!
  712.         new_baseline_array[ci]->SetPoints(*origin_, from_pos, from_pos + inc_vec);
  713.  
  714.         // Add a baseline plot object to the baseline origin.
  715.         origin_->Add (new_baseline_array[ci]);
  716.  
  717. #if 0
  718.         new_baseline_array[ci]->Disable();
  719. #endif
  720.  
  721.         // Color the baseline objects.
  722.         new_baseline_array[ci]->SetColor(ECG_plot_waveform_color);
  723.  
  724.         current_X += increment_length_;
  725.     }
  726.  
  727.     baseline_array_ = new_baseline_array;
  728.     plot_values_ = new_plot_values;
  729. }
  730.  
  731. // ECGPlot::shrink_baseline
  732. //    Shrinks the baseline to the number of increments specified.
  733. //    The old baseline array is copied to a new baseline array, leaving
  734. //    out extra elements farthest from the leading edge of the graph.
  735. // Parameters:
  736. //    unsigned int    old_increment_num
  737. //        Old number of increments on the the baseline.
  738. //    unsigned int    new_increment_num
  739. //        New number of increments on the the baseline.
  740. void
  741. ECGPlot::shrink_baseline (unsigned int old_increment_num,
  742.     unsigned int new_increment_num)
  743. {
  744.     ASSERT (new_increment_num <= old_increment_num);
  745.  
  746.     v3dLineObject    **new_baseline_array
  747. //        = (v3dLineObject **)(new (v3dLineObject *) [new_increment_num]);
  748.         = new v3dLineObject * [new_increment_num];
  749.     ECG_DATA_TYPE    *new_plot_values
  750.         = new ECG_DATA_TYPE [new_increment_num];
  751.  
  752.     // Copy the old baseline elements into the new array.
  753.     int    start_index, finish_index;
  754.     if (current_index_ >= new_increment_num) {
  755.         start_index = current_index_ - new_increment_num + 1;
  756.         finish_index = current_index_ + 1;
  757.         current_index_ = new_increment_num - 1;
  758.         current_x_ = current_index_ * increment_length_;
  759.         last_x_ = (current_index_-1) * increment_length_;
  760.     } else {
  761.         start_index = 0;
  762.         finish_index = new_increment_num;
  763.     }
  764.     for (int j = 0; j < start_index; ++j)
  765.         delete baseline_array_[j];
  766.     for (int i = start_index; i < finish_index; ++i) {
  767.         new_baseline_array[i-start_index] = baseline_array_[i];
  768.         new_plot_values [i-start_index] = plot_values_ [i];
  769.     }
  770.     for (int k = finish_index; k < old_increment_num; ++k)
  771.         delete baseline_array_[k];
  772.  
  773.     delete [] baseline_array_;
  774.     delete [] plot_values_;
  775.  
  776.     baseline_array_ = new_baseline_array;
  777.     plot_values_ = new_plot_values;
  778. }
  779.  
  780. void
  781. ECGPlot::hide_baseline()
  782. {
  783.     lead_object_->Hide();
  784.  
  785.     for (int i = 0; i < num_increments_; ++i)
  786.         baseline_array_[i]->Disable();
  787. }
  788.  
  789. void
  790. ECGPlot::show_baseline()
  791. {
  792.     lead_object_->Show();
  793. }
  794.  
  795. void
  796. ECGPlot::Hide()
  797. {
  798.     background_->Hide();
  799.  
  800.     frame_left_->Hide();
  801.     frame_right_->Hide();
  802.     frame_top_->Hide();
  803.     frame_bottom_->Hide();
  804.  
  805.     hide_baseline();
  806.     hide_heart_rate_display();
  807.     hidden_ = true;
  808. }
  809.  
  810. void
  811. ECGPlot::Show()
  812. {
  813.     if (!background_hidden_)
  814.         ShowBackground();
  815.  
  816.     if (!frame_hidden_)
  817.         ShowFrame();
  818.  
  819.     show_baseline();
  820.     show_heart_rate_display();
  821.     hidden_ = false;
  822. }
  823.  
  824. void
  825. ECGPlot::Disable()
  826. {
  827.     disabled_ = true;
  828. }
  829.  
  830. void
  831. ECGPlot::Enable()
  832. {
  833.     disabled_ = false;
  834. }
  835.  
  836. void
  837. ECGPlot::HideFrame ()
  838. {
  839.     frame_left_->Hide();
  840.     frame_right_->Hide();
  841.     frame_top_->Hide();
  842.     frame_bottom_->Hide();
  843.     frame_hidden_ = true;
  844. }
  845.  
  846. void
  847. ECGPlot::ShowFrame ()
  848. {
  849.     frame_left_->Show();
  850.     frame_right_->Show();
  851.     frame_top_->Show();
  852.     frame_bottom_->Show();
  853.     frame_hidden_ = false;
  854. }
  855.  
  856.  
  857. // ECGPlot::Scale
  858. //    Stretches the ECG plot about a point by the given factors.
  859. void
  860. ECGPlot::Scale (const v3dVector &factors, const v3dPos& point)
  861. {
  862.     scale_background_and_frame(factors, point);
  863.  
  864.     v3dDim dim = bounding_box_->GetSize();
  865.     length_ = dim.x();
  866.     height_ = dim.y();
  867.     depth_ = dim.z();
  868.  
  869.     calculate_plot_variables();
  870.     calculate_scale_variables();
  871.  
  872.     scale_heart_rate_display(factors, point);
  873.  
  874.     scale_baseline(factors, point);
  875.  
  876.     fix_hide_interval();
  877. }
  878.  
  879. // ECGPlot::ChangeSweepSpeed
  880. //    Increases or decreases the sweep speed of the ECG plot by the
  881. //    specified factor.
  882. //    The number of increments on the baseline is either increased or
  883. //    decreased, and the baseline as a whole is scaled up or down.
  884. // Parameters:
  885. //    float    change_factor
  886. //            Factor to change the speed by.  For example,
  887. //                1 = no change,
  888. //                1.05 = 5% faster,
  889. //                0.95 = 5% slower
  890. void
  891. ECGPlot::ChangeSweepSpeed (float change_factor)
  892. {
  893.     int new_num_increments = (int)trunc (num_increments_ * change_factor);
  894.     if (new_num_increments < Minimum_Num_Increments)
  895.         new_num_increments = Minimum_Num_Increments;
  896.  
  897.     if (change_factor > 1)
  898.         extend_baseline (num_increments_, new_num_increments);
  899.     else
  900.         shrink_baseline (num_increments_, new_num_increments);
  901.     num_increments_ = new_num_increments;
  902.  
  903.     increment_length_ = baseline_length_ / new_num_increments;
  904.     scale_baseline (v3dVector (1/change_factor, 1, 1),
  905.                 origin_->GetAbsolutePosition());
  906.  
  907.     fix_hide_interval();
  908. }
  909.  
  910. // ECGPlot::~ECGPlot
  911. ECGPlot::~ECGPlot ()
  912. {
  913.     delete_heart_rate_display();
  914.     delete_baseline();
  915.     delete_background_and_frame();
  916. }
  917.  
  918. void
  919. ECGPlot::switch_data ()
  920. {
  921.     last_drv_index_ = 0;
  922.     data_range_ = ECG_driver_->GetDataRange();
  923.     calculate_scale_variables();
  924. }
  925.  
  926. void
  927. ECGPlot::scale_data (float scale)
  928. {
  929.     last_drv_index_ = 0;
  930.     data_range_ = ECG_driver_->GetDataRange();
  931.     calculate_scale_variables();
  932. }
  933.  
  934.