1914 lines
81 KiB
C++
Executable File
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 ¢erPoint, 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 */
|