Isis 3 Programmer Reference
ViewportBuffer.cpp
1 #include "IsisDebug.h"
2 
3 #include "ViewportBuffer.h"
4 #include "ViewportBufferAction.h"
5 #include "ViewportBufferStretch.h"
6 #include "ViewportBufferFill.h"
7 #include "ViewportBufferTransform.h"
8 
9 #include <QApplication>
10 #include <QQueue>
11 #include <QRect>
12 #include <QScrollBar>
13 
14 #include "Brick.h"
15 #include "CubeDataThread.h"
16 #include "CubeViewport.h"
17 #include "SpecialPixel.h"
18 #include "PixelType.h"
19 
20 
21 #define round(x) ((x) > 0.0 ? (x) + 0.5 : (x) - 0.5)
22 
23 
24 using namespace std;
25 
26 
27 namespace Isis {
36  ViewportBuffer::ViewportBuffer(CubeViewport *viewport,
37  CubeDataThread *cubeData,
38  int cubeId) {
39  p_dataThread = cubeData;
40  p_cubeId = cubeId;
41 
42  p_actions = 0;
43 
44  p_actions = new QQueue< ViewportBufferAction * >();
45  p_viewport = viewport;
46  p_bufferInitialized = false;
47  p_band = -1;
48  p_enabled = true;
49  p_initialStretchDone = false;
50  p_viewportHeight = p_viewport->viewport()->height();
51  p_oldViewportHeight = p_viewport->viewport()->height();
52  p_vertScrollBarPos = p_viewport->verticalScrollBar()->value();
53  p_oldVertScrollBarPos = p_viewport->verticalScrollBar()->value();
54 
55  p_requestedFillArea = 0.0;
56  p_bricksOrdered = true;
57 
58  connect(this, SIGNAL(ReadCube(int, int, int, int, int, int, void *)),
59  p_dataThread, SLOT(ReadCube(int, int, int, int, int, int, void *)));
60 
61  connect(p_dataThread, SIGNAL(ReadReady(void *, int, const Isis::Brick *)),
62  this, SLOT(DataReady(void *, int, const Isis::Brick *)));
63 
64  connect(this, SIGNAL(DoneWithData(int, const Isis::Brick *)),
65  p_dataThread, SLOT(DoneWithData(int, const Isis::Brick *)));
66  }
67 
72  ViewportBuffer::~ViewportBuffer() {
73  disconnect(this, SIGNAL(ReadCube(int, int, int, int, int, int, void *)),
74  p_dataThread, SLOT(ReadCube(int, int, int, int, int, int, void *)));
75 
76  disconnect(p_dataThread, SIGNAL(ReadReady(void *, int, const Isis::Brick *)),
77  this, SLOT(DataReady(void *, int, const Isis::Brick *)));
78 
79  disconnect(this, SIGNAL(DoneWithData(int, const Isis::Brick *)),
80  p_dataThread, SLOT(DoneWithData(int, const Isis::Brick *)));
81 
82  p_dataThread = NULL;
83 
84  if(p_actions) {
85  while(!p_actions->empty()) {
86  ViewportBufferAction *action = p_actions->dequeue();
87  if(action) {
88  delete action;
89  action = NULL;
90  }
91  }
92  delete p_actions;
93  p_actions = NULL;
94  }
95 
96  emptyBuffer(true);
97  }
98 
99 
106  void ViewportBuffer::fillBuffer(QRect rect) {
107  if(p_band == -1) {
108  throw IException(IException::Programmer, "invalid band", _FILEINFO_);
109  }
110 
111  ViewportBufferFill *newFill = createViewportBufferFill(
112  rect.intersected(bufferXYRect()), false);
113  enqueueAction(newFill);
114  doQueuedActions();
115  }
116 
117 
125  void ViewportBuffer::fillBuffer(QRect rect, const Brick *data) {
126  if(p_band == -1) {
127  throw IException(IException::Programmer, "invalid band", _FILEINFO_);
128  }
129 
130  rect = rect.intersected(bufferXYRect());
131 
132  if (!rect.isValid())
133  return;
134 
135  try {
136  ViewportBufferFill *fill = createViewportBufferFill(rect, false);
137 
138  while(fill->shouldRequestMore()) {
139  fill->incRequestPosition();
140  fill->incReadPosition();
141 
142  for(int x = rect.left(); x <= rect.right(); x ++) {
143  // Index into internal buffer is minus leftmost/topmost pixel
144  int xIndex = x - fill->getLeftmostPixelPosition();
145  int yIndex = fill->getRequestPosition() -
146  fill->getTopmostPixelPosition();
147 
148  double samp = fill->viewportToSample(x);
149  double line = fill->viewportToLine(fill->getRequestPosition());
150  if (samp < data->Sample())
151  samp = data->Sample();
152  if (samp > data->Sample() + data->SampleDimension())
153  samp = data->Sample() + data->SampleDimension();
154  if (line < data->Line())
155  line = data->Line();
156  if (line > data->Line() + data->LineDimension())
157  line = data->Line() + data->LineDimension();
158 
159  // Index into buffer is current sample - start sample
160  // *Brick indices are in units of cube pixels, not screen pixels
161  int brickIndex = data->Index((int)(samp + 0.5), (int)(line + 0.5),
162  p_band);
163 
164  if(brickIndex < 0) {
165  p_buffer.at(yIndex).at(xIndex) = data->at(0);
166  }
167  else if(brickIndex >= data->size()) {
168  p_buffer.at(yIndex).at(xIndex) = data->at(data->size() - 1);
169  }
170  else {
171  if(yIndex < 0 || xIndex < 0 || yIndex >= (int) p_buffer.size() ||
172  xIndex >= (int) p_buffer.at(yIndex).size()) {
173  throw IException(IException::Programmer,
174  "index out of range",
175  _FILEINFO_);
176  }
177  else {
178  p_buffer.at(yIndex).at(xIndex) = data->at(brickIndex);
179  }
180  }
181  }
182  }
183  }
184  catch (IException &e) {
185  throw IException(e, IException::Programmer, "Failed to load brick "
186  "into buffer", _FILEINFO_);
187  }
188  }
189 
190 
200  void ViewportBuffer::DataReady(void *requester, int cubeId,
201  const Brick *brick) {
202  if(this != requester)
203  return;
204 
205  if(p_actions->empty()) {
206  throw IException(IException::Programmer, "no actions", _FILEINFO_);
207  }
208 
209  ViewportBufferAction *curAction = p_actions->head();
210 
211  if(curAction->getActionType() != ViewportBufferAction::fill ||
212  !curAction->started()) {
213  throw IException(IException::Programmer, "not a fill action", _FILEINFO_);
214  }
215 
216  ViewportBufferFill *fill = (ViewportBufferFill *) curAction;
217 
218  const QRect *rect = fill->getRect();
219 
220  int y = fill->getReadPosition(); // screen line
221 
222  // check to see if the next screen line's brick differs from this screen
223  // line's brick. If it does, which brick do we use?
224  int curBrickLine = (int) (fill->viewportToLine(y) + 0.5);
225  int nextBrickLine = (int) (fill->viewportToLine(y + 1) + 0.5);
226  bool brickOrderCorrection = p_bricksOrdered;
227  if (curBrickLine != nextBrickLine &&
228  nextBrickLine == (int) (brick->Line() + 0.5)) {
229  y++;
230  p_bricksOrdered = false;
231  }
232  else {
233  p_bricksOrdered = true;
234  }
235 
236  double samp;
237 
238  // Loop through x values of rect on screen that we want to fill
239  for(int x = rect->left(); x <= rect->right(); x++) {
240  // Index into internal buffer is minus leftmost/topmost pixel
241  int xIndex = x - fill->getLeftmostPixelPosition();
242  int yIndex = y - fill->getTopmostPixelPosition();
243 
244  samp = fill->viewportToSample(x);
245 
246  // Index into buffer is current sample - start sample
247  // *Brick indices are in units of cube pixels, not screen pixels
248  int brickIndex = (int)(samp + 0.5) - brick->Sample();
249 
250  if(brickIndex < 0) {
251  p_buffer.at(yIndex).at(xIndex) = brick->at(0);
252  }
253  else if(brickIndex >= brick->size()) {
254  p_buffer.at(yIndex).at(xIndex) = brick->at(brick->size() - 1);
255  }
256  else {
257  if(yIndex < 0 || xIndex < 0 || yIndex >= (int) p_buffer.size() ||
258  xIndex >= (int) p_buffer.at(yIndex).size()) {
259  IString msg = "An index out of range error was detected. ";
260 
261  if(yIndex < 0)
262  msg += "The Y-Index [" + IString(yIndex) + "] is less than 0";
263  else if(xIndex < 0)
264  msg += "The X-Index [" + IString(xIndex) + "] is less than 0";
265  else if(yIndex > (int)p_buffer.size())
266  msg += "The Y-Index [" + IString(yIndex) + "] is greater than the "
267  "Y-Size of [" + IString((int)p_buffer.size()) + "]";
268  else if(xIndex > (int)p_buffer.at(yIndex).size())
269  msg += "The X-Index [" + IString(xIndex) + " is greater than the "
270  "X-Size of [" + IString((int) p_buffer.at(yIndex).size()) + "]";
271 
272  throw IException(IException::Programmer, msg, _FILEINFO_);
273  }
274  else {
275  p_buffer.at(yIndex).at(xIndex) = brick->at(brickIndex);
276  }
277  }
278  }
279  fill->incReadPosition();
280 
281 
282  if(fill->shouldRequestMore()) {
283  if (p_bricksOrdered) {
284  requestCubeLine(fill);
285  }
286  else {
287  if (brickOrderCorrection) {
288  requestCubeLine(fill);
289  requestCubeLine(fill);
290  }
291  }
292  }
293  else if(fill->doneReading()) {
294  delete fill;
295  fill = NULL;
296  p_actions->dequeue();
297  doQueuedActions();
298  }
299 
300  emit DoneWithData(cubeId, brick);
301  }
302 
303 
312  void ViewportBuffer::enqueueAction(ViewportBufferAction *action) {
313  if(action->getActionType() == ViewportBufferAction::fill) {
314  QRect *fillRect = ((ViewportBufferFill *)action)->getRect();
315  p_requestedFillArea += fillRect->width() * fillRect->height();
316  }
317 
318  if(p_actions->empty()) {
319  p_viewport->enableProgress();
320  }
321 
322  p_actions->enqueue(action);
323  }
324 
325 
334  const vector<double> &ViewportBuffer::getLine(int line) {
335  if(!p_bufferInitialized || !p_enabled) {
336  throw IException(IException::Programmer, "no data", _FILEINFO_);
337  }
338 
339  if(line < 0 || line >= (int)p_buffer.size()) {
340  throw IException(IException::Programmer,
341  "Invalid call to getLine",
342  _FILEINFO_);
343  }
344 
345  return p_buffer.at(line);
346  }
347 
348 
356  QRect ViewportBuffer::getXYBoundingRect() {
357  int startx, starty, endx, endy;
358  p_viewport->cubeToViewport(0.5, 0.5, startx, starty);
359 
360  // Handle case where x,y 0,0 is sample,line 0,0 (which is outside the cube)
361  // and cubeToViewport still tells us 0.5, 0.5 is at x,y 0,0.
362  double startSamp, startLine;
363  p_viewport->viewportToCube(startx, starty, startSamp, startLine);
364 
365  if(startSamp < 0.5)
366  startx ++;
367 
368  if(startLine < 0.5)
369  starty ++;
370 
371  double rightmost = p_viewport->cubeSamples() + 0.5;
372  double bottommost = p_viewport->cubeLines() + 0.5;
373 
374  p_viewport->cubeToViewport(rightmost, bottommost, endx, endy);
375 
376  if(endx < 0 || endy < 0)
377  return QRect();
378 
379  double endSamp = -1, endLine = -1;
380  p_viewport->viewportToCube(endx, endy, endSamp, endLine);
381 
382  if(endSamp > rightmost)
383  endx --;
384 
385  if(endLine > bottommost)
386  endy --;
387 
388  // Make sure our rect makes sense
389  if(startx < 0) {
390  startx = 0;
391  }
392 
393  if(starty < 0) {
394  starty = 0;
395  }
396 
397  if(endx >= p_viewport->viewport()->width()) {
398  endx = p_viewport->viewport()->width() - 1;
399  }
400 
401  if(endy >= p_viewport->viewport()->height()) {
402  endy = p_viewport->viewport()->height() - 1;
403  }
404 
405  return QRect(startx, starty, endx - startx + 1, endy - starty + 1);
406  }
407 
408 
415  bool ViewportBuffer::hasEntireCube() {
416  double sampTolerance = 0.05 * p_viewport->cubeSamples();
417  double lineTolerance = 0.05 * p_viewport->cubeLines();
418 
419  bool hasCube = true;
420 
421  hasCube &= !working();
422  hasCube &= p_sampLineBoundingRect[rectLeft] <= (1 + sampTolerance);
423  hasCube &= p_sampLineBoundingRect[rectTop] <= (1 + lineTolerance);
424  hasCube &= p_sampLineBoundingRect[rectRight] >= (p_viewport->cubeSamples() -
425  sampTolerance);
426  hasCube &= p_sampLineBoundingRect[rectBottom] >= (p_viewport->cubeLines() -
427  lineTolerance);
428  return hasCube;
429  }
430 
431 
439  QList<double> ViewportBuffer::getSampLineBoundingRect() {
440  QRect xyRect = getXYBoundingRect();
441  double ssamp, esamp, sline, eline;
442  p_viewport->viewportToCube(xyRect.left(), xyRect.top(), ssamp, sline);
443  p_viewport->viewportToCube(xyRect.right(), xyRect.bottom(), esamp, eline);
444 
445  QList<double> boundingRect;
446 
447  boundingRect.insert(rectLeft, ssamp);
448  boundingRect.insert(rectTop, sline);
449  boundingRect.insert(rectRight, esamp);
450  boundingRect.insert(rectBottom, eline);
451 
452  return boundingRect;
453  }
454 
455 
460  void ViewportBuffer::updateBoundingRects() {
461  p_oldXYBoundingRect = p_XYBoundingRect;
462  p_XYBoundingRect = getXYBoundingRect();
463 
464  p_oldSampLineBoundingRect = p_sampLineBoundingRect;
465  p_sampLineBoundingRect = getSampLineBoundingRect();
466 
467  p_oldViewportHeight = p_viewportHeight;
468  p_viewportHeight = p_viewport->viewport()->height();
469 
470  p_oldVertScrollBarPos = p_vertScrollBarPos;
471  // Add +1 to remove the black line at the top
472  p_vertScrollBarPos = p_viewport->verticalScrollBar()->value() + 1;
473  }
474 
475 
488  ViewportBufferFill *ViewportBuffer::createViewportBufferFill(
489  QRect someRect, bool useOldY) {
490  QScrollBar *hsb = p_viewport->horizontalScrollBar();
491  int xConstCoef = hsb->value();
492  xConstCoef -= p_viewport->viewport()->width() / 2;
493 
494  // If panning over a full screen, it will try to create a fill rect that
495  // isn't actually valid. So, in the case of any bad fill rects we will
496  // fill everything.
497  if(!someRect.isValid()) {
498  throw IException(IException::Programmer, "Fill rect invalid", _FILEINFO_);
499  }
500 
501  double xScale = p_viewport->scale();
502 
503  int yConstCoef = 0;
504 
505  if(!useOldY)
506  yConstCoef = (p_vertScrollBarPos) - p_viewportHeight / 2 - 1;
507  else
508  yConstCoef = (p_oldVertScrollBarPos) - p_oldViewportHeight / 2 - 1;
509 
510  double yScale = xScale;
511 
512  QPoint topLeft;
513 
514  if(!useOldY) {
515  topLeft = p_XYBoundingRect.topLeft();
516  }
517  else {
518  topLeft = QPoint(p_XYBoundingRect.left(), p_oldXYBoundingRect.top());
519  }
520 
521  ViewportBufferFill *newFill = new ViewportBufferFill(someRect, xConstCoef,
522  xScale, yConstCoef, yScale, topLeft);
523 
524  return newFill;
525  }
526 
527 
533  void ViewportBuffer::requestCubeLine(ViewportBufferFill *fill) {
534  if(p_band == -1) {
535  throw IException(IException::Programmer, "invalid band", _FILEINFO_);
536  }
537 
538  // Prep to create minimal buffer(s) to read the cube
539  QRect &rect = *fill->getRect();
540 
541  double ssamp = fill->viewportToSample(rect.left());
542 
543  double esamp = fill->viewportToSample(rect.right());
544 
545  int brickWidth = (int)(ceil(esamp) - floor(ssamp)) + 1;
546 
547  if(brickWidth <= 0)
548  return;
549 
550  double line = fill->viewportToLine(fill->getRequestPosition());
551  int roundedSamp = (int)(ssamp + 0.5);
552  int roundedLine = (int)(line + 0.5);
553 
554  emit ReadCube(p_cubeId, roundedSamp, roundedLine, roundedSamp + brickWidth,
555  roundedLine, p_band, this);
556 
557  fill->incRequestPosition();
558  }
559 
560 
571  void ViewportBuffer::doQueuedActions() {
572  bool doNextAction = false;
573 
574  ViewportBufferAction *curAction = NULL;
575 
576  // if we aren't preserving data, and we don't still need the initial
577  // stretch (on startup), let's reset the buffer.
578  if(!reinitializeActionExists() && !actionsPreserveData() &&
579  p_initialStretchDone) {
580  // Actions don't preserve data - call reinitialize!
581  reinitialize();
582  }
583 
584  if(!working()) {
585  p_requestedFillArea = 0.0;
586  }
587 
588  if(!p_actions->empty()) {
589  curAction = p_actions->head();
590  doNextAction = !curAction->started();
591  }
592 
593  while(doNextAction) {
594  if(curAction->getActionType() == ViewportBufferAction::transform) {
595  doTransformAction((ViewportBufferTransform *) curAction);
596  }
597  else if(curAction->getActionType() == ViewportBufferAction::fill) {
598  startFillAction((ViewportBufferFill *) curAction);
599  }
600  else {
601  doStretchAction((ViewportBufferStretch *) curAction);
602  p_initialStretchDone = true;
603  }
604 
605  doNextAction = !p_actions->empty();
606 
607  if(doNextAction) {
608  curAction = p_actions->head();
609  doNextAction = !curAction->started();
610  }
611  }
612 
613  if(p_actions->empty()) {
614  // Buffer Updated - Giving it BufferXYRect
615  p_viewport->bufferUpdated(bufferXYRect());
616  }
617  }
618 
619 
625  double ViewportBuffer::currentProgress() {
626  if(!working())
627  return 1.0;
628  if(p_requestedFillArea <= 0.0)
629  return 0.0;
630 
631  return 1.0 - totalUnfilledArea() / p_requestedFillArea;
632  }
633 
634 
641  double ViewportBuffer::totalUnfilledArea() {
642  double totalFillArea = 0.0;
643 
644  // If at any time the total X or Y shift exceeds the buffer size, we aren't
645  // preserving data. Check to see if this is the case!
646  for(int actionIndex = 0; actionIndex < p_actions->size(); actionIndex ++) {
647  ViewportBufferAction *action = (*p_actions)[actionIndex];
648 
649  if(action->getActionType() == ViewportBufferAction::fill) {
650  ViewportBufferFill *fill = (ViewportBufferFill *)action;
651 
652  QRect unfilledRect(*fill->getRect());
653  unfilledRect.setTop(fill->getReadPosition());
654  totalFillArea += unfilledRect.width() * unfilledRect.height();
655  }
656  }
657 
658  return totalFillArea;
659  }
660 
661 
666  bool ViewportBuffer::actionsPreserveData() {
667  int totalXShift = 0;
668  int totalYShift = 0;
669 
670  QRect currentBufferRect(bufferXYRect());
671 
672  int bufferWidth = currentBufferRect.width();
673  int bufferHeight = currentBufferRect.height();
674 
675  // If at any time the total X or Y shift exceeds the buffer size, we aren't
676  // preserving data. Check to see if this is the case!
677  for(int actionIndex = 0; actionIndex < p_actions->size(); actionIndex ++) {
678  ViewportBufferAction *action = (*p_actions)[actionIndex];
679 
680  if(action->getActionType() == ViewportBufferAction::transform) {
681  ViewportBufferTransform *transform = (ViewportBufferTransform *)action;
682 
683  if(transform->resizeFirst()) {
684  bufferWidth = transform->getBufferWidth();
685  bufferHeight = transform->getBufferHeight();
686  }
687 
688  if(abs(totalXShift) >= bufferWidth)
689  return false;
690  if(abs(totalYShift) >= bufferHeight)
691  return false;
692 
693  // Without the absolute value this will calculate
694  // if any data on the screen is preserved, however
695  // a better method is to see if its quicker to reread
696  // it all which happens when we use abs
697  totalXShift += abs(transform->getXTranslation());
698  totalYShift += abs(transform->getYTranslation());
699 
700  if(!transform->resizeFirst()) {
701  bufferWidth = transform->getBufferWidth();
702  bufferHeight = transform->getBufferHeight();
703  }
704 
705  if(abs(totalXShift) >= bufferWidth)
706  return false;
707  if(abs(totalYShift) >= bufferHeight)
708  return false;
709  }
710  }
711 
712  return true;
713  }
714 
715 
722  bool ViewportBuffer::reinitializeActionExists() {
723  QRect currentBufferRect(bufferXYRect());
724 
725  if(currentBufferRect.width() == 0 || currentBufferRect.height() == 0) {
726  return true;
727  }
728 
729  for(int actionIndex = 0; actionIndex < p_actions->size(); actionIndex ++) {
730  ViewportBufferAction *action = (*p_actions)[actionIndex];
731 
732  if(action->getActionType() == ViewportBufferAction::transform) {
733  ViewportBufferTransform *transform = (ViewportBufferTransform *)action;
734 
735  if(transform->getBufferWidth() == 0)
736  return true;
737  if(transform->getBufferHeight() == 0)
738  return true;
739  }
740  }
741 
742  return false;
743  }
744 
745 
751  bool ViewportBuffer::working() {
752  return !p_actions->empty() || !p_bufferInitialized || !p_enabled;
753  }
754 
755 
761  void ViewportBuffer::doTransformAction(ViewportBufferTransform *action) {
762  bool needResize = action->getBufferWidth() > -1 &&
763  action->getBufferHeight() > -1;
764 
765  if(action->resizeFirst() && needResize) {
766  resizeBuffer(action->getBufferWidth(), action->getBufferHeight());
767  }
768 
769  shiftBuffer(action->getXTranslation(), action->getYTranslation());
770 
771  if(!action->resizeFirst() && needResize) {
772  resizeBuffer(action->getBufferWidth(), action->getBufferHeight());
773  }
774 
775  delete action;
776  action = NULL;
777  p_actions->dequeue();
778  }
779 
780 
787  void ViewportBuffer::startFillAction(ViewportBufferFill *action) {
788  if(action->started())
789  return;
790 
791  action->started(true);
792 
793  requestCubeLine(action);
794 
795  if(action->shouldRequestMore()) {
796  requestCubeLine(action);
797  }
798  }
799 
800 
806  void ViewportBuffer::doStretchAction(ViewportBufferStretch *action) {
807  delete action;
808  action = NULL;
809  p_actions->dequeue();
810 
811  p_viewport->restretch(this);
812  }
813 
814 
821  void ViewportBuffer::resizeBuffer(unsigned int width, unsigned int height) {
822  p_buffer.resize(height);
823 
824  for(unsigned int i = 0; i < p_buffer.size(); i++) {
825  p_buffer[i].resize(width, Null);
826  }
827  }
828 
829 
837  void ViewportBuffer::shiftBuffer(int deltaX, int deltaY) {
838  if(deltaY >= 0) {
839  for(int i = p_buffer.size() - 1; i >= deltaY; i--) {
840  p_buffer[i] = p_buffer[i - deltaY];
841 
842  // If we have y shift, null out original data (keep only moved data)
843  if(deltaY != 0) {
844  for(unsigned int x = 0; x < p_buffer[i - deltaY].size(); x++) {
845  p_buffer[i - deltaY][x] = Null;
846  }
847  }
848 
849  if(deltaX > 0) {
850  for(int j = p_buffer[i].size() - 1; j >= deltaX; j--) {
851  p_buffer[i][j] = p_buffer[i][j - deltaX];
852  p_buffer[i][j - deltaX] = Null;
853  }
854  }
855  else if(deltaX < 0) {
856  for(int j = 0; j < (int)p_buffer[i].size() + deltaX; j++) {
857  p_buffer[i][j] = p_buffer[i][j - deltaX];
858  p_buffer[i][j - deltaX] = Null;
859  }
860  }
861  }
862  }
863  else if(deltaY < 0) {
864  for(int i = 0; i < (int)p_buffer.size() + deltaY; i++) {
865  p_buffer[i] = p_buffer[i - deltaY];
866 
867  // null out original data (keep only moved data)
868  for(unsigned int x = 0; x < p_buffer[i - deltaY].size(); x++) {
869  p_buffer[i - deltaY][x] = Null;
870  }
871 
872  if(deltaX > 0) {
873  for(int j = p_buffer[i].size() - 1; j >= deltaX; j--) {
874  p_buffer[i][j] = p_buffer[i][j - deltaX];
875  p_buffer[i][j - deltaX] = Null;
876  }
877  }
878  else if(deltaX < 0) {
879  for(int j = 0; j < (int)p_buffer[i].size() + deltaX; j++) {
880  p_buffer[i][j] = p_buffer[i][j - deltaX];
881  p_buffer[i][j - deltaX] = Null;
882  }
883  }
884  }
885  }
886  }
887 
888 
893  void ViewportBuffer::resizedViewport() {
894  updateBoundingRects();
895 
896  if(!p_bufferInitialized || !p_enabled)
897  return;
898 
899  // ensure we have a valid bounding rect! For example, If the cube viewport
900  // is hidden and then shown again this could happen.
901  if(!p_XYBoundingRect.isValid())
902  return;
903 
904  if(!p_oldXYBoundingRect.isValid()) {
905  reinitialize();
906  return;
907  }
908 
909  //We need to know how much data was gained/lost on each side of the cube
910  double deltaLeftSamples = p_sampLineBoundingRect[rectLeft] -
911  p_oldSampLineBoundingRect[rectLeft];
912  //The input to round should be close to an integer
913  int deltaLeftPixels = (int)round(deltaLeftSamples * p_viewport->scale());
914 
915  double deltaRightSamples = p_sampLineBoundingRect[rectRight] -
916  p_oldSampLineBoundingRect[rectRight];
917  int deltaRightPixels = (int)round(deltaRightSamples * p_viewport->scale());
918 
919  double deltaTopSamples = p_sampLineBoundingRect[rectTop] -
920  p_oldSampLineBoundingRect[rectTop];
921  int deltaTopPixels = (int)round(deltaTopSamples * p_viewport->scale());
922 
923  double deltaBottomSamples = p_sampLineBoundingRect[rectBottom] -
924  p_oldSampLineBoundingRect[rectBottom];
925  int deltaBottomPixels = (int)round(deltaBottomSamples *
926  p_viewport->scale());
927 
928  //deltaW is the change in width in the visible area of the cube
929  int deltaW = - deltaLeftPixels + deltaRightPixels;
930 
931  //deltaH is the change in height in the visible area of the cube
932  int deltaH = - deltaTopPixels + deltaBottomPixels;
933 
934  //If the new visible width has changed (resized in the horizontal direction)
935  if(p_XYBoundingRect.width() != p_oldXYBoundingRect.width()) {
936  //Resized larger in the horizontal direction
937  if(deltaW > 0) {
938  //Using old height because we might lose data if new height is smaller
940  transform->setTranslation(-deltaLeftPixels, 0);
941  transform->setResize(p_XYBoundingRect.width(),
942  p_oldXYBoundingRect.height());
943  transform->resizeFirst(true);
944 
945  enqueueAction(transform);
946 
947  // left side that needs filled
948  QPoint topLeftOfLeftRect(p_XYBoundingRect.left(),
949  p_oldXYBoundingRect.top());
950 
951  QPoint bottomRightOfLeftRect(p_XYBoundingRect.left() - deltaLeftPixels,
952  p_oldXYBoundingRect.bottom());
953 
954  QRect leftRect(topLeftOfLeftRect, bottomRightOfLeftRect);
955 
956  ViewportBufferFill *leftFill = createViewportBufferFill(leftRect,
957  true);
958  enqueueAction(leftFill);
959 
960  // right side that needs filled
961  QPoint topLeftOfRightRect(p_XYBoundingRect.right() - deltaRightPixels,
962  p_oldXYBoundingRect.top());
963 
964  QPoint bottomRightOfRightRect(p_XYBoundingRect.right(),
965  p_oldXYBoundingRect.bottom());
966 
967  QRect rightRect(topLeftOfRightRect, bottomRightOfRightRect);
968 
969  ViewportBufferFill *rightFill = createViewportBufferFill(rightRect,
970  true);
971  enqueueAction(rightFill);
972  }
973  //Resized smaller in the horizontal direction
974  else if(deltaW < 0) {
976  transform->setTranslation(-deltaLeftPixels, 0);
977  transform->setResize(p_XYBoundingRect.width(),
978  p_oldXYBoundingRect.height());
979  transform->resizeFirst(false);
980  enqueueAction(transform);
981  }
982  }
983 
984  //If the new visible height has changed (resized in the vertical direction)
985  if(p_XYBoundingRect.height() != p_oldXYBoundingRect.height()) {
986  //Resized larger in the vertical direction
987  if(deltaH > 0) {
989  transform->setTranslation(0, -deltaTopPixels);
990  transform->setResize(p_XYBoundingRect.width(),
991  p_XYBoundingRect.height());
992  transform->resizeFirst(true);
993 
994  enqueueAction(transform);
995 
996  QPoint bottomRightOfTopSide(p_XYBoundingRect.right(),
997  p_XYBoundingRect.top() - deltaTopPixels);
998 
999  QRect topSideToFill(p_XYBoundingRect.topLeft(), bottomRightOfTopSide);
1000 
1001 
1002  QPoint topLeftOfbottomSide(p_XYBoundingRect.left(),
1003  p_XYBoundingRect.bottom() -
1004  deltaBottomPixels);
1005 
1006  QRect bottomSideToFill(topLeftOfbottomSide,
1007  p_XYBoundingRect.bottomRight());
1008 
1009  ViewportBufferFill *topFill = createViewportBufferFill(topSideToFill,
1010  false);
1011  enqueueAction(topFill);
1012 
1013  ViewportBufferFill *bottomFill =
1014  createViewportBufferFill(bottomSideToFill, false);
1015  enqueueAction(bottomFill);
1016  }
1017  //Resized smaller in the vertical direction
1018  else if(deltaH < 0) {
1020 
1021  transform->setTranslation(0, -deltaTopPixels);
1022  transform->setResize(p_XYBoundingRect.width(),
1023  p_oldXYBoundingRect.height());
1024 
1025  enqueueAction(transform);
1026  }
1027  }
1028 
1029  doQueuedActions();
1030  }
1031 
1032 
1040  void ViewportBuffer::pan(int deltaX, int deltaY) {
1041  updateBoundingRects();
1042 
1043  if(!p_bufferInitialized || !p_enabled) {
1044  return;
1045  }
1046 
1047 
1048  if(p_sampLineBoundingRect == p_oldSampLineBoundingRect) {
1049  //The sample line bounding rect contains the bounds of the
1050  //screen pixels to the cube sample/line bounds. If the cube
1051  //bounds do not change, then we do not need to do anything to
1052  //the buffer.
1053  return;
1054  }
1055 
1056  double deltaLeftSamples = p_sampLineBoundingRect[rectLeft] -
1057  p_oldSampLineBoundingRect[rectLeft];
1058  int deltaLeftPixels = (int)round(deltaLeftSamples * p_viewport->scale());
1059 
1060  double deltaTopLines = p_sampLineBoundingRect[rectTop] -
1061  p_oldSampLineBoundingRect[rectTop];
1062  int deltaTopPixels = (int)round(deltaTopLines * p_viewport->scale());
1063 
1064  // Don't try to figure out panning beyond a full screen,
1065  // even though data could very well be preserved.
1066  if(abs(deltaY) >= p_XYBoundingRect.height() ||
1067  abs(deltaX) >= p_XYBoundingRect.width()) {
1068  reinitialize();
1069  return;
1070  }
1071 
1072  //Left side of the visible area changed (start sample is different)
1073  if(p_sampLineBoundingRect[rectLeft] != p_oldSampLineBoundingRect[rectLeft]) {
1074  //Shifting data to the right
1075  if(deltaX > 0) {
1076  // The buffer is getting bigger
1078  transform->setResize(p_XYBoundingRect.width(),
1079  p_oldXYBoundingRect.height());
1080  transform->setTranslation(-deltaLeftPixels, 0);
1081  transform->resizeFirst(true);
1082 
1083  enqueueAction(transform);
1084 
1085  QPoint topLeftOfRefill(p_XYBoundingRect.left(),
1086  p_oldXYBoundingRect.top());
1087 
1088  QPoint bottomRightOfRefill(p_XYBoundingRect.left() + deltaX,
1089  p_oldXYBoundingRect.bottom());
1090  QRect fillArea(topLeftOfRefill, bottomRightOfRefill);
1091 
1092  ViewportBufferFill *fill = createViewportBufferFill(fillArea, true);
1093  enqueueAction(fill);
1094  }
1095  //Shifting data to the left
1096  else if(deltaX < 0) {
1097  //The buffer is getting smaller - no new data
1099  transform->setTranslation(-deltaLeftPixels, 0);
1100  transform->setResize(p_XYBoundingRect.width(),
1101  p_oldXYBoundingRect.height());
1102  transform->resizeFirst(false);
1103  enqueueAction(transform);
1104 
1105  // if new samples on the screen at all, mark it for reading
1106  if(p_sampLineBoundingRect[rectRight] !=
1107  p_oldSampLineBoundingRect[rectRight]) {
1108  QPoint topLeftOfRefill(p_XYBoundingRect.right() + deltaX,
1109  p_oldXYBoundingRect.top());
1110  QPoint bottomRightOfRefill(p_XYBoundingRect.right(),
1111  p_oldXYBoundingRect.bottom());
1112 
1113  QRect refillArea(topLeftOfRefill, bottomRightOfRefill);
1114 
1115  ViewportBufferFill *fill = createViewportBufferFill(refillArea,
1116  true);
1117  enqueueAction(fill);
1118  }
1119  }
1120  }
1121  // Left side of the visible area is the same (start sample has not changed,
1122  // but end sample may be different)
1123  else {
1125  transform->setResize(p_XYBoundingRect.width(),
1126  p_oldXYBoundingRect.height());
1127  enqueueAction(transform);
1128 
1129  if(deltaX < 0) {
1130  QPoint topLeftOfFillArea(p_XYBoundingRect.right() + deltaX,
1131  p_oldXYBoundingRect.top());
1132 
1133  QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
1134  p_oldXYBoundingRect.bottom());
1135 
1136  QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
1137  ViewportBufferFill *fill = createViewportBufferFill(fillArea, true);
1138 
1139  enqueueAction(fill);
1140  }
1141  }
1142 
1143  //Top side of the visible area changed (start line is different)
1144  if(p_sampLineBoundingRect[rectTop] != p_oldSampLineBoundingRect[rectTop]) {
1145  //Shifting data down
1146  if(deltaY > 0) {
1148  transform->setTranslation(0, -deltaTopPixels);
1149  transform->setResize(p_XYBoundingRect.width(),
1150  p_XYBoundingRect.height());
1151  transform->resizeFirst(true);
1152  enqueueAction(transform);
1153 
1154  QPoint topLeftOfFillArea(p_XYBoundingRect.left(),
1155  p_XYBoundingRect.top());
1156  QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
1157  p_XYBoundingRect.top() + deltaY);
1158  QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
1159  ViewportBufferFill *fill = createViewportBufferFill(fillArea, false);
1160  enqueueAction(fill);
1161  }
1162  //Shifting data up
1163  else if(deltaY < 0) {
1165  transform->setTranslation(0, -deltaTopPixels);
1166  transform->setResize(p_XYBoundingRect.width(),
1167  p_XYBoundingRect.height());
1168  transform->resizeFirst(false);
1169  enqueueAction(transform);
1170 
1171  // if new lines on the screen at all, mark it for reading
1172  if(p_sampLineBoundingRect[rectBottom] !=
1173  p_oldSampLineBoundingRect[rectBottom]) {
1174  QPoint topLeftOfFillArea(p_XYBoundingRect.left(),
1175  p_oldXYBoundingRect.bottom() + deltaY);
1176  QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
1177  p_XYBoundingRect.bottom());
1178  QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
1179  ViewportBufferFill *fill = createViewportBufferFill(fillArea, false);
1180  enqueueAction(fill);
1181  }
1182  }
1183  }
1184  // Top side of the visible area is the same (start line has not changed, but
1185  // end line may be different)
1186  else {
1188  transform->setResize(p_XYBoundingRect.width(),
1189  p_XYBoundingRect.height());
1190  enqueueAction(transform);
1191 
1192  if(deltaY < 0) {
1193  QPoint topLeftOfFillArea(p_XYBoundingRect.left(),
1194  p_XYBoundingRect.bottom() + deltaY);
1195  QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
1196  p_XYBoundingRect.bottom());
1197  QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
1198  ViewportBufferFill *fill = createViewportBufferFill(fillArea, false);
1199  enqueueAction(fill);
1200  }
1201  }
1202 
1203  doQueuedActions();
1204  }
1205 
1206 
1212  void ViewportBuffer::addStretchAction() {
1213  for(int i = 0; i < p_actions->size(); i++) {
1214  if((*p_actions)[i]->getActionType() == ViewportBufferAction::stretch) {
1215  p_actions->removeAt(i);
1216  i --;
1217  }
1218  }
1219 
1220  enqueueAction(new ViewportBufferStretch());
1221  doQueuedActions();
1222  }
1223 
1233  void ViewportBuffer::emptyBuffer(bool force) {
1234  if(force) {
1235  p_buffer.clear();
1236  p_bufferInitialized = false;
1237  }
1238  }
1239 
1240 
1249  QRect ViewportBuffer::bufferXYRect() {
1250  QRect rect = p_XYBoundingRect;
1251 
1252  if(!rect.height() || !p_buffer.size())
1253  return QRect();
1254 
1255  if(rect.height() > (int) p_buffer.size())
1256  rect.setBottom(rect.top() + p_buffer.size() - 1);
1257 
1258  if(rect.width() > (int) p_buffer[0].size())
1259  rect.setRight(rect.left() + p_buffer[0].size() - 1);
1260 
1261  return rect;
1262  }
1263 
1264 
1273  void ViewportBuffer::scaleChanged() {
1274  if(!p_enabled)
1275  return;
1276 
1277  try {
1278  updateBoundingRects();
1279  reinitialize();
1280  }
1281  catch (IException &e) {
1282  throw IException(
1283  e, IException::Programmer, "Unable to change scale.", _FILEINFO_);
1284  }
1285  }
1286 
1287 
1294  void ViewportBuffer::enable(bool enabled) {
1295  bool wasEnabled = p_enabled;
1296 
1297  p_enabled = enabled;
1298 
1299  if(!wasEnabled && p_enabled) {
1300  updateBoundingRects();
1301  reinitialize();
1302  }
1303  }
1304 
1305 
1311  void ViewportBuffer::setBand(int band) {
1312  if(p_band == band)
1313  return;
1314  p_band = band;
1315 
1316  updateBoundingRects();
1317 
1318  if(!p_enabled)
1319  return;
1320 
1321  reinitialize();
1322  }
1323 
1324 
1332  void ViewportBuffer::reinitialize() {
1333 
1334  try {
1335  // If we're in the middle of a process, we got an okay stretch on startup,
1336  // then we can stop what we're doing.
1337  if(working() && p_initialStretchDone) {
1338  // We only need to handle the current action, can ignore others
1339  ViewportBufferAction *curAction = p_actions->head();
1340 
1341  // Delete older actions
1342  for(int i = p_actions->size() - 1; i > 0; i--) {
1343  delete(*p_actions)[i];
1344  p_actions->pop_back();
1345  }
1346 
1347  // Deal with current action
1348  if(curAction->started()) {
1349  if(curAction->getActionType() == ViewportBufferAction::fill) {
1350  ViewportBufferFill *fill = (ViewportBufferFill *)curAction;
1351 
1352  fill->stop();
1353 
1354  p_requestedFillArea = fill->getRect()->height() *
1355  fill->getRect()->width();
1356  }
1357  }
1358  else {
1359  delete curAction;
1360  p_actions->clear();
1361  p_requestedFillArea = 0.0;
1362  }
1363  }
1364 
1365 
1366  p_bufferInitialized = true;
1367 
1369  reset->setResize(0, 0);
1370  enqueueAction(reset);
1371 
1372  if (p_XYBoundingRect.isValid()) {
1374  transform->setResize(p_XYBoundingRect.width(), p_XYBoundingRect.height());
1375  enqueueAction(transform);
1376  ViewportBufferFill *fill = createViewportBufferFill(p_XYBoundingRect,
1377  false);
1378  enqueueAction(fill);
1379  }
1380 
1381  doQueuedActions();
1382  }
1383  catch (IException &e) {
1384  throw IException(IException::Programmer,
1385  "Unable to resize and fill buffer.",
1386  _FILEINFO_);
1387  }
1388  }
1389 }
int getXTranslation()
Gets the amount the buffer should be translated in X.
const double Null
Value for an Isis Null pixel.
Definition: SpecialPixel.h:110
int getLeftmostPixelPosition()
Returns the left of the X/Y bounding rect for this fill.
int getRequestPosition() const
Returns the current request position (>= read position)
bool resizeFirst()
Returns true if the resize should happen before the translation.
bool shouldRequestMore()
Returns true if request position is past the end of the fill.
Buffer for containing a three dimensional section of an image.
Definition: Brick.h:61
double at(const int index) const
Returns the value in the shape buffer at the given index.
Definition: Buffer.cpp:247
Namespace for the standard library.
virtual ActionType getActionType()
Returns the instantiated type.
int getYTranslation()
Gets the amount the buffer should be translated in Y.
QRect * getRect()
Returns the rect that this action is filling in screen pixels.
Widget to display Isis cubes for qt apps.
Definition: CubeViewport.h:132
int LineDimension() const
Returns the number of lines in the shape buffer.
Definition: Buffer.h:95
void incRequestPosition()
Increment request position.
int size() const
Returns the total number of pixels in the shape buffer.
Definition: Buffer.h:113
int Sample(const int index=0) const
Returns the sample position associated with a shape buffer index.
Definition: Buffer.cpp:143
bool started()
Returns true if this is an action that takes time and has begun.
const int & getBufferWidth()
Returns the new buffer width.
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:40
void setTranslation(int x, int y)
Sets the translation amount in x and y.
void stop()
Cancels the current operation.
int Line(const int index=0) const
Returns the line position associated with a shape buffer index.
Definition: Buffer.cpp:161
double viewportToSample(int x)
Converts screen x position to cube sample position.
Encapsulation of Cube I/O with Change Notifications.
bool doneReading()
Returns true if read position is past the end of the fill.
int SampleDimension() const
Returns the number of samples in the shape buffer.
Definition: Buffer.h:86
void setResize(int width, int height)
Sets the size the buffer should be resized to.
void incReadPosition()
Increment read position.
Isis exception class.
Definition: IException.h:107
Adds specific functionality to C++ strings.
Definition: IString.h:181
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
double viewportToLine(int y)
Converts screen y position to cube line position.
int Index(const int i_samp, const int i_line, const int i_band) const
Given a sample, line, and band position, this returns the appropriate index in the shape buffer...
Definition: Buffer.cpp:213
int getReadPosition() const
Returns the current read position.
const int & getBufferHeight()
Returns the new buffer height.
int getTopmostPixelPosition()
Returns the top of the X/Y bounding rect for this fill.