Isis 3 Programmer Reference
RubberBandTool.cpp
1 #include "RubberBandTool.h"
2 
3 #include <cmath>
4 #include <float.h>
5 
6 #include <QDebug>
7 #include <QList>
8 #include <QMessageBox>
9 #include <QPainter>
10 #include <QPen>
11 #include <QPoint>
12 #include <QRect>
13 
14 #include "geos/geom/CoordinateArraySequence.h"
15 #include "geos/geom/CoordinateSequence.h"
16 #include "geos/geom/Coordinate.h"
17 #include "geos/geom/LineString.h"
18 #include "geos/geom/MultiLineString.h"
19 #include "geos/geom/Polygon.h"
20 
21 #include "Angle.h"
22 #include "Constants.h"
23 #include "MdiCubeViewport.h"
24 #include "PolygonTools.h"
25 #include "SerialNumberList.h"
26 
27 using namespace std;
28 
29 namespace Isis {
35  RubberBandTool::RubberBandTool(QWidget *parent) : Tool(parent) {
36  p_mouseLoc = NULL;
37  p_vertices = NULL;
38 
39  p_mouseLoc = new QPoint;
40  p_vertices = new QList< QPoint >;
41  p_bandingMode = LineMode;
42 
43  activate(false);
44  repaint();
45  }
46 
47 
48  RubberBandTool::~RubberBandTool() {
49  if(p_mouseLoc) {
50  delete p_mouseLoc;
51  p_mouseLoc = NULL;
52  }
53 
54  if(p_vertices) {
55  delete p_vertices;
56  p_vertices = NULL;
57  }
58  }
59 
73  void RubberBandTool::paintViewport(MdiCubeViewport *vp, QPainter *painter) {
74  QPen pen(QColor(255, 0, 0));
75  QPen greenPen(QColor(0, 255, 0));
76  pen.setStyle(Qt::SolidLine);
77  greenPen.setStyle(Qt::SolidLine);
78  painter->setPen(pen);
79 
80  if ( (vp != cubeViewport() && p_drawActiveOnly) ||
81  !(vp == cubeViewport() || (cubeViewport()->isLinked() &&
82  vp->isLinked()))) {
83  return;
84  }
85 
86  switch(p_bandingMode) {
87  case AngleMode:
88  paintVerticesConnected(painter);
89  break;
90 
91  case LineMode:
92  // if point needed, draw an X
93  if(figureIsPoint() && !p_tracking) {
94  painter->drawLine((*p_vertices)[0].x() - 10, (*p_vertices)[0].y() - 10,
95  (*p_vertices)[0].x() + 10, (*p_vertices)[0].y() + 10);
96  painter->drawLine((*p_vertices)[0].x() - 10, (*p_vertices)[0].y() + 10,
97  (*p_vertices)[0].x() + 10, (*p_vertices)[0].y() - 10);
98  }
99  else {
100  paintVerticesConnected(painter);
101  }
102 
103  break;
104 
105  case PolygonMode:
106  paintVerticesConnected(painter);
107 
108  if(!p_tracking && p_vertices->size() > 0) {
109  painter->drawLine((*p_vertices)[0], (*p_vertices)[ p_vertices->size() - 1 ]);
110  }
111  break;
112 
113  case CircleMode:
114  case EllipseMode: {
115  if(p_vertices->size() != 0) {
116  QList<QPoint> verticesList = vertices();
117  int width = 2 * (verticesList[1].x() - verticesList[0].x());
118  int height = 2 * (verticesList[1].y() - verticesList[0].y());
119 
120  // upper left x,y,width,height
121  painter->drawEllipse(verticesList[0].x() - width / 2, verticesList[0].y() - height / 2,
122  width,
123  height
124  );
125  }
126  }
127  break;
128 
129  case RectangleMode: {
130  if(figureIsPoint() && !p_tracking) {
131  painter->drawLine((*p_vertices)[0].x() - 10, (*p_vertices)[0].y() - 10,
132  (*p_vertices)[0].x() + 10, (*p_vertices)[0].y() + 10);
133  painter->drawLine((*p_vertices)[0].x() - 10, (*p_vertices)[0].y() + 10,
134  (*p_vertices)[0].x() + 10, (*p_vertices)[0].y() - 10);
135  }
136  else {
137  if(p_tracking && p_vertices->size() > 0) {
138  paintRectangle((*p_vertices)[0], *p_mouseLoc, painter);
139  }
140  else if(p_vertices->size() > 0) {
141  paintVerticesConnected(painter);
142  painter->drawLine((*p_vertices)[0], (*p_vertices)[ p_vertices->size() - 1 ]);
143  }
144  }
145  }
146  break;
147 
148  case RotatedRectangleMode: {
149  if(p_vertices->size() == 2) {
150  QPoint missingVertex;
151  calcRectCorners((*p_vertices)[0], (*p_vertices)[1], *p_mouseLoc, missingVertex);
152  painter->drawLine(*p_mouseLoc, missingVertex);
153  painter->drawLine(missingVertex, (*p_vertices)[0]);
154  }
155  else if(p_vertices->size() == 4) {
156  painter->drawLine((*p_vertices)[0], (*p_vertices)[ 3 ]);
157  }
158 
159  paintVerticesConnected(painter);
160 
161  // Draw indicator on top of original lines if applicable
162  if(p_indicatorColors) {
163  painter->setPen(greenPen);
164  if(p_vertices->size() > 1) {
165  painter->drawLine((*p_vertices)[0], (*p_vertices)[1]);
166  }
167  else if(p_vertices->size() == 1) {
168  painter->drawLine((*p_vertices)[0], *p_mouseLoc);
169  }
170 
171  painter->setPen(pen);
172  }
173  }
174  break;
175 
176  case SegmentedLineMode:
177  paintVerticesConnected(painter);
178  break;
179  }
180  }
181 
190  void RubberBandTool::calcRectCorners(QPoint corner1, QPoint corner2, QPoint &corner3, QPoint &corner4) {
191  double slope = ((double)corner2.y() - (double)corner1.y()) / ((double)corner2.x() - (double)corner1.x());
192 
193  if((fabs(slope) > DBL_EPSILON) && (slope < DBL_MAX) && (slope > -DBL_MAX)) {
194  // corner1,corner2 make up y=m(x-x1)+y1
195  // corner3,corner4 must make up || line crossing corner3.
196  // b (y-intercept) is what differs from the original line and our parallel line.
197  // Go ahead and figure out our new b by using b = -mx1+y1 from the point-slope formula.
198  double parallelB = -1 * slope * corner3.x() + corner3.y();
199 
200  // Now we have our equation for a parallel line, which our new points lie on. Let's find the perpendicular lines
201  // which cross corner1,corner2 in order to figure out where they cross it. Use -1/slope = perpendicular slope and
202  // now we have y=m(x-x1)+y1. What we care about is b in y=mx+b, so figure it out using b = m*(-x1)+y1
203  double perpSlope = -1.0 / slope;
204  double perpLineMode1b = perpSlope * (-1 * corner1.x()) + corner1.y();
205  double perpLineMode2b = perpSlope * (-1 * corner2.x()) + corner2.y();
206 
207  // Now let's find the perpendicular lines' intercepts on the parallel line.
208  // y = mx+b = y = mx+b => mx+b(perpendicular) = mx+b(parallel) for the perp. lines and the parallel line.
209  // Combine the b's on the left to make y= m(perp)x+k = m(par)x.
210  double perpLineMode1k = perpLineMode1b - parallelB;
211  double perpLineMode2k = perpLineMode2b - parallelB;
212 
213  // Now we have mx + k = mx (parallel). Divive the parallel slope out to get
214  // (m(perp)x+k)/m(parallel) = x. Move the x over from the left side of the equation by subtracting...
215  // k/m(parallel) = x - m(perp)x/m(parallel). Factor out the x's... k/m(par) = x(1-m(per)/m(par)) and divive
216  // both sides by "(1-m(per)/m(par))". So we end up with: (k/m(par)) / (1 - m(per) / m(par) ) =
217  // k/m(par) / ( (m(par)-m(per)) / m(par) ) = k / m(par) * m(par) / (m(par) - m(per)) = k / (m(par) - m(per))
218  double perpLineMode1IntersectX = perpLineMode1k / (slope - perpSlope);
219  double perpLineMode2IntersectX = perpLineMode2k / (slope - perpSlope);
220 
221  // The intersecting X values are now known, and the equation of the parallel line, so let's roll with it and
222  // get our two corners set. perpLineMode1 => corner1 => corner4, perpLineMode2 => corner2 => corner3
223  corner3.setX((int)perpLineMode2IntersectX);
224  corner3.setY((int)(perpLineMode2IntersectX * slope + parallelB)); //mx+b
225  corner4.setX((int)perpLineMode1IntersectX);
226  corner4.setY((int)(perpLineMode1IntersectX * slope + parallelB)); //mx+b
227  }
228  else if(fabs(slope) < DBL_EPSILON) {
229  corner3.setX(corner2.x());
230  corner3.setY(corner3.y());
231  corner4.setX(corner1.x());
232  corner4.setY(corner3.y());
233  }
234  else {
235  corner3.setX(corner3.x());
236  corner3.setY(corner2.y());
237  corner4.setX(corner3.x());
238  corner4.setY(corner1.y());
239  }
240  }
241 
248  void RubberBandTool::paintVerticesConnected(QPainter *painter) {
249  for(int vertex = 1; vertex < p_vertices->size(); vertex++) {
250  QPoint start = (*p_vertices)[vertex - 1];
251  QPoint end = (*p_vertices)[vertex];
252 
253  painter->drawLine(start, end);
254  }
255 
256  if(p_tracking && (p_vertices->size() > 0)) {
257  QPoint start = (*p_vertices)[p_vertices->size() - 1];
258  QPoint end = *p_mouseLoc;
259 
260  painter->drawLine(start, end);
261  }
262  }
263 
271  void RubberBandTool::paintRectangle(QPoint upperLeft, QPoint lowerRight, QPainter *painter) {
272  QPoint upperRight = QPoint(lowerRight.x(), upperLeft.y());
273  QPoint lowerLeft = QPoint(upperLeft.x(), lowerRight.y());
274 
275  paintRectangle(upperLeft, upperRight, lowerLeft, lowerRight, painter);
276  }
277 
287  void RubberBandTool::paintRectangle(QPoint upperLeft, QPoint upperRight,
288  QPoint lowerLeft, QPoint lowerRight, QPainter *painter) {
289  painter->drawLine(upperLeft, upperRight);
290  painter->drawLine(upperRight, lowerRight);
291  painter->drawLine(lowerRight, lowerLeft);
292  painter->drawLine(lowerLeft, upperLeft);
293  }
294 
302  void RubberBandTool::enable(RubberBandMode mode, bool showIndicatorColors) {
303  RubberBandMode oldMode = p_bandingMode;
304  p_bandingMode = mode;
305  p_indicatorColors = showIndicatorColors;
306  //Took this out because it was reseting and not letting us plot single points.
307  //p_pointTolerance = 0;
308  p_allClicks = false;
309  p_drawActiveOnly = false;
310  reset();
311  activate(true);
312 
313  if(oldMode != mode) {
314  emit modeChanged();
315  }
316  }
317 
323 
324  activate(false);
325  reset();
326  repaint();
327  }
328 
334  p_drawActiveOnly = activeOnly;
335  repaint();
336  }
337 
344  p_doubleClicking = true;
345  *p_mouseLoc = p;
346 
347  switch(p_bandingMode) {
348  case AngleMode:
349  case CircleMode:
350  case EllipseMode:
351  case LineMode:
352  case RectangleMode:
353  case RotatedRectangleMode:
354  break;
355 
356  case SegmentedLineMode:
357  case PolygonMode:
358  p_tracking = false;
359  repaint();
360  emit bandingComplete();
361  break;
362  }
363  }
364 
386  void RubberBandTool::mouseButtonPress(QPoint p, Qt::MouseButton s) {
387  *p_mouseLoc = p;
388  p_mouseButton = s;
389 
390  if((s & Qt::LeftButton) != Qt::LeftButton && !p_allClicks) {
391  return;
392  }
393 
394  switch(p_bandingMode) {
395  case AngleMode:
396  break;
397 
398  case CircleMode:
399  case EllipseMode:
400  case LineMode:
401  case RectangleMode:
402  reset();
403  p_tracking = true;
404  p_vertices->push_back(p);
405  break;
406 
407  case RotatedRectangleMode:
408  if(p_vertices->size() == 4) {
409  reset();
410  }
411 
412  if(p_vertices->size() == 0) {
413  p_vertices->push_back(p);
414  p_tracking = true;
415  }
416  break;
417 
418  case SegmentedLineMode:
419  case PolygonMode:
420  if(!p_tracking) {
421  reset();
422  p_tracking = true;
423  }
424 
425  if(p_vertices->size() == 0 || (*p_vertices)[ p_vertices->size() - 1 ] != p) {
426  p_vertices->push_back(p);
427  }
428 
429  break;
430  }
431 
432  p_mouseDown = true;
433  }
434 
475  void RubberBandTool::mouseButtonRelease(QPoint p, Qt::MouseButton s) {
476  if ((s & Qt::ControlModifier) == Qt::ControlModifier)
477  *p_mouseLoc = snapMouse(p);
478  else
479  *p_mouseLoc = p;
480 
481  p_mouseButton = s;
482 
483  if((s & Qt::LeftButton) == Qt::LeftButton || p_allClicks) {
484  p_mouseDown = false;
485  }
486  else {
487  return;
488  }
489 
490  switch(p_bandingMode) {
491  case AngleMode: {
492  if(p_vertices->size() == 3) {
493  reset();
494  }
495 
496  p_vertices->push_back(*p_mouseLoc);
497  p_tracking = true;
498 
499  if(p_vertices->size() == 3) {
500  p_tracking = false;
501  emit bandingComplete();
502  }
503  }
504  break;
505 
506  case LineMode:
507  case CircleMode:
508  case EllipseMode:
509  case RectangleMode: {
510  *p_vertices = vertices();
511  p_tracking = false;
512  emit bandingComplete();
513  }
514  break;
515 
516  case RotatedRectangleMode: {
517  if(p_vertices->size() == 1) {
518  p_vertices->push_back(*p_mouseLoc);
519  }
520  else if(p_vertices->size() == 2) {
521  *p_vertices = vertices();
522  p_tracking = false;
523  emit bandingComplete();
524  }
525  }
526  break;
527 
528  case SegmentedLineMode:
529  case PolygonMode:
530  break;
531  }
532 
533  p_doubleClicking = false; // If we were in a double click, it's over now.
534 
535 
536  MdiCubeViewport * activeViewport = cubeViewport();
537  for (int i = 0; i < (int) cubeViewportList()->size(); i++) {
538  MdiCubeViewport * curViewport = cubeViewportList()->at(i);
539  if (curViewport == activeViewport ||
540  (activeViewport->isLinked() && curViewport->isLinked())) {
541  curViewport->viewport()->repaint();
542  }
543  }
544  }
545 
546 
554  QPoint RubberBandTool::snapMouse(QPoint p) {
555  if (p_vertices->size()) {
556  QPoint lastVertex = p_vertices->at(p_vertices->size() - 1);
557 
558  int deltaX = abs(p.x() - lastVertex.x());
559  int deltaY = abs(p.y() - lastVertex.y());
560 
561  if (deltaX > deltaY)
562  p.setY(lastVertex.y());
563  else
564  p.setX(lastVertex.x());
565  }
566 
567  return p;
568  }
569 
570 
589  void RubberBandTool::mouseMove(QPoint p, Qt::MouseButton mouseButton) {
590  if(!p_tracking) {
591  return;
592  }
593 
594  p_mouseButton = mouseButton;
595 
596  if ((p_mouseButton & Qt::ControlModifier) == Qt::ControlModifier)
597  *p_mouseLoc = snapMouse(p);
598  else
599  *p_mouseLoc = p;
600 
601  switch(p_bandingMode) {
602  case AngleMode:
603  case RotatedRectangleMode:
604  if(p_vertices->size() == 2) {
605  emit measureChange();
606  }
607  break;
608 
609  case CircleMode:
610  case EllipseMode:
611  case RectangleMode:
612  if(p_vertices->size() == 1) {
613  emit measureChange();
614  }
615  break;
616 
617  case LineMode:
618  emit measureChange();
619  break;
620 
621  case SegmentedLineMode:
622  case PolygonMode: {
623  if(p_mouseDown && p != (*p_vertices)[ p_vertices->size() - 1 ]) {
624  p_vertices->push_back(p);
625  }
626 
627  if (p_bandingMode == SegmentedLineMode)
628  emit measureChange();
629  }
630  break;
631  }
632 
633  MdiCubeViewport * activeViewport = cubeViewport();
634  for (int i = 0; i < (int) cubeViewportList()->size(); i++) {
635  MdiCubeViewport * curViewport = cubeViewportList()->at(i);
636  if (curViewport == activeViewport ||
637  (activeViewport->isLinked() && curViewport->isLinked())) {
638  curViewport->viewport()->repaint();
639  }
640  }
641  }
642 
673  QList<QPoint> vertices = *p_vertices;
674 
675  if(!figureComplete())
676  return vertices;
677 
678  if(p_tracking) {
679  switch(p_bandingMode) {
680  case AngleMode:
681  case LineMode:
682  case SegmentedLineMode:
683  vertices.push_back(*p_mouseLoc);
684  break;
685 
686  case RectangleMode: {
687  QPoint corner1 = QPoint(p_mouseLoc->x(), vertices[0].y());
688  QPoint corner2 = QPoint(p_mouseLoc->x(), p_mouseLoc->y());
689  QPoint corner3 = QPoint(vertices[0].x(), p_mouseLoc->y());
690  vertices.push_back(corner1);
691  vertices.push_back(corner2);
692  vertices.push_back(corner3);
693  }
694  break;
695 
696  case RotatedRectangleMode: {
697  QPoint missingVertex;
698  calcRectCorners((*p_vertices)[0], (*p_vertices)[1], *p_mouseLoc, missingVertex);
699  vertices.push_back(*p_mouseLoc);
700  vertices.push_back(missingVertex);
701  }
702  break;
703 
704 
705  case CircleMode: {
706  int xradius = abs(p_mouseLoc->x() - vertices[0].x()) / 2;
707  int yradius = xradius;
708 
709  if(p_mouseLoc->x() - vertices[0].x() < 0) {
710  xradius *= -1;
711  }
712 
713  if(p_mouseLoc->y() - vertices[0].y() < 0) {
714  yradius *= -1;
715  }
716 
717  // Adjust p_vertices[0] from upper left to center
718  vertices[0].setX(vertices[0].x() + xradius);
719  vertices[0].setY(vertices[0].y() + yradius);
720 
721  vertices.push_back(*p_mouseLoc);
722 
723  vertices[1].setX(vertices[0].x() + xradius);
724  vertices[1].setY(vertices[0].y() + yradius);
725  }
726  break;
727 
728  case EllipseMode: {
729  // Adjust p_vertices[0] from upper left to center
730  double xradius = (p_mouseLoc->x() - vertices[0].x()) / 2.0;
731  double yradius = (p_mouseLoc->y() - vertices[0].y()) / 2.0;
732  vertices[0].setX((int)(vertices[0].x() + xradius));
733  vertices[0].setY((int)(vertices[0].y() + yradius));
734 
735  vertices.push_back(*p_mouseLoc);
736  }
737  break;
738 
739  case PolygonMode:
740  break;
741  }
742  }
743 
744  return vertices;
745  }
746 
753  p_vertices->clear();
754  p_tracking = false;
755  p_mouseDown = false;
756  p_doubleClicking = false;
757  repaint();
758  }
759 
760  Angle RubberBandTool::angle() {
761  Angle result;
762 
763  if(currentMode() == AngleMode) {
764  // We cancluate the angle by considering each line an angle itself, with respect to the
765  // X-Axis, and then differencing them.
766  //
767  // theta1 = arctan((point0Y - point1Y) / (point0X - point1X))
768  // theta2 = arctan((point2Y - point1Y) / (point2X - point1X))
769  // |
770  // | / <-- point0
771  // | / |
772  // | / |
773  // theta1 | / |
774  // --> |/ | <-- 90 degrees
775  // point1 --> ------|---------------------------
776  //(vertex) --> |\ | <-- 90 degrees
777  // theta2 | \ |
778  // | \ |
779  // | \ |
780  // | \ |
781  // | \|
782  // | | <-- point 2
783  //
784  // angle = theta1 - theta2; **
785  QList<QPoint> verticesList = vertices();
786  double theta1 = atan2((double)(verticesList[0].y() - verticesList[1].y()), (double)(verticesList[0].x() - verticesList[1].x()));
787  double theta2 = atan2((double)(verticesList[2].y() - verticesList[1].y()), (double)(verticesList[2].x() - verticesList[1].x()));
788  double angle = (theta1 - theta2);
789 
790  // Force the angle into [0,2PI]
791  while(angle < 0.0) {
792  angle += PI * 2;
793  }
794  while(angle > PI * 2) {
795  angle -= PI * 2;
796  }
797 
798  // With our [0,2PI] angle, let's make it [0,PI] to get the interior angle.
799  if(angle > PI) {
800  angle = (PI * 2.0) - angle;
801  }
802 
803  result = Angle(angle, Angle::Radians);
804  }
805 
806  return result;
807  }
808 
813  if(cubeViewport() != NULL) {
814  cubeViewport()->viewport()->repaint();
815  }
816  }
817 
823  geos::geom::Geometry *RubberBandTool::geometry() {
824  geos::geom::Geometry *geometry = NULL;
825  QList<QPoint> verticesList = vertices();
826 
827  switch(p_bandingMode) {
828  case AngleMode: {
829  if(verticesList.size() != 3)
830  break;
831 
832  geos::geom::CoordinateSequence *points1 = new geos::geom::CoordinateArraySequence();
833  geos::geom::CoordinateSequence *points2 = new geos::geom::CoordinateArraySequence();
834 
835  points1->add(geos::geom::Coordinate(verticesList[0].x(), verticesList[0].y()));
836  points1->add(geos::geom::Coordinate(verticesList[1].x(), verticesList[1].y()));
837  points2->add(geos::geom::Coordinate(verticesList[1].x(), verticesList[1].y()));
838  points2->add(geos::geom::Coordinate(verticesList[2].x(), verticesList[2].y()));
839 
840  geos::geom::LineString *line1 = globalFactory.createLineString(points1);
841  geos::geom::LineString *line2 = globalFactory.createLineString(points2);
842  std::vector<geos::geom::Geometry *> *lines = new std::vector<geos::geom::Geometry *>;
843  lines->push_back(line1);
844  lines->push_back(line2);
845 
846  geos::geom::MultiLineString *angle = globalFactory.createMultiLineString(lines);
847  geometry = angle;
848  }
849  break;
850 
851  case CircleMode:
852  case EllipseMode: {
853  if(verticesList.size() != 2)
854  break;
855  // A circle is an ellipse, so it gets no special case
856  // Equation of an ellipse: (x-h)^2/a^2 + (y-k)^2/b^2 = 1 where
857  // h is the X-location of the center, k is the Y-location of the center
858  // a is the x-radius and b is the y-radius.
859  // Solving for y, we get
860  // y = +-sqrt(b^2(1-(x-h)^2/a^2)) + k
861  // and our domain is [h-a,h+a]
862  // We need the equation of this ellipse!
863  double h = verticesList[0].x();
864  double k = verticesList[0].y();
865  double a = abs(verticesList[0].x() - verticesList[1].x());
866  double b = abs(verticesList[0].y() - verticesList[1].y());
867  if(a == 0)
868  break; // Return null, we can't make an ellipse from this.
869  if(b == 0)
870  break; // Return null, we can't make an ellipse from this.
871 
872  // We're ready to try to solve
873  double originalX = 0.0, originalY = 0.0;
874  geos::geom::CoordinateSequence *points = new geos::geom::CoordinateArraySequence();
875 
876  // Now iterate through our domain, solving for y positive, using 1/5th of a pixel increments
877  for(double x = h - a; x <= h + a; x += 0.2) {
878  double y = sqrt(pow(b, 2) * (1.0 - pow((x - h), 2) / pow(a, 2))) + k;
879  points->add(geos::geom::Coordinate(x, y));
880 
881  if(x == h - a) {
882  originalX = x;
883  originalY = y;
884  }
885  }
886 
887  // Iterate through our domain backwards, solving for y negative, using 1/5th of a pixel decrements
888  for(double x = h + a; x >= h - a; x -= 0.2) {
889  double y = -1.0 * sqrt(pow(b, 2) * (1.0 - pow((x - h), 2) / pow(a, 2))) + k;
890  points->add(geos::geom::Coordinate(x, y));
891  }
892 
893  points->add(geos::geom::Coordinate(originalX, originalY));
894 
895  geometry = globalFactory.createPolygon(
896  globalFactory.createLinearRing(points), NULL
897  );
898  }
899  break;
900 
901  case RectangleMode:
902  case RotatedRectangleMode:
903  case PolygonMode: {
904  if(verticesList.size() < 3)
905  break;
906 
907  geos::geom::CoordinateSequence *points = new geos::geom::CoordinateArraySequence();
908 
909  for(int vertex = 0; vertex < verticesList.size(); vertex++) {
910  points->add(geos::geom::Coordinate(verticesList[vertex].x(), verticesList[vertex].y()));
911  }
912 
913  points->add(geos::geom::Coordinate(verticesList[0].x(), verticesList[0].y()));
914 
915  geometry = globalFactory.createPolygon(globalFactory.createLinearRing(points), NULL);
916 
917  }
918  break;
919 
920  case LineMode: {
921  if(verticesList.size() != 2)
922  break;
923 
924  geos::geom::CoordinateSequence *points = new geos::geom::CoordinateArraySequence();
925  points->add(geos::geom::Coordinate(verticesList[0].x(), verticesList[0].y()));
926  points->add(geos::geom::Coordinate(verticesList[1].x(), verticesList[1].y()));
927  geos::geom::LineString *line = globalFactory.createLineString(points);
928  geometry = line;
929  }
930  break;
931 
932  case SegmentedLineMode: {
933  if(verticesList.size() < 2)
934  break;
935  geos::geom::CoordinateSequence *points = new geos::geom::CoordinateArraySequence();
936 
937  for(int vertex = 0; vertex < verticesList.size(); vertex++) {
938  points->add(geos::geom::Coordinate(verticesList[vertex].x(), verticesList[vertex].y()));
939  }
940 
941  geos::geom::LineString *line = globalFactory.createLineString(points);
942  geometry = line;
943  }
944  break;
945  }
946 
947  if(geometry != NULL && !geometry->isValid()) {
948  geometry = NULL;
949  }
950 
951  return geometry;
952  }
953 
967  QRect rect;
968 
969  if(currentMode() == RectangleMode && figureValid()) {
970  QList<QPoint> verticesList = vertices();
971 
972  //Check the corners for upper left and lower right.
973  int x1, x2, y1, y2;
974  //Point 1 is in the left, make point1.x upperleft.x
975  if(verticesList[0].x() < verticesList[2].x()) {
976  x1 = verticesList[0].x();
977  x2 = verticesList[2].x();
978  }
979  //Otherwise Point1 is in the right, make point1.x lowerright.x
980  else {
981  x1 = verticesList[2].x();
982  x2 = verticesList[0].x();
983  }
984 
985  //Point 1 is in the top, make point1.y upperleft.y
986  if(verticesList[0].y() < verticesList[2].y()) {
987  y1 = verticesList[0].y();
988  y2 = verticesList[2].y();
989  }
990  //Otherwise Point1 is in the bottom, make point1.y lowerright.y
991  else {
992  y1 = verticesList[2].y();
993  y2 = verticesList[0].y();
994  }
995 
996  rect = QRect(x1, y1, x2 - x1, y2 - y1);
997  }
998  //RectangleMode is not valid, or RubberBandTool is in the wrong mode, return error
999  else {
1000  QMessageBox::information((QWidget *)parent(),
1001  "Error", "**PROGRAMMER ERROR** Invalid RectangleMode");
1002  }
1003 
1004  return rect;
1005  }
1006 
1012  Qt::MouseButton RubberBandTool::mouseButton() {
1013  return p_mouseButton;
1014  }
1015 
1017  return figureComplete() && figureValid();
1018  }
1019 
1020 
1021  bool RubberBandTool::figureComplete() {
1022  bool complete = false;
1023 
1024  switch(p_bandingMode) {
1025  case AngleMode:
1026  complete = (p_vertices->size() == 2 && p_tracking) || (p_vertices->size() == 3);
1027  break;
1028  case LineMode:
1029  complete = (p_vertices->size() == 1 && p_tracking) || (p_vertices->size() == 2);
1030  break;
1031 
1032  case RectangleMode:
1033  complete = (p_vertices->size() == 1 && p_tracking) || (p_vertices->size() == 4);
1034  break;
1035 
1036  case RotatedRectangleMode:
1037  complete = (p_vertices->size() == 2 && p_tracking) || (p_vertices->size() == 4);
1038  break;
1039 
1040  case CircleMode:
1041  case EllipseMode:
1042  complete = (p_vertices->size() == 1 && p_tracking) || (p_vertices->size() == 2);
1043  break;
1044 
1045  case SegmentedLineMode:
1046  complete = (p_vertices->size() > 1 && !p_tracking) || (p_vertices->size() > 0);
1047  break;
1048 
1049  case PolygonMode:
1050  complete = (p_vertices->size() > 2 && !p_tracking);
1051  break;
1052  }
1053  return complete;
1054  }
1055 
1056  bool RubberBandTool::figureValid() {
1057  if(!figureComplete())
1058  return false;
1059  bool valid = true;
1060  QList<QPoint> verticesList = vertices();
1061 
1062  switch(p_bandingMode) {
1063  case AngleMode: {
1064  // Just make sure the vertex and an angle side don't lie on the same point
1065  // No point tolerance allowed
1066  valid = verticesList[0].x() != verticesList[1].x() || verticesList[0].y() != verticesList[1].y();
1067  valid &= verticesList[2].x() != verticesList[1].x() || verticesList[2].y() != verticesList[1].y();
1068  }
1069  break;
1070  case LineMode: {
1071  // Just make sure the line doesnt start/end at same point if point not allowed
1072  if(p_pointTolerance == 0) {
1073  valid = verticesList[0].x() != verticesList[1].x() || verticesList[0].y() != verticesList[1].y();
1074  }
1075  }
1076  break;
1077 
1078  case RectangleMode: {
1079  // Make sure there's a height and width if point not allowed
1080  if(p_pointTolerance == 0) {
1081  valid = verticesList[0].x() != verticesList[2].x() && verticesList[0].y() != verticesList[2].y();
1082  }
1083  }
1084  break;
1085 
1086  case RotatedRectangleMode: {
1087  // Make sure there's a height and width once again, point tolerance not allowed
1088  valid = verticesList[0].x() != verticesList[1].x() || verticesList[0].y() != verticesList[1].y();
1089  valid &= verticesList[1].x() != verticesList[2].x() || verticesList[1].y() != verticesList[2].y();
1090  }
1091  break;
1092 
1093  case CircleMode:
1094  case EllipseMode: {
1095  // Make sure there's a height and width, point tolerance not allowed
1096  valid = verticesList[0].x() != verticesList[1].x() && verticesList[0].y() != verticesList[1].y();
1097  }
1098  break;
1099 
1100  case SegmentedLineMode: {
1101  valid = verticesList.size() > 1;
1102  }
1103  break;
1104 
1105  case PolygonMode: {
1106  // For polygons, we must revert back to using geos
1107  geos::geom::Geometry *poly = geometry();
1108  valid = poly && poly->isValid();
1109  delete poly;
1110  }
1111  break;
1112  }
1113 
1114  return valid;
1115  }
1116 
1117  void RubberBandTool::enablePoints(unsigned int pixTolerance) {
1118  p_pointTolerance = pixTolerance;
1119  }
1120 
1121  bool RubberBandTool::figureIsPoint() {
1122  if(!figureValid())
1123  return false;
1124  bool isPoint = true;
1125  QList<QPoint> verticesList = vertices();
1126 
1127  switch(p_bandingMode) {
1128  case AngleMode:
1129  case RotatedRectangleMode:
1130  case CircleMode:
1131  case EllipseMode:
1132  case PolygonMode:
1133  case SegmentedLineMode:
1134  isPoint = false;
1135  break;
1136 
1137  case LineMode: {
1138  isPoint = (abs(verticesList[0].x() - verticesList[1].x()) +
1139  abs(verticesList[0].y() - verticesList[1].y()) < (int)p_pointTolerance);
1140  }
1141  break;
1142 
1143  case RectangleMode: {
1144  isPoint = (abs(verticesList[0].x() - verticesList[2].x()) +
1145  abs(verticesList[0].y() - verticesList[2].y()) < (int)p_pointTolerance);
1146  }
1147  break;
1148  }
1149 
1150  return isPoint;
1151  }
1152 
1153 
1156  reset();
1157  repaint();
1158  }
1159 
1160 
1161  RubberBandTool::RubberBandMode RubberBandTool::currentMode() {
1162  return p_bandingMode;
1163  };
1164 
1165 
1166  double RubberBandTool::area() {
1167  return 0.0; //<! Returns the area of the figure
1168  }
1169 
1170 
1171  void RubberBandTool::enableAllClicks() {
1172  p_allClicks = true;
1173  }
1174 
1175 
1176  void RubberBandTool::escapeKeyPress() {
1177  reset();
1178  }
1179 
1180 
1181  void RubberBandTool::scaleChanged() {
1182  reset();
1183  }
1184 
1185 
1186 }
Cube display widget for certain Isis MDI applications.
const double PI
The mathematical constant PI.
Definition: Constants.h:56
void reset()
This initializes the class except for the current mode, which is set on enable.
QList< QPoint > vertices()
This method returns the vertices.
CubeViewportList * cubeViewportList() const
Return the list of cubeviewports.
Definition: Tool.cpp:390
QRect rectangle()
This method returns a rectangle from the vertices set by the RubberBandTool.
Qt::MouseButton mouseButton()
This method returns the mouse button modifier.
Namespace for the standard library.
void mouseMove(QPoint p, Qt::MouseButton)
If tracking is not enabled, this does nothing.
void setDrawActiveViewportOnly(bool activeOnly=false)
This called to set whether rubber band is drawn on active viewport only rather than all linked viewpo...
void clear()
clears the rubber band!
QPoint snapMouse(QPoint)
moves the mouse&#39;s location p to the nearest axis
void mouseButtonRelease(QPoint p, Qt::MouseButton s)
If this is not the left mouse button, this does nothing.
void repaint()
This method will call the viewport&#39;s repaint if there is a current cube viewport. ...
void calcRectCorners(QPoint corner1, QPoint corner2, QPoint &corner3, QPoint &corner4)
Given two set corners, and the mouse location, this method will interpolate the last two corners...
bool isLinked() const
Is the viewport linked with other viewports.
void mouseDoubleClick(QPoint p)
This triggers on a second mouse press.
void paintVerticesConnected(QPainter *painter)
This paints connecting lines to p_vertices.
void paintViewport(MdiCubeViewport *vp, QPainter *painter)
This is the main paint method for the rubber bands.
Defines an angle and provides unit conversions.
Definition: Angle.h:62
bool isValid()
This returns true if we can return complete & valid data.
void disable()
This is called when something is not using me, so turn off events, reset & repaint to clear the clear...
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
Base class for the Qisis tools.
Definition: Tool.h:81
MdiCubeViewport * cubeViewport() const
Return the current cubeviewport.
Definition: Tool.h:211
geos::geom::Geometry * geometry()
Radians are generally used in mathematical equations, 0-2*PI is one circle, however these are more di...
Definition: Angle.h:80
void activate(bool)
Activates the tool.
Definition: Tool.cpp:131
void paintRectangle(QPoint upperLeft, QPoint lowerRight, QPainter *painter)
Given opposite corners, the other two are interpolated and the rectangle is drawn.
void mouseButtonPress(QPoint p, Qt::MouseButton s)
If the click is not the left mouse button, this does nothing.
void enable(RubberBandMode mode, bool showIndicatorColors=false)
This is called when changing modes or turning on.