/** ** ** @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 #include #include #include #include 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 _tmpConstruction; SituationMap::RefPointer _frameHandle; SituationMap::RefPointer _pointHandle[4]; SituationMap::RefPointer _lineHandle[4]; SituationMap::RefPointer _rotateHandle; QVector _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 & points ); void updateFrame( Esri::ArcGISRuntime::MapGraphicsView * mapView, const QVector & 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 & 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::infinity(); qreal xmax = - std::numeric_limits::infinity(); qreal ymin = std::numeric_limits::infinity(); qreal ymax = - std::numeric_limits::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 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::infinity(); xmax = - std::numeric_limits::infinity(); ymin = std::numeric_limits::infinity(); ymax = - std::numeric_limits::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 & 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::infinity(); qreal xmax = - std::numeric_limits::infinity(); qreal ymin = std::numeric_limits::infinity(); qreal ymax = - std::numeric_limits::infinity(); QVector 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::infinity(); xmax = -std::numeric_limits::infinity(); ymin = std::numeric_limits::infinity(); ymax = -std::numeric_limits::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(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(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 RubberbandRectangle::coordinateVector( CoordinateSystem cs ) const { return rectangleVerticies(cs); } QPointF RubberbandRectangle::singleCoordinate(unsigned int, CoordinateSystem cs ) const { return centerPoint(cs); } void RubberbandRectangle::setCoordinateVector( const QVector & 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 RubberbandRectangle::rectangleVerticies( CoordinateSystem cs ) const { QVector 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 & coordinateVector, CoordinateSystem cs ) { if (coordinateVector.size() == 4) { QVector 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 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 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(Rubberband::_internal->_handleList[beforeIdx].get()); RubberbandPointHandle * nextPoint = dynamic_cast(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(Rubberband::_internal->_handleList[idx0].get())->getCoordinate()._sceneCoordinate; QPointF loc1 = dynamic_cast(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(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(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(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(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(Rubberband::_internal->_handleList[idx0].get())->updateCoordinate(stepLoc0); dynamic_cast(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(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(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 & 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 & 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 path; QPointF sceneCenter; qreal sceneWidth; qreal sceneHeight; buildTmpConstructionPath( start, end, path, sceneCenter, sceneWidth, sceneHeight ); DrawablePainterPath * tmpPath = static_cast(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 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 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(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 */