Files
qt5-legatus-legacy/Lts/Viewer/SituationMap/Rubberbands/rubberband_rectangle.cpp
T
2026-08-13 18:33:10 +02:00

1914 lines
81 KiB
C++
Executable File

/**
**
** @RDE_HEADER@
** Implementation of rubberband class to define point feature geoemtry
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 17.06.2015, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#include "rubberband_rectangle.h"
#include "rubberband_point_handle.h"
#include "rubberband_frame_handle.h"
#include "rubberband_drawable.h"
#include "drawable_painter_path.h"
#include "rubberband_handle_proxy.h"
#include "rubberband_internal.h"
#include "rubberband_base_internal.h"
#include <MathUtils/math_const.h>
#include <MathUtils/simple_helper.h>
#include <arcgis/Map.h>
#include <arcgis/MapGraphicsView.h>
#include <QtCore/QLine>
namespace SituationMap { namespace RB
{
static bool epsilonEqual( qreal x1, qreal x2, qreal thresholdValue = 0.001 )
{
return (fabs(x1-x2) < thresholdValue);
} /* epsilonEqual */
static qreal distance(const QPointF & p0, const QPointF & p1)
{
qreal dx = p1.x() - p0.x();
qreal dy = p1.y() - p0.y();
return sqrt(dx*dx+dy*dy);
} /* distance */
struct RubberbandRectangle::InternalData
{
Q_DISABLE_COPY(InternalData)
const int _initialRectPixelSize = 100;
RubberbandRectangle * _me;
bool _constrainQuadratic;
bool _constrainOrthogonal;
qreal _minWidth;
qreal _maxWidth;
qreal _minHeight;
qreal _maxHeight;
qreal _rectWidth; // referred to map coordinates
qreal _rectHeight; // referred to map coordinate
QPointF _center; // center refferd to map coordinates
QPointF _oVec; // normalized orientation vector according to scene coordinate system
QPointF _screenCenter; // center of screen in scene coordinates
QPointF _mapOVec; // map rotation as normalized orientation vector
qreal _rectWidthScene; // rectangle width in scene coordinates
qreal _rectHeightScene; // rectangle height in scene coordinates
QPointF _centerScene; // rectangle center in scene coordinates
QPointF _constructionStart;
unsigned int _rotationHandleSize;
bool _useDottedLineHandle;
SituationMap::RefPointer<RubberbandDrawable> _tmpConstruction;
SituationMap::RefPointer<RubberbandFrameHandle> _frameHandle;
SituationMap::RefPointer<RubberbandPointHandle> _pointHandle[4];
SituationMap::RefPointer<RubberbandLineHandle> _lineHandle[4];
SituationMap::RefPointer<RubberbandPointHandle> _rotateHandle;
QVector<unsigned int> _pointIndexVector;
LOGGER_T _logger;
ModificationMode _modificationMode;
MathUtils::Matrix2D & _lastCoordinateChange;
InternalData( MathUtils::Matrix2D & lastCoordinateChange, RubberbandRectangle * me )
: _me(me)
, _constrainQuadratic(false)
, _constrainOrthogonal(false)
, _minWidth(0.0)
, _maxWidth(0.0)
, _minHeight(0.0)
, _maxHeight(0.0)
, _rectWidth(0.0)
, _rectHeight(0.0)
, _center()
, _oVec(1,0)
, _screenCenter(0,0)
, _mapOVec(1,0)
, _rectWidthScene(0.0)
, _rectHeightScene(0.0)
, _centerScene()
, _constructionStart()
, _rotationHandleSize(0)
, _useDottedLineHandle(false)
, _tmpConstruction()
, _frameHandle()
, _pointHandle()
, _lineHandle()
, _rotateHandle()
, _pointIndexVector()
, _logger(getLogger("RubberbandRectangle"))
, _modificationMode( ModificationMode::NoneRestriction )
, _lastCoordinateChange(lastCoordinateChange)
{
}
inline static QPointF rotateVertex( const QPointF & center, qreal x, qreal y, qreal sinA, qreal cosA )
{
return QPointF(x*cosA-y*sinA,y*cosA+x*sinA) + center;
} // rotateVertex
void setScreenCenter( Esri::ArcGISRuntime::MapGraphicsView * mapView );
void setRotatedFrame( Esri::ArcGISRuntime::MapGraphicsView * mapView );
void updateFrame( Esri::ArcGISRuntime::MapGraphicsView * mapView, QVector<QPointF> & points );
void updateFrame( Esri::ArcGISRuntime::MapGraphicsView * mapView, const QVector<RubberbandHandle::RubberbandCoordinate> & coordinateList );
inline QPointF getSceneOffsetRotationHandle(qreal margin)
{
qreal sinPhi = _oVec.y(); // * _mapOVec.x() + _oVec.x() * _mapOVec.y(); // Addition-Theorem
qreal cosPhi = _oVec.x(); // * _mapOVec.x() - _oVec.y() * _mapOVec.y();
return rotateVertex( QPointF(0,0), 0.0,-(margin+2*_rotationHandleSize), sinPhi, cosPhi );
} /* getSceneOffsetRotationHandle */
inline QPointF getScenePositionRotationHandle()
{
if ((_pointHandle[2]) && (_pointHandle[3]))
{
return (_pointHandle[2]->getCoordinate()._sceneCoordinate + _pointHandle[3]->getCoordinate()._sceneCoordinate) * 0.5;
}
QPointF p( 0.0, -_rectHeightScene*0.5);
return rotateVertex(_centerScene, p.x(), p.y(), _oVec.y(), _oVec.x() );
} /* getScenePositionRotationHandle */
inline void verifyConstrains()
{
if (_constrainQuadratic)
{
if ((_minWidth > 0) || (_minHeight > 0))
{
if (_minWidth < _minHeight) _minWidth = _minHeight;
else _minHeight = _minWidth;
}
if ((_maxWidth > 0) || (_maxHeight > 0))
{
if (_maxWidth < _maxHeight) _maxHeight = _maxWidth;
else _maxWidth = _maxHeight;
}
}
} /* verifyConstrains */
inline void setInitialRectSize(RubberbandRectangle * rubBnd, const QPointF & scenePos)
{
const QPoint & pixelPos = rubBnd->Rubberband::_internal->_mapView -> mapFromScene(scenePos);
setInitialRectSize( rubBnd, scenePos, pixelPos.x(), pixelPos.y() );
} /* setInitialRectSize */
inline void setInitialRectSize( RubberbandRectangle * rubBnd, const QPointF & scenePos, qreal pixelPosX, qreal pixelPosY )
{
verifyConstrains();
if ((_rectWidthScene <= 0.0) && (_rectHeightScene <= 0.0))
{
QPointF rectSize(rubBnd->pixel2ScenePosition(pixelPosX+_initialRectPixelSize,pixelPosY+_initialRectPixelSize));
_rectWidthScene = fabs(rectSize.x() - scenePos.x());
_rectHeightScene = fabs(rectSize.y() - scenePos.y());
setInitialRectSize( rubBnd );
}
} /* setInitialRectSize */
inline void setInitialRectSize(RubberbandRectangle * rubBnd, qreal sceneWidth, qreal sceneHeight)
{
verifyConstrains();
if ((_rectWidthScene <= 0.0) && (_rectHeightScene <= 0.0))
{
_rectWidthScene = sceneWidth;
_rectHeightScene = sceneHeight;
setInitialRectSize( rubBnd );
}
} /* setInitialRectSize */
bool minimumSizeReached( int pointHandleIdx = -1, int lineHandleIdx = -1, const QPointF & loc = QPointF() )
{
qreal min = 10;
bool result = false;
bool isPointHandle = ((pointHandleIdx >= 0) && (pointHandleIdx < 4));
bool isLineHandle = ((lineHandleIdx >= 0) && (lineHandleIdx < 4));
if (isPointHandle ^ isLineHandle)
{
qreal h = loc.y() - _centerScene.y();
qreal w = loc.x() - _centerScene.x();
if (lineHandleIdx >=0)
{
switch (lineHandleIdx)
{
case 0 : if (h > min) result = true; break;
case 1 : if (w > min) result = true; break;
case 2 : if (h < -min) result = true; break;
case 3 : if (w < -min) result = true; break;
}
}
if (isPointHandle)
{
switch (pointHandleIdx)
{
case 0 : if ((w < -min) && (h > min)) result = true; break;
case 1 : if ((w > min) && (h > min)) result = true; break;
case 2 : if ((w > min) && (h < -min)) result = true; break;
case 3 : if ((w < -min) && (h < -min)) result = true; break;
}
}
return result;
}
else
{
for (const auto &ph : _pointHandle) if (nullptr == ph.get()) return false;
min -= 1;
if (QLineF( _pointHandle[0].get()->getCoordinate()._sceneCoordinate, _pointHandle[1].get()->getCoordinate()._sceneCoordinate ).length() < min) result = true;
if (QLineF( _pointHandle[0].get()->getCoordinate()._sceneCoordinate, _pointHandle[2].get()->getCoordinate()._sceneCoordinate ).length() < min) result = true;
if (QLineF( _pointHandle[0].get()->getCoordinate()._sceneCoordinate, _pointHandle[3].get()->getCoordinate()._sceneCoordinate ).length() < min) result = true;
if (QLineF( _pointHandle[1].get()->getCoordinate()._sceneCoordinate, _pointHandle[2].get()->getCoordinate()._sceneCoordinate ).length() < min) result = true;
if (QLineF( _pointHandle[2].get()->getCoordinate()._sceneCoordinate, _pointHandle[3].get()->getCoordinate()._sceneCoordinate ).length() < min) result = true;
}
return result;
} // minimumSizeReached
private:
inline void setInitialRectSize( RubberbandRectangle * rubBnd )
{
auto length = [](qreal x, qreal y) -> qreal { return sqrt(x*x+y*y); };
RubberbandHandle::RubberbandCoordinate mapPointCenter( rubBnd->Rubberband::_internal->_mapView, _centerScene );
const Esri::ArcGISRuntime::Point & mapCenter = mapPointCenter._mapCoordinate;
const Esri::ArcGISRuntime::Point mapMaxW( mapCenter.x() + _maxWidth
, mapCenter.y()
, rubBnd->Rubberband::_internal->_mapView->map()->spatialReference() );
const Esri::ArcGISRuntime::Point mapMinW( mapCenter.x() + _minWidth
, mapCenter.y()
, rubBnd->Rubberband::_internal->_mapView->map()->spatialReference() );
const Esri::ArcGISRuntime::Point mapMaxH( mapCenter.x()
, mapCenter.y() + _maxHeight
, rubBnd->Rubberband::_internal->_mapView->map()->spatialReference() );
const Esri::ArcGISRuntime::Point mapMinH( mapCenter.x()
, mapCenter.y() + _minHeight
, rubBnd->Rubberband::_internal->_mapView->map()->spatialReference() );
RubberbandHandle::RubberbandCoordinate mapPointMaxW( rubBnd->Rubberband::_internal->_mapView, mapMaxW );
RubberbandHandle::RubberbandCoordinate mapPointMinW( rubBnd->Rubberband::_internal->_mapView, mapMinW );
RubberbandHandle::RubberbandCoordinate mapPointMaxH( rubBnd->Rubberband::_internal->_mapView, mapMaxH );
RubberbandHandle::RubberbandCoordinate mapPointMinH( rubBnd->Rubberband::_internal->_mapView, mapMinH );
qreal maxSceneWidth = length(mapPointMaxW._sceneCoordinate.x()-_centerScene.x(), mapPointMaxW._sceneCoordinate.y()-_centerScene.y());
qreal minSceneWidth = length(mapPointMinW._sceneCoordinate.x()-_centerScene.x(), mapPointMinW._sceneCoordinate.y()-_centerScene.y());
qreal maxSceneHeight = length(mapPointMaxH._sceneCoordinate.x()-_centerScene.x(), mapPointMaxH._sceneCoordinate.y()-_centerScene.y());
qreal minSceneHeight = length(mapPointMinH._sceneCoordinate.x()-_centerScene.x(), mapPointMinH._sceneCoordinate.y()-_centerScene.y());
if ((_maxWidth > 0) && (_rectWidthScene > maxSceneWidth )) _rectWidthScene = maxSceneWidth;
if ((_minWidth > 0) && (_rectWidthScene < minSceneWidth )) _rectWidthScene = minSceneWidth;
if ((_maxHeight > 0) && (_rectHeightScene > maxSceneHeight)) _rectHeightScene = maxSceneHeight;
if ((_minHeight > 0) && (_rectHeightScene < minSceneHeight)) _rectHeightScene = minSceneHeight;
RubberbandHandle::RubberbandCoordinate mapRectWidth ( rubBnd->Rubberband::_internal->_mapView, _centerScene + QPointF( _rectWidthScene, 0.0 ));
RubberbandHandle::RubberbandCoordinate mapRectHeight( rubBnd->Rubberband::_internal->_mapView, _centerScene + QPointF( 0.0 , _rectHeightScene ));
_rectWidth = length( mapRectWidth._mapCoordinate.x()-mapCenter.x(), mapRectWidth._mapCoordinate.y()-mapCenter.y() );
_rectHeight = length( mapRectHeight._mapCoordinate.x()-mapCenter.x(), mapRectHeight._mapCoordinate.y()-mapCenter.y() );
_center = QPointF(mapCenter.x(),mapCenter.y());
rubBnd->_rectangleIsValid = true;
} /* setInitialRectSize */
}; /* InternalData */
void RubberbandRectangle::InternalData::setScreenCenter( Esri::ArcGISRuntime::MapGraphicsView * mapView )
{
qreal mapRotatePhi = mapView->mapRotation() * MathConst::pi / 180.0;
_mapOVec = QPointF( cos(mapRotatePhi), sin(mapRotatePhi) );
_screenCenter = QPointF( mapView->viewport()->size().width()* 0.5, mapView->viewport()->size().height()* 0.5 );
} /* RubberbandRectangle::InternalData::setScreenCenter */
void RubberbandRectangle::InternalData::setRotatedFrame( Esri::ArcGISRuntime::MapGraphicsView* )
{
// _center and unrotated _rectWidth and _rectHeight is already set from setInitialRectSize() which has been executed before
qreal sinA = -_oVec.y(); // be carefull the scene coordinate system is contra clockwise -- sinA must be negated
qreal cosA = _oVec.x();
qreal w = _rectWidth * 0.5;
qreal h = _rectHeight * 0.5;
QPointF p0 = MathUtils::rotate( _center + QPointF(-w,-h ), _center, sinA, cosA );
QPointF p1 = MathUtils::rotate( _center + QPointF( w,-h ), _center, sinA, cosA );
QPointF p2 = MathUtils::rotate( _center + QPointF( w, h ), _center, sinA, cosA );
_rectWidth = distance(p1,p0);
_rectHeight = distance(p2,p1);
} /* RubberbandRectangle::InternalData::setRotatedFrame */
void RubberbandRectangle::InternalData::updateFrame( Esri::ArcGISRuntime::MapGraphicsView * mapView, QVector<QPointF> & points )
{
DEBUG_LOG(_logger) << LOGFUNCTION << " RubberbandRectangle::InternalData::updateFrame for map-coordinates " << _oVec.x() << " ; " << _oVec.y();
qreal cosA = _oVec.x();
qreal sinA = _oVec.y(); // be carefull the scene coordinate system is contra clockwise -- sinA must be negated
qreal xmin = std::numeric_limits<qreal>::infinity();
qreal xmax = - std::numeric_limits<qreal>::infinity();
qreal ymin = std::numeric_limits<qreal>::infinity();
qreal ymax = - std::numeric_limits<qreal>::infinity();
for (unsigned int i = 0; i < 4; ++i)
{
const QPointF & p = MathUtils::rotate( points[i], points[0], sinA, cosA ); // rotate back to orthogonal position
const qreal & x = p.x();
const qreal & y = p.y();
if (x < xmin) xmin = x;
if (x > xmax) xmax = x;
if (y < ymin) ymin = y;
if (y > ymax) ymax = y;
DEBUG_LOG(_logger) << LOGFUNCTION << " point " << i << " : " << p.x() << " ; " << p.y();
}
// set center, keep in mind that the center point must be rotated to it's real position
_center = MathUtils::rotate( QPointF((xmin + xmax)*0.5, (ymin + ymax)*0.5), points[0], -sinA, cosA );
if (_constrainQuadratic)
{
double w = xmax-xmin;
double h = ymax-ymin;
if (w > h) w = h;
if (h > w) h = w;
if ((_modificationMode != ModificationMode::RotationOnly) && (_modificationMode != ModificationMode::TranslationOnly))
{
// size of rectangle
_rectWidth = w; w *= 0.5;
_rectHeight = h; h *= 0.5;
}
points[0] = MathUtils::rotate( _center + QPointF(-w,-h), _center, -sinA, cosA );
points[1] = MathUtils::rotate( _center + QPointF( w,-h), _center, -sinA, cosA );
points[2] = MathUtils::rotate( _center + QPointF( w, h), _center, -sinA, cosA );
points[3] = MathUtils::rotate( _center + QPointF(-w, h), _center, -sinA, cosA );
}
else
{
// size of rectangle
_rectWidth = xmax-xmin;
_rectHeight = ymax-ymin;
}
QVector<QPointF> sceneCoordinates;
sceneCoordinates.reserve(4);
for (unsigned int i = 0; i < 4; ++i)
{
const QPointF & p = MathUtils::rotate( points[i], points[0], sinA, cosA ); // rotate back to orthogonal position, keep in mind points are map coordinates
const qreal & x = p.x();
const qreal & y = p.y();
RubberbandHandle::RubberbandCoordinate point( mapView, Esri::ArcGISRuntime::Point( x, y, mapView->map()->spatialReference() ));
sceneCoordinates.append(point._sceneCoordinate );
}
RubberbandHandle::RubberbandCoordinate point( mapView, Esri::ArcGISRuntime::Point( _center.x(), _center.y(), mapView->map()->spatialReference() ));
_centerScene = point._sceneCoordinate;
xmin = std::numeric_limits<qreal>::infinity();
xmax = - std::numeric_limits<qreal>::infinity();
ymin = std::numeric_limits<qreal>::infinity();
ymax = - std::numeric_limits<qreal>::infinity();
for (const QPointF & p : qAsConst(sceneCoordinates))
{
const QPointF & unRotMap = MathUtils::rotate( p, _screenCenter, -_mapOVec.y(), _mapOVec.x() );
const qreal & x = unRotMap.x();
const qreal & y = unRotMap.y();
if (x < xmin) xmin = x;
if (x > xmax) xmax = x;
if (y < ymin) ymin = y;
if (y > ymax) ymax = y;
}
// size of scene rectangle
_rectWidthScene = xmax-xmin;
_rectHeightScene = ymax-ymin;
DEBUG_LOG(_logger) << LOGFUNCTION << " center = " << _centerScene.x() << " ; " << _centerScene.y()
<< " _rectWidthScene = " << _rectWidthScene
<< " _rectHeightScene = " << _rectHeightScene;
} /* RubberbandRectangle::InternalData::updateFrame */
void RubberbandRectangle::InternalData::updateFrame( Esri::ArcGISRuntime::MapGraphicsView*, const QVector<RubberbandHandle::RubberbandCoordinate> & coordinateList )
{
if (coordinateList.isEmpty()) return;
DEBUG_LOG(_logger) << LOGFUNCTION << " RubberbandRectangle::InternalData::updateFrame for scene-coordinates " << _oVec.x() << " ; " << _oVec.y();
qreal cosA = _oVec.x();
qreal sinA = _oVec.y();
qreal xmin = std::numeric_limits<qreal>::infinity();
qreal xmax = - std::numeric_limits<qreal>::infinity();
qreal ymin = std::numeric_limits<qreal>::infinity();
qreal ymax = - std::numeric_limits<qreal>::infinity();
QVector<QPointF> sceneCoordinates;
sceneCoordinates.reserve(4);
for (unsigned int i = 0; i < 4; ++i)
{
QPointF p = MathUtils::rotate( coordinateList[i]._sceneCoordinate, coordinateList[0]._sceneCoordinate, -sinA, cosA ); // rotate back to orthogonal position
sceneCoordinates.append( p );
const QPointF & unRotMap = MathUtils::rotate( p, _screenCenter, -_mapOVec.y(), _mapOVec.x() );
const qreal & x = unRotMap.x();
const qreal & y = unRotMap.y();
if (x < xmin) xmin = x;
if (x > xmax) xmax = x;
if (y < ymin) ymin = y;
if (y > ymax) ymax = y;
}
// set center, keep in mind that the center point must be rotated to it's real position
const QPointF & rotMap = MathUtils::rotate( QPointF((xmin + xmax)*0.5, (ymin + ymax)*0.5), _screenCenter, _mapOVec.y(), _mapOVec.x() );
_centerScene = MathUtils::rotate( rotMap, coordinateList[0]._sceneCoordinate, sinA, cosA );
// size of scene rectangle
_rectWidthScene = xmax - xmin;
_rectHeightScene = ymax - ymin;
xmin = std::numeric_limits<qreal>::infinity();
xmax = -std::numeric_limits<qreal>::infinity();
ymin = std::numeric_limits<qreal>::infinity();
ymax = -std::numeric_limits<qreal>::infinity();
QPointF p0(coordinateList[0]._mapCoordinate.x(), coordinateList[0]._mapCoordinate.y());
for (unsigned int i = 0; i < 4; ++i)
{
QPointF pi(coordinateList[i]._mapCoordinate.x(), coordinateList[i]._mapCoordinate.y());
QPointF p = MathUtils::rotate(pi, p0, sinA, cosA); // rotate back to orthogonal position
const qreal & x = p.x();
const qreal & y = p.y();
if (x < xmin) xmin = x;
if (x > xmax) xmax = x;
if (y < ymin) ymin = y;
if (y > ymax) ymax = y;
}
qreal sRectWidth = _rectWidth;
qreal sRectHeight = _rectHeight;
// size of rectangle
_rectWidth = xmax - xmin;
_rectHeight = ymax - ymin;
QPointF lastCenter = _center;
_center = MathUtils::rotate(QPointF((xmin + xmax)*0.5, (ymin + ymax)*0.5), p0, -sinA, cosA);
DEBUG_LOG(_logger) << LOGFUNCTION << " centerScene = " << _centerScene.x() << " ; " << _centerScene.y() << " _rectWidthScene = " << _rectWidthScene << " _rectHeightScene = " << _rectHeightScene;
DEBUG_LOG(_logger) << LOGFUNCTION << " center = " << _center.x() << " ; " << _center.y() << " _rectWidth = " << _rectWidth << " _rectHeight = " << _rectHeight ;
sRectWidth = _rectWidth / sRectWidth;
sRectHeight = _rectHeight / sRectHeight;
if (!_lastCoordinateChange.isRotation()) // _lastCoordinateChange.isIdentity())
{
// move center to origin
_lastCoordinateChange.putTranslation( -lastCenter );
// do scaling
_lastCoordinateChange.putScale( QPointF(sRectWidth,sRectHeight), QPointF(cosA,sinA) );
// move back to new center
_lastCoordinateChange.putTranslation( _center );
}
} /* RubberbandRectangle::InternalData::updateFrame */
RubberbandRectangle::RubberbandRectangle(SituationMap::Map * map, QObject * parent)
: Rubberband(map,parent)
, _internal(new InternalData(_lastCoordinateChange,this))
, _rectangleIsValid(false)
, _lastCoordinateChange()
{
} /* RubberbandRectangle::RubberbandRectangle */
RubberbandRectangle::~RubberbandRectangle()
{
delete _internal;
} /* RubberbandRectangle::~RubberbandRectangle */
void RubberbandRectangle::setModificationMode( ModificationMode mode ) { _internal->_modificationMode = mode; }
bool RubberbandRectangle::rectangleIsValid() const { return _rectangleIsValid; }
void RubberbandRectangle::minWidth(qreal x) { _internal->_minWidth = (x > 0) ? x : 0.0; }
void RubberbandRectangle::maxWidth(qreal x) { _internal->_maxWidth = (x > 0) ? x : 0.0; }
void RubberbandRectangle::minHeight(qreal x) { _internal->_minHeight = (x > 0) ? x : 0.0; }
void RubberbandRectangle::maxHeight(qreal x) { _internal->_maxHeight = (x > 0) ? x : 0.0; }
QPointF RubberbandRectangle::getSceneCenter() const { return _internal->_centerScene; }
qreal RubberbandRectangle::getSceneWidth() const { return _internal->_rectWidthScene; }
qreal RubberbandRectangle::getSceneHeight() const { return _internal->_rectHeightScene; }
QPointF RubberbandRectangle::getCenter() const { return _internal->_center; }
qreal RubberbandRectangle::getWidth() const { return _internal->_rectWidth; }
qreal RubberbandRectangle::getHeight() const { return _internal->_rectHeight; }
void RubberbandRectangle::getSceneRotation( qreal & sinA, qreal & cosA ) const { sinA = -_internal->_oVec.y() ; cosA = _internal->_oVec.x(); }
void RubberbandRectangle::getMapRotation ( qreal & sinA, qreal & cosA ) const { sinA = -_internal->_mapOVec.y(); cosA = _internal->_mapOVec.x(); }
void RubberbandRectangle::useDottedLineHandles(bool on)
{
_internal->_useDottedLineHandle = on;
} /* RubberbandRectangle::useDottedLineHandles */
void RubberbandRectangle::setButtonPosition()
{
QPointF buttonPos(0, 0);
if (Rubberband::_internal->_handleList.size() == 4)
{
for (unsigned int i = 0; i < 4; ++i)
{
if (Rubberband::_internal->_handleList[i].valid())
{
RubberbandPointHandle * handlePointPtr = dynamic_cast<RubberbandPointHandle*>(Rubberband::_internal->_handleList[i].get());
const QPointF & p = handlePointPtr->getCoordinate()._sceneCoordinate;
if (i > 0)
{
if ((p.x() == buttonPos.x()) && (p.y() >= buttonPos.y()))
{
buttonPos = p;
}
else if (p.x() > buttonPos.x()) buttonPos = p;
}
else buttonPos = p;
}
}
}
else buttonPos = InternalData::rotateVertex( _internal->_centerScene, _internal->_rectWidthScene*0.5, _internal->_rectHeightScene*0.5, _internal->_oVec.y(), fabs(_internal->_oVec.x()) );
_baseInternal->_buttonProxy->setPos( QPointF(buttonPos.x(),buttonPos.y()) );
} /* RubberbandRectangle::setButtonPosition */
void RubberbandRectangle::activateButtons()
{
if (isEnabledModificationState())
{
_baseInternal->_hasUndoButton = false;
_baseInternal->_applyIsPossible = true;
_baseInternal->_undoIsPossible = false;
Rubberband::activateButtons();
_baseInternal->_buttonProxy->setRotateSceneOffset(false);
setButtonPosition();
_baseInternal->_buttonProxy->show();
}
else Rubberband::deactivateButtons();
} /* RubberbandRectangle::activateButtons */
#include "./Rubberbands/winkel_icon.xpm"
void RubberbandRectangle::constructHandles()
{
_internal->setScreenCenter( Rubberband::_internal->_mapView );
_internal->verifyConstrains();
// remove existing handles
Rubberband::_internal->_handleList.clear();
if ((_internal->_modificationMode != ModificationMode::RotationOnly) && (_internal->_modificationMode != ModificationMode::TranslationOnly))
{
qreal w2 = _internal->_rectWidth * 0.5;
qreal h2 = _internal->_rectHeight * 0.5;
qreal sinA = _internal->_oVec.y();
qreal cosA = _internal->_oVec.x();
QPointF p0 = InternalData::rotateVertex( _internal->_center, -w2, -h2, -sinA, cosA );
QPointF p1 = InternalData::rotateVertex( _internal->_center, w2, -h2, -sinA, cosA );
QPointF p2 = InternalData::rotateVertex( _internal->_center, w2, h2, -sinA, cosA );
QPointF p3 = InternalData::rotateVertex( _internal->_center, -w2, h2, -sinA, cosA );
RubberbandHandle::RubberbandCoordinate mapP0( Rubberband::_internal->_mapView, Esri::ArcGISRuntime::Point( p0.x(), p0.y(), Rubberband::_internal->_mapView->map()->spatialReference() ));
RubberbandHandle::RubberbandCoordinate mapP1( Rubberband::_internal->_mapView, Esri::ArcGISRuntime::Point( p1.x(), p1.y(), Rubberband::_internal->_mapView->map()->spatialReference() ));
RubberbandHandle::RubberbandCoordinate mapP2( Rubberband::_internal->_mapView, Esri::ArcGISRuntime::Point( p2.x(), p2.y(), Rubberband::_internal->_mapView->map()->spatialReference() ));
RubberbandHandle::RubberbandCoordinate mapP3( Rubberband::_internal->_mapView, Esri::ArcGISRuntime::Point( p3.x(), p3.y(), Rubberband::_internal->_mapView->map()->spatialReference() ));
_internal->_lineHandle[0] = new RubberbandLineHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapP0._sceneCoordinate, mapP1._sceneCoordinate, _lineHandleWidth, _internal->_useDottedLineHandle );
_internal->_lineHandle[1] = new RubberbandLineHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapP1._sceneCoordinate, mapP2._sceneCoordinate, _lineHandleWidth, _internal->_useDottedLineHandle );
_internal->_lineHandle[2] = new RubberbandLineHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapP2._sceneCoordinate, mapP3._sceneCoordinate, _lineHandleWidth, _internal->_useDottedLineHandle );
_internal->_lineHandle[3] = new RubberbandLineHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapP3._sceneCoordinate, mapP0._sceneCoordinate, _lineHandleWidth, _internal->_useDottedLineHandle );
_internal->_pointHandle[0] = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapP0._mapCoordinate );
_internal->_pointHandle[1] = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapP1._mapCoordinate );
_internal->_pointHandle[2] = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapP2._mapCoordinate );
_internal->_pointHandle[3] = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapP3._mapCoordinate );
for (auto ph : _internal->_pointHandle)
{
Rubberband::_internal->_handleList.append( SituationMap::RefPointer<RubberbandHandle>(ph.get()) );
ph -> setDrawColor(_handleDrawColor);
ph -> setFillColor( _handleFillColor );
ph -> setSelectionColor(_handleSelectionColor);
}
for (auto lh : _internal->_lineHandle)
{
lh -> setDrawColor(_handleDrawColor);
lh -> setFillColor( _handleFillColor );
lh -> setSelectionColor(_handleSelectionColor);
}
}
if ((_internal->_modificationMode == ModificationMode::NoneRestriction) || (_internal->_modificationMode == ModificationMode::RotationOnly))
{
static QPixmap * icon = nullptr;
if (icon == nullptr)
{
icon = new QPixmap( QPixmap( winkelIcon_xpm ).transformed(QTransform::fromScale(0.5,0.5)) );
}
if (!_internal->_constrainOrthogonal)
{
_internal->_rotationHandleSize = std::max( icon->width(), icon->height() ) * 0.5;
_internal->_rotateHandle = new RubberbandPointHandle( Rubberband::_internal->_mapView
, Rubberband::_internal->_mapScene
, _internal->getScenePositionRotationHandle(), *icon );
_internal->_rotateHandle -> setSceneOffset( _internal->getSceneOffsetRotationHandle( (_frameHandleMargin > 0) ? _frameHandleMargin : 0) );
_internal->_rotateHandle -> setDrawColor(_handleDrawColor);
_internal->_rotateHandle -> setFillColor( _handleFillColor );
_internal->_rotateHandle -> setSelectionColor(_handleSelectionColor );
}
}
_internal->_frameHandle = new RubberbandFrameHandle( Rubberband::_internal->_mapView
, Rubberband::_internal->_mapScene
, _frameHandleMargin );
_internal->_frameHandle -> setDrawColor( _frameColor );
_internal->_frameHandle -> setFillColor( _frameFillColor );
_internal->_frameHandle -> setSelectionColor(_frameSelectionColor);
_internal->_frameHandle -> setSelectable((_internal->_modificationMode != ModificationMode::ScalingOnly) && (_internal->_modificationMode != ModificationMode::RotationOnly));
_internal->_frameHandle -> drawDisabled( true );
// lambda slot to update screen center and map rotation
connect( _internal->_frameHandle.get(), &RubberbandFrameHandle::adjusted, this, [this]()
{
this->_internal->setScreenCenter( this->Rubberband::_internal->_mapView );
this->setButtonPosition();
} );
activateButtons();
} /* RubberbandRectangle::constructHandles */
QVector<QPointF> RubberbandRectangle::coordinateVector( CoordinateSystem cs ) const { return rectangleVerticies(cs); }
QPointF RubberbandRectangle::singleCoordinate(unsigned int, CoordinateSystem cs ) const { return centerPoint(cs); }
void RubberbandRectangle::setCoordinateVector( const QVector<QPointF> & coordinateVector, CoordinateSystem cs )
{
if (coordinateVector.size() == 4)
{
rectangleVerticies( coordinateVector, cs );
}
} /* RubberbandRectangle::setCoordinateVector */
QPointF RubberbandRectangle::centerPoint( CoordinateSystem cs ) const
{
return projectCoordinateFromMap( cs, _internal->_center );
} /* RubberbandRectangle::centerPoint */
void RubberbandRectangle::centerPoint(const QPointF &centerPoint, CoordinateSystem cs )
{
_internal->_center = projectCoordinateToMap( cs, centerPoint );
RubberbandHandle::RubberbandCoordinate point( Rubberband::_internal->_mapView,
Esri::ArcGISRuntime::Point( _internal->_center.x(),
_internal->_center.y(),
Rubberband::_internal->_mapView->map()->spatialReference() ));
_internal->_centerScene = point._sceneCoordinate;
_internal->setInitialRectSize( this, _internal->_centerScene );
_internal->setRotatedFrame( Rubberband::_internal->_mapView );
disenableConstructionState();
constructHandles();
activateButtons();
update();
} /* RubberbandRectangle::centerPoint */
void RubberbandRectangle::rectangleParameter( QPointF & center, qreal & width, qreal & height, QPointF & direction, CoordinateSystem cs )
{
center = projectCoordinateFromMap(cs,_internal->_center);
width = _internal->_rectWidth;
height = _internal->_rectHeight;
direction = QPointF(_internal->_oVec.x(),-_internal->_oVec.y());
// projectCoordinateFromMap( CoordinateSystem::Geo, center );
} /* RubberbandRectangle::rectangleParameter */
QVector<QPointF> RubberbandRectangle::rectangleVerticies( CoordinateSystem cs ) const
{
QVector<QPointF> points(4);
for ( unsigned int i = 0; i < 4; ++i )
{
// they may be emtpy (e.g. select action, move mouse but don't release button, hit 'ENTER'
if (!_internal->_pointHandle[i])
return {};
const Esri::ArcGISRuntime::Point & p = _internal->_pointHandle[i]-> getCoordinate()._mapCoordinate;
if (_internal->_pointIndexVector.size() == 4)
points[_internal->_pointIndexVector[i]] = projectCoordinateFromMap( cs,p);
else
points[i] = projectCoordinateFromMap( cs,p);
}
return points;
} /* RubberbandRectangle::rectangleVerticies */
void RubberbandRectangle::rectangleVerticies( const QVector<QPointF> & coordinateVector, CoordinateSystem cs )
{
if (coordinateVector.size() == 4)
{
QVector<QPointF> rectPoints;
// first do coordinate projection to map coordinate system
rectPoints.reserve(4);
for (auto loc : coordinateVector)
{
rectPoints.append( projectCoordinateToMap( cs, loc ) );
}
// here first sort the verticies
_internal->_pointIndexVector.clear();
for (unsigned int i = 0; i< 4;++i) _internal->_pointIndexVector.append(i);
// here first sort the verticies
for (unsigned i = 0; i < 4; ++i)
{
for (unsigned k = i + 1; k < 4; ++k)
{
if (rectPoints[k].y() < rectPoints[i].y())
{
std::swap(rectPoints[i], rectPoints[k]);
std::swap(_internal->_pointIndexVector[i],_internal->_pointIndexVector[k]);
}
}
}
if (rectPoints[0].x() > rectPoints[1].x())
{
std::swap(rectPoints[0], rectPoints[1]);
std::swap(_internal->_pointIndexVector[0],_internal->_pointIndexVector[1]);
}
if (rectPoints[3].x() > rectPoints[2].x())
{
std::swap(rectPoints[3], rectPoints[2]);
std::swap(_internal->_pointIndexVector[3],_internal->_pointIndexVector[2]);
}
// Verticies are now sorted in counter clockwise order with beginning
// with bottom left verticies as shown in following sketch
//
// 3 2
// X------------------------------------X
// | |
// | |
// | |
// X------------------------------------X
// 0 1
//
// now check input verticies for beeing a rectangle
QPointF p1( (rectPoints[0] + rectPoints[3]) * 0.5); // construct 2 helpful points
QPointF p2( (rectPoints[1] + rectPoints[2]) * 0.5);
qreal cosA = p2.x()-p1.x();
qreal sinA = p2.y()-p1.y();
qreal len = sqrt(cosA*cosA+sinA*sinA);
auto epsilonThreshold = [this](qreal distance) -> qreal
{
Esri::ArcGISRuntime::Point p1 = Rubberband::_internal->_mapView->screenToLocation(0, 0);
Esri::ArcGISRuntime::Point p2 = Rubberband::_internal->_mapView->screenToLocation(0, 1);
qreal dx = p2.x() - p1.x();
qreal dy = p2.y() - p1.y();
return sqrt(dx*dx + dy * dy) / distance;
};
qreal eps = epsilonThreshold(len);
cosA /= len;
sinA /= len;
qreal cosP = rectPoints[1].x()-rectPoints[0].x();
qreal sinP = rectPoints[1].y()-rectPoints[0].y();
len = sqrt(cosP*cosP+sinP*sinP);
cosP /= len;
sinP /= len;
_rectangleIsValid = (epsilonEqual(cosA,cosP,eps) && epsilonEqual(sinA,sinP,eps));
// be careful _oVec is relative to scene coordinate system and the scene coordinate system is counter clockwise -- sinA must be negated
if (_internal->_constrainOrthogonal)
{
_internal->_oVec = QPointF(1,0);
if (_rectangleIsValid)
{
_rectangleIsValid = epsilonEqual( fabs(cosA-1), fabs(sinA), eps );
}
}
else _internal->_oVec = QPointF( cosA, -sinA );
_internal->updateFrame( Rubberband::_internal->_mapView, rectPoints );
disenableConstructionState();
constructHandles();
activateButtons();
update();
}
} /* RubberbandRectangle::rectangleVerticies */
void RubberbandRectangle::buildFromRectangleParamter(const QPointF & center, qreal width, qreal height, const QPointF & direction, CoordinateSystem cs)
{
auto rotate = [](const QPointF & p, qreal sinA, qreal cosA) -> QPointF
{
return QPointF(p.x()*cosA-p.y()*sinA,p.y()*cosA+p.x()*sinA);
};
qreal dl = sqrt(direction.x()*direction.x()+direction.y()*direction.y());
qreal sinA = direction.y()/dl;
qreal cosA = direction.x()/dl;
_internal->_constrainOrthogonal = false;
_internal->_oVec = QPointF( cosA, -sinA );
_internal->_pointIndexVector.clear();
QVector<QPointF> rectPoints(4);
width *= 0.5;
height *= 0.5;
QPointF centerMapCoordinate = projectCoordinateToMap( cs, center );
rectPoints[0] = centerMapCoordinate + rotate( QPointF(-width,-height), sinA, cosA );
rectPoints[1] = centerMapCoordinate + rotate( QPointF( width,-height), sinA, cosA );
rectPoints[2] = centerMapCoordinate + rotate( QPointF( width, height), sinA, cosA );
rectPoints[3] = centerMapCoordinate + rotate( QPointF(-width, height), sinA, cosA );
_internal->updateFrame( Rubberband::_internal->_mapView, rectPoints );
disenableConstructionState();
constructHandles();
activateButtons();
update();
} /* RubberbandRectangle::buildFromRectangleParamter */
void RubberbandRectangle::buildRectangleRelativeToBottomLeftCorner(const QPointF & bottomLeft, qreal width, qreal height, const QPointF & direction, CoordinateSystem cs)
{
auto rotate = [](const QPointF & p, qreal sinA, qreal cosA) -> QPointF
{
return QPointF(p.x()*cosA-p.y()*sinA,p.y()*cosA+p.x()*sinA);
};
qreal dl = sqrt(direction.x()*direction.x()+direction.y()*direction.y());
qreal sinA = direction.y()/dl;
qreal cosA = direction.x()/dl;
_internal->_constrainOrthogonal = false;
_internal->_oVec = QPointF( cosA, -sinA );
_internal->_pointIndexVector.clear();
QVector<QPointF> rectPoints(4);
QPointF bottomLeftMapCoordinate = projectCoordinateToMap( cs, bottomLeft );
rectPoints[0] = bottomLeftMapCoordinate;
rectPoints[1] = bottomLeftMapCoordinate + rotate( QPointF(width, 0), sinA, cosA );
rectPoints[2] = bottomLeftMapCoordinate + rotate( QPointF(width,height), sinA, cosA );
rectPoints[3] = bottomLeftMapCoordinate + rotate( QPointF( 0,height), sinA, cosA );
_internal->updateFrame( Rubberband::_internal->_mapView, rectPoints );
disenableConstructionState();
constructHandles();
activateButtons();
update();
} /* RubberbandRectangle::buildRectangleRelativeToBottomLeftCorner */
void RubberbandRectangle::constrainQuadratic(bool on)
{
_internal->_constrainQuadratic = on;
if (isEnabledModificationState())
{
constructHandles();
activateButtons();
update();
}
} /* RubberbandRectangle::constrainQuadratic */
bool RubberbandRectangle::constrainQuadratic() const { return _internal->_constrainQuadratic; }
void RubberbandRectangle::constrainOrthogonal(bool on)
{
_internal->_constrainOrthogonal = on;
if (_internal->_constrainOrthogonal)
{
_internal->_rotateHandle = nullptr;
_internal->_oVec = QPointF(1,0);
}
} /* RubberbandRectangle::constrainQuadratic */
bool RubberbandRectangle::constrainOrthogonal() const { return _internal->_constrainOrthogonal; }
bool RubberbandRectangle::multiSelectActionPress ( AbstractEventDispatcher * causer, int x, int y ) { return selectAction ( causer, x, y ); }
bool RubberbandRectangle::multiSelectActionRelease( AbstractEventDispatcher * causer, int x, int y ) { return setCoordinateAction( causer, x, y ); }
bool RubberbandRectangle::selectAction( AbstractEventDispatcher *, int x, int y )
{
if (rubberbandIsAlive())
{
bool eventHandled = false;
QPointF scenePos(pixel2ScenePosition(x,y));
_baseInternal->_lastClickPosition = scenePos;
if (isEnabledConstructionState())
{
_internal->_constructionStart = scenePos;
enableSelectedState();
eventHandled = true;
}
else
{
if (_internal->minimumSizeReached()) return true;
RubberbandHandle * handlePtr = pickHandle(x,y);
if (handlePtr)
{
handlePtr->setSelection();
enableSelectedState();
eventHandled = true;
}
else
{
for (auto lh : _internal->_lineHandle)
{
if ((lh.valid()) && (lh->isPositionInside(scenePos)))
{
lh->setSelection(true);
enableSelectedState();
eventHandled = true;
}
}
if ((_internal->_rotateHandle.valid()) && (_internal->_rotateHandle->isPositionInside(scenePos)))
{
_internal->_rotateHandle->setSelection(true);
enableSelectedState();
eventHandled = true;
}
}
if (!isEnabledSelectedState())
{
// ok doesn't hit any point or line handle now looking to frame handle
if (_internal->_frameHandle.valid())
{
if ((_internal->_frameHandle->isSelectable()) && (_internal->_frameHandle->isPositionInside(scenePos)))
{
_internal->_frameHandle->setSelection(true);
enableSelectedState();
eventHandled = true;
}
}
}
}
if (eventHandled)
{
update();
deactivateButtons();
}
return eventHandled;
}
return false;
} /* RubberbandRectangle::selectAction */
bool RubberbandRectangle::moveAction( AbstractEventDispatcher*, int x, int y )
{
adjustConnectedPreviewPixmap(x,y);
if (rubberbandIsAlive())
{
if (!isEnabledSelectedState())
{
deSelectHandles();
if (_internal->minimumSizeReached()) return true;
if ((_internal->_pointHandle[0] == nullptr) && (_internal->_lineHandle[0] == nullptr) && (_internal->_rotateHandle == nullptr) && (_internal->_frameHandle == nullptr))
{
// constructHandles has not been called jet. Leave function and eat event
return false;
}
if (_internal->_rotateHandle.valid())
{
_internal->_rotateHandle->setSelection(false);
}
RubberbandHandle * handlePtr = pickHandle(x,y);
if (handlePtr)
{
handlePtr->setSelection();
for (auto lh : _internal->_lineHandle)
lh->setSelection( false );
}
else
{
QPointF scenePos(pixel2ScenePosition(x,y));
bool selectRotationHandle = _internal->_rotateHandle.valid() ? _internal->_rotateHandle->isPositionInside(scenePos) : false;
if (!selectRotationHandle)
{
for (auto lh : _internal->_lineHandle)
{
if (lh != nullptr)
{
bool selectHandle = lh->isPositionInside(scenePos);
lh->setSelection( selectHandle );
}
}
}
else _internal->_rotateHandle->setSelection(true);
}
}
}
return false;
} /* RubberbandRectangle::moveAction */
bool RubberbandRectangle::checkMinMaxConstrains(const QPointF & vertexScene, QPointF & deltaMovePtr )
{
bool ok_x = true;
bool ok_y = true;
// check min max constrains
RubberbandHandle::RubberbandCoordinate vertex( Rubberband::_internal->_mapView, vertexScene );
qreal mapRectWidth = 2.0 * fabs(vertex._mapCoordinate.x() - _internal->_center.x());
qreal mapRectHeight = 2.0 * fabs(vertex._mapCoordinate.y() - _internal->_center.y());
if ((_internal->_minWidth > 0) && (mapRectWidth < _internal->_minWidth )) { ok_x = false; deltaMovePtr.setX(0); }
if ((_internal->_maxWidth > 0) && (mapRectWidth > _internal->_maxWidth )) { ok_x = false; deltaMovePtr.setX(0); }
if ((_internal->_minHeight > 0) && (mapRectHeight < _internal->_minHeight)) { ok_y = false; deltaMovePtr.setY(0); }
if ((_internal->_maxHeight > 0) && (mapRectHeight > _internal->_maxHeight)) { ok_y = false; deltaMovePtr.setY(0); }
return (ok_x || ok_y);
} /* RubberbandRectangle::checkMinMaxConstrains */
bool RubberbandRectangle::checkMinMaxConstrains(const QPointF & vertexScene, bool isVertical)
{
bool ok = true;
if (_internal->_constrainQuadratic)
{
_internal->verifyConstrains();
/*
if (_internal->_minHeight > 0) _internal->_minWidth = _internal->_minHeight;
else if (_internal->_minWidth > 0) _internal->_minHeight = _internal->_minWidth;
if (_internal->_maxHeight > 0) _internal->_maxWidth = _internal->_maxHeight;
else if (_internal->_maxWidth > 0) _internal->_maxHeight = _internal->_maxWidth;
*/
}
// check min max constrains
RubberbandHandle::RubberbandCoordinate vertex( Rubberband::_internal->_mapView, vertexScene );
if (isVertical)
{
qreal mapRectWidth = 2.0 * fabs(vertex._mapCoordinate.x() - _internal->_center.x());
if ((_internal->_minWidth > 0) && (mapRectWidth < _internal->_minWidth )) { ok = false; }
if ((_internal->_maxWidth > 0) && (mapRectWidth > _internal->_maxWidth )) { ok = false; }
return ok;
}
else
{
qreal mapRectHeight = 2.0 * fabs(vertex._mapCoordinate.y() - _internal->_center.y());
if ((_internal->_minHeight > 0) && (mapRectHeight < _internal->_minHeight)) { ok = false; }
if ((_internal->_maxHeight > 0) && (mapRectHeight > _internal->_maxHeight)) { ok = false; }
return ok;
}
} /* RubberbandRectangle::checkMinMaxConstrains */
bool RubberbandRectangle::constrainsSatisfied( RubberbandPointHandle * handlePointPtr, unsigned int handleIdx, QPointF & deltaMove, bool & constrainsComplied )
{
constrainsComplied = true;
qreal sinMap = _internal->_mapOVec.y(); // calculate rotation
qreal cosMap = _internal->_mapOVec.x();
qreal sinRec = _internal->_oVec.y();
qreal cosRec = _internal->_oVec.x();
qreal sinPhi = sinRec * cosMap - cosRec * sinMap; // Addition-Theorem
qreal cosPhi = cosRec * cosMap + sinRec * sinMap;
QPointF rotatedDeltaMove = MathUtils::rotate( deltaMove, QPointF( 0.0, 0.0 ), -sinPhi, cosPhi ); // rotate to orthogonal position relative to rectangle
if (_internal->_constrainQuadratic)
{
qreal d = floor(sqrt(0.5*(rotatedDeltaMove.x()*rotatedDeltaMove.x() + rotatedDeltaMove.y()*rotatedDeltaMove.y())));
switch (handleIdx)
{
case 0 : rotatedDeltaMove = (rotatedDeltaMove.x() < 0) ? QPointF(-d, d) : QPointF( d,-d); break;
case 1 : rotatedDeltaMove = (rotatedDeltaMove.x() > 0) ? QPointF( d, d) : QPointF(-d,-d); break;
case 2 : rotatedDeltaMove = (rotatedDeltaMove.x() > 0) ? QPointF( d,-d) : QPointF(-d, d); break;
case 3 : rotatedDeltaMove = (rotatedDeltaMove.x() < 0) ? QPointF(-d,-d) : QPointF( d, d); break;
default: break;
}
}
int dx = int((fabs(rotatedDeltaMove.x()) > 0) ? rotatedDeltaMove.x()/fabs(rotatedDeltaMove.x()) : 0 );
int dy = int((fabs(rotatedDeltaMove.y()) > 0) ? rotatedDeltaMove.y()/fabs(rotatedDeltaMove.y()) : 0 );
QPointF moveStepByStep(0,0);
QPointF resultDeltaMove(0,0);
while (constrainsComplied)
{
moveStepByStep.setX( moveStepByStep.x() + dx );
moveStepByStep.setY( moveStepByStep.y() + dy );
int endCondition = 2;
if (fabs(moveStepByStep.x()) >= fabs(rotatedDeltaMove.x())) { moveStepByStep.setX(rotatedDeltaMove.x()); --endCondition; }
if (fabs(moveStepByStep.y()) >= fabs(rotatedDeltaMove.y())) { moveStepByStep.setY(rotatedDeltaMove.y()); --endCondition; }
QPointF loc = MathUtils::rotate( handlePointPtr-> getCoordinate()._sceneCoordinate, _internal->_centerScene, -sinPhi, cosPhi ); // rotate back to orthogonal position
loc += moveStepByStep;
constrainsComplied = _internal->minimumSizeReached( handleIdx, -1, loc );
if (constrainsComplied)
{
QPointF tmpDeltaMove(moveStepByStep);
constrainsComplied = checkMinMaxConstrains( loc, tmpDeltaMove );
if (constrainsComplied)
{
if (fabs(tmpDeltaMove.x()) > fabs(resultDeltaMove.x())) resultDeltaMove.setX( tmpDeltaMove.x() );
if (fabs(tmpDeltaMove.y()) > fabs(resultDeltaMove.y())) resultDeltaMove.setY( tmpDeltaMove.y() );
}
}
if (0 == endCondition) break;
} // while
deltaMove = MathUtils::rotate( resultDeltaMove, QPointF( 0.0, 0.0 ), sinPhi, cosPhi );
DEBUG_LOG(_internal->_logger) << LOGFUNCTION << " " << handleIdx << " deltaMove " << deltaMove.x() << " ; " << deltaMove.y() << " centerScene "
<< _internal->_centerScene.x() << " ; " << _internal->_centerScene.y() << " --> " << constrainsComplied;
return (constrainsComplied || !deltaMove.isNull());
} /* RubberbandRectangle::constrainsSatisfied */
void RubberbandRectangle::adjustNeighbouringPoints( RubberbandPointHandle * handlePointPtr, unsigned int handleIdx, QPointF deltaMove )
{
// adjust neighbouring point handles
unsigned int beforeIdx = (handleIdx > 0) ? handleIdx-1 : 3;
unsigned int nextIdx = (handleIdx < 3) ? handleIdx+1 : 0;
RubberbandPointHandle * beforePoint = dynamic_cast<RubberbandPointHandle*>(Rubberband::_internal->_handleList[beforeIdx].get());
RubberbandPointHandle * nextPoint = dynamic_cast<RubberbandPointHandle*>(Rubberband::_internal->_handleList[nextIdx ].get());
qreal sinMap = _internal->_mapOVec.y();
qreal cosMap = _internal->_mapOVec.x();
qreal sinRec = _internal->_oVec.y();
qreal cosRec = _internal->_oVec.x();
qreal sinPhi = sinRec * cosMap - cosRec * sinMap; // Addition-Theorem
qreal cosPhi = cosRec * cosMap + sinRec * sinMap;
QPointF loc = handlePointPtr -> getCoordinate()._sceneCoordinate;
QPointF beforeLoc = beforePoint -> getCoordinate()._sceneCoordinate;
QPointF nextLoc = nextPoint -> getCoordinate()._sceneCoordinate;
loc = MathUtils::rotate( loc , _internal->_center, -sinPhi, cosPhi );
beforeLoc = MathUtils::rotate( beforeLoc, _internal->_center, -sinPhi, cosPhi );
nextLoc = MathUtils::rotate( nextLoc , _internal->_center, -sinPhi, cosPhi );
deltaMove = MathUtils::rotate( deltaMove, QPointF(0.0, 0.0) , -sinPhi, cosPhi );
bool beforeIsHorizontal( fabs(loc.x() - beforeLoc.x()) > fabs(loc.y() - beforeLoc.y()) );
bool nextIsHorizontal ( fabs(loc.x() - nextLoc.x()) > fabs(loc.y() - nextLoc.y()) );
#ifdef NEVER
qDebug() << "RubberbandRectangle::adjustNeibouringPoints " << deltaMove.x() << " , " << deltaMove.y();
qDebug() << " before is horizontal " << beforeIsHorizontal;
qDebug() << " next is horizontal " << nextIsHorizontal;
qDebug() << " point " << loc;
qDebug() << " point before " << beforeLoc;
qDebug() << " point next " << nextLoc;
#endif
if (beforeIsHorizontal) beforeLoc.setY( beforeLoc.y() + deltaMove.y() );
else beforeLoc.setX( beforeLoc.x() + deltaMove.x() );
if (nextIsHorizontal) nextLoc.setY( nextLoc.y() + deltaMove.y() );
else nextLoc.setX( nextLoc.x() + deltaMove.x() );
#ifdef NEVER
qDebug() << " point before " << beforeLoc;
qDebug() << " point next " << nextLoc;
#endif
// rotate back
beforeLoc = MathUtils::rotate( beforeLoc, _internal->_center, sinPhi, cosPhi );
nextLoc = MathUtils::rotate( nextLoc , _internal->_center, sinPhi, cosPhi );
// store coordinates
beforePoint -> updateCoordinate(beforeLoc);
nextPoint -> updateCoordinate(nextLoc );
} /* RubberbandRectangle::adjustNeighbouringPoints */
bool RubberbandRectangle::adjustLine( QPointF & deltaMove, bool & constrainsComplied )
{
constrainsComplied = true;
for (unsigned i = 0; i < 4; ++i)
{
if (_internal->_lineHandle[i]->isSelected())
{
int idx0 = i;
int idx1 = (i+1) % 4;
QPointF loc0 = dynamic_cast<RubberbandPointHandle*>(Rubberband::_internal->_handleList[idx0].get())->getCoordinate()._sceneCoordinate;
QPointF loc1 = dynamic_cast<RubberbandPointHandle*>(Rubberband::_internal->_handleList[idx1].get())->getCoordinate()._sceneCoordinate;
qreal sinMap = _internal->_mapOVec.y();
qreal cosMap = _internal->_mapOVec.x();
qreal sinRec = _internal->_oVec.y();
qreal cosRec = _internal->_oVec.x();
qreal sinPhi = sinRec * cosMap - cosRec * sinMap; // Addition-Theorem
qreal cosPhi = cosRec * cosMap + sinRec * sinMap;
loc0 = MathUtils::rotate( loc0 , _internal->_centerScene, -sinPhi, cosPhi );
loc1 = MathUtils::rotate( loc1 , _internal->_centerScene, -sinPhi, cosPhi );
deltaMove = MathUtils::rotate( deltaMove, QPointF( 0.0, 0.0 ) , -sinPhi, cosPhi );
bool isVertical((i%2) == 1);
int dx = int( ( isVertical && (fabs(deltaMove.x()) > 0)) ? deltaMove.x()/fabs(deltaMove.x()) : 0 );
int dy = int( (!isVertical && (fabs(deltaMove.y()) > 0)) ? deltaMove.y()/fabs(deltaMove.y()) : 0 );
bool doStepByStep = ((0 != dx) || (0 != dy));
QPointF moveStepByStep(0,0);
QPointF resultDeltaMove(0,0);
while (doStepByStep)
{
QPointF stepLoc0( loc0 );
QPointF stepLoc1( loc1 );
if (0 != dx)
{
moveStepByStep.setX( moveStepByStep.x() + dx );
if (fabs(moveStepByStep.x()) >= fabs(deltaMove.x())) { moveStepByStep.setX(deltaMove.x()); doStepByStep = false; }
stepLoc0.setX( stepLoc0.x() + moveStepByStep.x() ); stepLoc1.setX( stepLoc1.x() + moveStepByStep.x() );
}
if (0 != dy)
{
moveStepByStep.setY( moveStepByStep.y() + dy );
if (fabs(moveStepByStep.y()) >= fabs(deltaMove.y())) { moveStepByStep.setY(deltaMove.y()); doStepByStep = false; }
stepLoc0.setY( stepLoc0.y() + moveStepByStep.y() ); stepLoc1.setY( stepLoc1.y() + moveStepByStep.y() );
}
constrainsComplied = _internal->minimumSizeReached(-1,i,stepLoc0);
if (!constrainsComplied) continue;
constrainsComplied = checkMinMaxConstrains( stepLoc0, isVertical );
if (!constrainsComplied) continue;
resultDeltaMove = moveStepByStep;
if (_internal->_constrainQuadratic)
{
qreal h = stepLoc0.y() - _internal->_centerScene.y();
qreal w = stepLoc0.x() - _internal->_centerScene.x();
qreal d = fabs( isVertical ? w : h );
stepLoc0 = MathUtils::rotate( QPointF(_internal->_centerScene.x() - d, _internal->_centerScene.y() + d ), _internal->_centerScene , sinPhi, cosPhi );
dynamic_cast<RubberbandPointHandle*>(Rubberband::_internal->_handleList[0].get())->updateCoordinate( stepLoc0 );
stepLoc0 = MathUtils::rotate( QPointF(_internal->_centerScene.x() + d, _internal->_centerScene.y() + d ), _internal->_centerScene , sinPhi, cosPhi );
dynamic_cast<RubberbandPointHandle*>(Rubberband::_internal->_handleList[1].get())->updateCoordinate( stepLoc0 );
stepLoc0 = MathUtils::rotate( QPointF(_internal->_centerScene.x() + d, _internal->_centerScene.y() - d ), _internal->_centerScene , sinPhi, cosPhi );
dynamic_cast<RubberbandPointHandle*>(Rubberband::_internal->_handleList[2].get())->updateCoordinate( stepLoc0 );
stepLoc0 = MathUtils::rotate( QPointF(_internal->_centerScene.x() - d, _internal->_centerScene.y() - d ), _internal->_centerScene , sinPhi, cosPhi );
dynamic_cast<RubberbandPointHandle*>(Rubberband::_internal->_handleList[3].get())->updateCoordinate( stepLoc0 );
}
else
{
stepLoc0 = MathUtils::rotate( stepLoc0, _internal->_centerScene , sinPhi, cosPhi );
stepLoc1 = MathUtils::rotate( stepLoc1, _internal->_centerScene , sinPhi, cosPhi );
// store coordinates
dynamic_cast<RubberbandPointHandle*>(Rubberband::_internal->_handleList[idx0].get())->updateCoordinate(stepLoc0);
dynamic_cast<RubberbandPointHandle*>(Rubberband::_internal->_handleList[idx1].get())->updateCoordinate(stepLoc1);
}
} // while (doStepByStep)
deltaMove = MathUtils::rotate( resultDeltaMove, QPointF( 0.0, 0.0 ), sinPhi, cosPhi );
return (constrainsComplied || !deltaMove.isNull());
} // if handle is selected
} // for handle-index
return true;
} /* RubberbandRectangle::adjustLine */
void RubberbandRectangle::updateHandles(const QPointF & scenePos, QPointF deltaMove)
{
bool constrainsComplied = true;
if ((_internal->_rotateHandle.valid()) && _internal->_rotateHandle->isSelected())
{
qreal sinA = scenePos.x() - _internal->_centerScene.x();
qreal cosA = - scenePos.y() + _internal->_centerScene.y();
qreal len = sqrt(cosA*cosA+sinA*sinA);
cosA /= len;
sinA /= len;
for (auto handle : qAsConst(Rubberband::_internal->_handleList))
{
RubberbandPointHandle * handlePointPtr = dynamic_cast<RubberbandPointHandle*>(handle.get());
if (handlePointPtr == nullptr) continue;
QPointF p = MathUtils::rotate( handlePointPtr->getCoordinate()._sceneCoordinate, _internal->_centerScene, -_internal->_oVec.y(), _internal->_oVec.x() );
p = MathUtils::rotate( p, _internal->_centerScene, sinA, cosA );
handlePointPtr->updateCoordinate( p );
p.setX( handlePointPtr->getCoordinate()._mapCoordinate.x() );
p.setY( handlePointPtr->getCoordinate()._mapCoordinate.y() );
}
qreal dSin = sinA*_internal->_oVec.x() - cosA*_internal->_oVec.y(); // addition theorem
qreal dCos = cosA*_internal->_oVec.x() + sinA*_internal->_oVec.y();
_lastCoordinateChange.setToIdentity(); // clear matrix
_lastCoordinateChange.putTranslation( -_internal->_center ); // move center to (0,0)
_lastCoordinateChange.putRotate( QPointF(dCos,dSin) ); // do rotation
_lastCoordinateChange.putTranslation( _internal->_center ); // move center back
_internal->_oVec.setX(cosA);
_internal->_oVec.setY(sinA);
_baseInternal->_lastClickPosition = scenePos;
}
else
{
bool frameIsSelected( ((_internal->_frameHandle.valid()) && (_internal->_frameHandle->isSelected())) );
bool pointsAdjusted( false );
unsigned int handleIdx = 0;
for (auto handle : qAsConst(Rubberband::_internal->_handleList))
{
RubberbandPointHandle * handlePointPtr = dynamic_cast<RubberbandPointHandle*>(handle.get());
if ((handlePointPtr) && ((handlePointPtr->isSelected()) || (frameIsSelected)))
{
pointsAdjusted = true;
if (!frameIsSelected)
{
if (constrainsSatisfied( handlePointPtr, handleIdx, deltaMove, constrainsComplied ))
{
adjustNeighbouringPoints( handlePointPtr, handleIdx, deltaMove );
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Move selected handle after moving the neighbouring points because
* for larger deltaMove the decision which values have to be modified
* will fail if the handle has already moved to new position.
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
handlePointPtr->moveCoordinate( deltaMove );
if (_internal->_constrainQuadratic) _baseInternal->_lastClickPosition = scenePos;
else _baseInternal->_lastClickPosition += deltaMove;
break; // only one handle can be selected at time
}
}
else handlePointPtr->moveCoordinate( deltaMove ); // frameIsSelected == true
}
++handleIdx;
}
if (frameIsSelected)
{
_internal->_centerScene += deltaMove;
_baseInternal->_lastClickPosition = scenePos;
if (Rubberband::_internal->_handleList.isEmpty())
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Rubberband has only frame handle, we have to do some calculations to set the new center point
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
QPointF lastCenter = _internal->_center;
RubberbandHandle::RubberbandCoordinate movedCenter(Rubberband::_internal->_mapView, _internal->_centerScene );
_internal->_center.setX( movedCenter._mapCoordinate.x() );
_internal->_center.setY( movedCenter._mapCoordinate.y() );
_lastCoordinateChange.setToIdentity(); // clear matrix
_lastCoordinateChange.putTranslation( _internal->_center - lastCenter ); // set translation
}
}
else if ((!pointsAdjusted) && (adjustLine(deltaMove,constrainsComplied))) _baseInternal->_lastClickPosition += deltaMove;
}
markFailedConstrains( scenePos, !constrainsComplied );
update();
} /* RubberbandRectangle::updateHandles */
void RubberbandRectangle::buildTmpConstructionPath( const QPointF & start
, const QPointF & end
, QVector<QPointF> & path
, QPointF & sceneCenter
, qreal & sceneWidth
, qreal & sceneHeight )
{
_internal->setScreenCenter( Rubberband::_internal->_mapView );
auto length = [ ]( qreal x, qreal y ) -> qreal { return sqrt(x*x+y*y); };
auto rotate = [this]( qreal x, qreal y, bool back ) -> QPointF
{
qreal sinPhi = (back) ? -_internal->_mapOVec.y() : _internal->_mapOVec.y();
qreal cosPhi = _internal->_mapOVec.x();
qreal px = x*cosPhi + y*sinPhi;
qreal py = y*cosPhi - x*sinPhi;
return QPointF(px,py);
}; // lambda rotate
RubberbandHandle::RubberbandCoordinate mapPointStart( Rubberband::_internal->_mapView, start );
const Esri::ArcGISRuntime::Point & mapStart = mapPointStart._mapCoordinate;
const Esri::ArcGISRuntime::Point mapMaxW( mapStart.x() + _internal->_maxWidth
, mapStart.y()
, Rubberband::_internal->_mapView->map()->spatialReference() );
const Esri::ArcGISRuntime::Point mapMinW( mapStart.x() + _internal->_minWidth
, mapStart.y()
, Rubberband::_internal->_mapView->map()->spatialReference() );
const Esri::ArcGISRuntime::Point mapMaxH( mapStart.x()
, mapStart.y() + _internal->_maxHeight
, Rubberband::_internal->_mapView->map()->spatialReference() );
const Esri::ArcGISRuntime::Point mapMinH( mapStart.x()
, mapStart.y() + _internal->_minHeight
, Rubberband::_internal->_mapView->map()->spatialReference() );
RubberbandHandle::RubberbandCoordinate mapPointMaxW( Rubberband::_internal->_mapView, mapMaxW );
RubberbandHandle::RubberbandCoordinate mapPointMinW( Rubberband::_internal->_mapView, mapMinW );
RubberbandHandle::RubberbandCoordinate mapPointMaxH( Rubberband::_internal->_mapView, mapMaxH );
RubberbandHandle::RubberbandCoordinate mapPointMinH( Rubberband::_internal->_mapView, mapMinH );
qreal maxSceneWidth = length( mapPointMaxW._sceneCoordinate.x() - mapPointStart._sceneCoordinate.x(), mapPointMaxW._sceneCoordinate.y() - mapPointStart._sceneCoordinate.y() );
qreal minSceneWidth = length( mapPointMinW._sceneCoordinate.x() - mapPointStart._sceneCoordinate.x(), mapPointMinW._sceneCoordinate.y() - mapPointStart._sceneCoordinate.y() );
qreal maxSceneHeight = length( mapPointMaxH._sceneCoordinate.x() - mapPointStart._sceneCoordinate.x(), mapPointMaxH._sceneCoordinate.y() - mapPointStart._sceneCoordinate.y() );
qreal minSceneHeight = length( mapPointMinH._sceneCoordinate.x() - mapPointStart._sceneCoordinate.x(), mapPointMinH._sceneCoordinate.y() - mapPointStart._sceneCoordinate.y() );
const QPointF & pe = rotate( end.x() - start.x(), end.y() - start.y(), true );
qreal w = pe.x();
qreal h = pe.y();
if ((_internal->_minWidth > 0) && (minSceneWidth > fabs(w))) w = (w>=0) ? minSceneWidth : -minSceneWidth;
if ((_internal->_maxWidth > 0) && (maxSceneWidth < fabs(w))) w = (w>=0) ? maxSceneWidth : -maxSceneWidth;
if ((_internal->_minHeight > 0) && (minSceneHeight > fabs(h))) h = (h>=0) ? minSceneHeight : -minSceneHeight;
if ((_internal->_maxHeight > 0) && (maxSceneHeight < fabs(h))) h = (h>=0) ? maxSceneHeight : -maxSceneHeight;
if (_internal->_constrainQuadratic)
{
if (fabs(w) > fabs(h)) h = (h >= 0) ? fabs(w) : -fabs(w);
else if (fabs(w) < fabs(h)) w = (w >= 0) ? fabs(h) : -fabs(h);
}
path << start;
path << start + rotate( w, 0, false );
path << start + rotate( w, h, false );
path << start + rotate( 0, h, false );
sceneWidth = fabs(w);
sceneHeight = fabs(h);
sceneCenter = start + rotate(w*0.5,h*0.5,false);
} /* RubberbandRectangle::buildTmpConstructionPath */
void RubberbandRectangle::buildTmpConstruction( SituationMap::RefPointer<RubberbandDrawable> & tmpGraphic, QPointF & start, QPointF & end )
{
if (!tmpGraphic.valid())
{
tmpGraphic = new DrawablePainterPath( Rubberband::_internal->_mapView );
tmpGraphic -> setDrawColor( _drawColor );
tmpGraphic -> setFillColor( _fillColor );
tmpGraphic -> setLineWidth( 1 );
tmpGraphic -> zBufferPlane( RubberbandDrawable::ZBufferPlane::Drawables );
}
QVector<QPointF> path;
QPointF sceneCenter;
qreal sceneWidth;
qreal sceneHeight;
buildTmpConstructionPath( start, end, path, sceneCenter, sceneWidth, sceneHeight );
DrawablePainterPath * tmpPath = static_cast<DrawablePainterPath*>(tmpGraphic.get());
tmpPath -> setPath( path );
tmpPath -> enableClosedLine(true);
} /* RubberbandRectangle::buildTmpConstruction */
void RubberbandRectangle::getTmpConstructionParameter( const QPointF & start, const QPointF & end, QPointF & sceneCenter, qreal & sceneWidth, qreal & sceneHeight )
{
QVector<QPointF> path;
buildTmpConstructionPath( start, end, path, sceneCenter, sceneWidth, sceneHeight );
} /* RubberbandRectangle::getTmpConstructionParameter */
bool RubberbandRectangle::setCoordinateAction( AbstractEventDispatcher * causer, int x, int y )
{
markFailedConstrains( QPointF(), false );
if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene))
{
QPointF scenePos(pixel2ScenePosition(x,y));
Rubberband::_internal->_lastClickPosition = scenePos;
if (isEnabledConstructionState())
{
if (_internal->_tmpConstruction.valid())
{
qreal sceneWidth;
qreal sceneHeight;
getTmpConstructionParameter( _internal->_constructionStart, scenePos, _internal->_centerScene, sceneWidth, sceneHeight );
_internal->setInitialRectSize( this, sceneWidth, sceneHeight );
_internal->_tmpConstruction = nullptr;
if (_finishAfterConstruction)
{
disenableConstructionState();
constructHandles();
update();
applyAndEndAction(causer,x,y);
return true;
}
}
else
{
_internal->_centerScene = scenePos;
_internal->setInitialRectSize( this, scenePos, x, y );
// now set values in map coordinates
}
_internal->setRotatedFrame( Rubberband::_internal->_mapView );
disenableSelectedState();
disenableConstructionState();
constructHandles();
update();
activateButtons();
return true;
}
else // isEnabledConstructionState()
{
if (isEnabledSelectedState())
{
updateHandles( scenePos, scenePos-_baseInternal->_lastClickPosition );
markFailedConstrains( QPointF(), false );
for (auto lh : _internal->_lineHandle)
{
if (lh.valid()) lh->setSelection(false);
}
if (_internal->_rotateHandle.valid()) _internal->_rotateHandle->setSelection(false);
if (_internal->_frameHandle.valid()) _internal->_frameHandle->setSelection(false);
disenableSelectedState();
disenableMultiSelectedState();
updateButtonPosition();
update();
activateButtons();
return true;
}
else if (_baseInternal->buttonProxyActive( scenePos )) return false;
}
updateButtonPosition();
if (isEnabledModificationState()) activateButtons();
}
return false;
} /* RubberbandRectangle::setCoordinateAction */
bool RubberbandRectangle::movePickedAction( AbstractEventDispatcher*, int x, int y )
{
if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene))
{
if (isEnabledConstructionState())
{
QPointF scenePos(pixel2ScenePosition(x,y));
if ((_internal->_tmpConstruction.valid()) || ((_internal->_constructionStart - scenePos).manhattanLength() > 5))
{
buildTmpConstruction( _internal->_tmpConstruction, _internal->_constructionStart, scenePos );
}
return true;
}
else
{
if (isEnabledSelectedState())
{
QPointF scenePos(pixel2ScenePosition(x,y));
updateHandles( scenePos, scenePos-_baseInternal->_lastClickPosition );
return true;
}
}
}
return false;
} /* RubberbandRectangle::movePickedAction */
bool RubberbandRectangle::contextMenuAction( AbstractEventDispatcher * , int , int )
{
return rubberbandIsAlive();
} /* RubberbandRectangle::contextMenuAction */
bool RubberbandRectangle::completeStateAction( AbstractEventDispatcher * causer, int x, int y )
{
if (isEnabledConstructionState())
{
// Position of rectangle is still unknown, there are no data to apply
return abortAction(causer,x,y);
}
return applyAndEndAction(causer,x,y);
} /* RubberbandRectangle::completeStateAction */
bool RubberbandRectangle::undoAction( AbstractEventDispatcher * , int , int )
{
return rubberbandIsAlive();
} /* RubberbandRectangle::undoAction */
bool RubberbandRectangle::deleteAction(AbstractEventDispatcher * , int , int )
{
return rubberbandIsAlive();
} /* RubberbandRectangle::deleteAction */
void RubberbandRectangle::update()
{
Rubberband::update();
QVector<RubberbandHandle::RubberbandCoordinate> coordinateList;
coordinateList.reserve(4);
if (Rubberband::_internal->_handleList.count() == 4)
{
for (unsigned int i = 0; i < 4; ++i)
{
auto handlePoint = Rubberband::_internal->_handleList[i];
RubberbandPointHandle * handlePointPtr = dynamic_cast<RubberbandPointHandle*>(handlePoint.get());
if (handlePointPtr)
{
const RubberbandHandle::RubberbandCoordinate & pos = handlePointPtr->getCoordinate();
_internal->_lineHandle[i] -> updateCoordinate( 0, pos._sceneCoordinate );
_internal->_lineHandle[i] -> setDrawColor(_handleDrawColor);
_internal->_lineHandle[i] -> setFillColor(_handleFillColor);
_internal->_lineHandle[i] -> setSelectionColor(_handleSelectionColor);
_internal->_lineHandle[i] -> setPickingRatio( _pickingRatio );
if (i == 0) _internal->_lineHandle[ 3 ] -> updateCoordinate( 1, pos._sceneCoordinate );
else _internal->_lineHandle[i-1] -> updateCoordinate( 1, pos._sceneCoordinate );
coordinateList.append( handlePointPtr->getCoordinate() );
}
} /* for handlePoint */
}
if (_internal->_frameHandle.valid())
{
if (coordinateList.isEmpty())
{
const QPointF & center = _internal->_center;
qreal w = _internal->_rectWidth*0.5;
qreal h = _internal->_rectHeight*0.5;
qreal sinPhi = -_internal->_oVec.y();
qreal cosPhi = _internal->_oVec.x();
QPointF p;
p = _internal->rotateVertex( center, -w, -h, sinPhi, cosPhi );
coordinateList += RubberbandHandle::RubberbandCoordinate( Rubberband::_internal->_mapView, convert(p,Rubberband::_internal->_mapView->map()->spatialReference()) );
p = _internal->rotateVertex( center, w, -h, sinPhi, cosPhi );
coordinateList += RubberbandHandle::RubberbandCoordinate( Rubberband::_internal->_mapView, convert(p,Rubberband::_internal->_mapView->map()->spatialReference()) );
p = _internal->rotateVertex( center, w, h, sinPhi, cosPhi );
coordinateList += RubberbandHandle::RubberbandCoordinate( Rubberband::_internal->_mapView, convert(p,Rubberband::_internal->_mapView->map()->spatialReference()) );
p = _internal->rotateVertex( center, -w, h, sinPhi, cosPhi );
coordinateList += RubberbandHandle::RubberbandCoordinate( Rubberband::_internal->_mapView, convert(p,Rubberband::_internal->_mapView->map()->spatialReference()) );
}
_internal->_frameHandle -> setDrawColor(_frameColor);
_internal->_frameHandle -> setFillColor(_frameFillColor);
_internal->_frameHandle -> setSelectionColor(_frameSelectionColor);
_internal->_frameHandle -> updateFrameSceneMargin( _frameHandleMargin );
_internal->_frameHandle -> updateFrameShape( coordinateList, _internal->_oVec.y(), _internal->_oVec.x() );
_internal->_frameHandle -> drawDisabled( (_frameHandleMargin < 0) );
}
_internal -> updateFrame( Rubberband::_internal->_mapView, coordinateList );
if (_internal->_rotateHandle.valid())
{
_internal -> _rotateHandle -> setSceneOffset( _internal->getSceneOffsetRotationHandle( (_frameHandleMargin > 0) ? _frameHandleMargin : 0 ) );
_internal -> _rotateHandle -> updateCoordinate( _internal->getScenePositionRotationHandle() );
}
} /* RubberbandRectangle::update */
} } /* namespace SituationMap::RB */