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