/** ** ** @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_ellipse.h" #include "drawable_ellipse.h" #include "rubberband_point_handle.h" #include "rubberband_internal.h" #include "rubberband_base_internal.h" #include #include #include namespace SituationMap { namespace RB { struct RubberbandEllipse::InternalData { SituationMap::RefPointer _ellipseSymbol; int _alphaFill = 50; // alpha value used for fill color of ellipse QPointF _sectorOStart = QPointF(1,0); QPointF _sectorOEnd = QPointF(1,0); SituationMap::RefPointer _sectorStartHandle; SituationMap::RefPointer _sectorEndHandle; bool _sectorEnabled = false; bool _sectorFixedAngleEnabled = false; }; /* RubberbandEllipse::InternalData */ RubberbandEllipse::RubberbandEllipse( SituationMap::Map * map, QObject * parent ) : RubberbandRectangle(map,parent), _internal(new InternalData) { useDottedLineHandles(true); } /* RubberbandEllipse::RubberbandEllipse */ RubberbandEllipse::~RubberbandEllipse() { delete _internal; _internal = nullptr; } /* RubberbandEllipse::~RubberbandEllipse */ QPointF RubberbandEllipse::getRotatedPoint(QPointF sector) const { qreal sinRect = 0, cosRect = 1; getSceneRotation( sinRect, cosRect); double angleRad = SituationMap::MathUtils::getMathAngle(QPointF(cosRect,sinRect));// angle rad //double angleDeg = SituationMap::MathUtils::getMathAngle(QPointF(cosRect,sinRect)) * LtsCoordTrans::rad2deg(); const double clockwiseAngle = ((360*LtsCoordTrans::deg2rad()) - angleRad);// angle in rad; // map clockwise rotation QPointF point = sector; point.setX( sector.x()*std::cos(clockwiseAngle) + sector.y()*std::sin(clockwiseAngle)); point.setY( -sector.x()*std::sin(clockwiseAngle) + sector.y()*std::cos(clockwiseAngle)); return point; } void RubberbandEllipse::setSectorStart(qreal sinPhi, qreal cosPhi) { _internal->_sectorOStart = QPointF( cosPhi, sinPhi ); update(); } /* RubberbandEllipse::setSectorStart */ void RubberbandEllipse::setSectorEnd(qreal sinPhi, qreal cosPhi) { _internal->_sectorOEnd = QPointF( cosPhi, sinPhi ); update(); } /* RubberbandEllipse::setSectorEnd */ QPointF RubberbandEllipse::sectorStart() const { return getRotatedPoint(_internal->_sectorOStart); } /* RubberbandEllipse::sectorStart */ QPointF RubberbandEllipse::sectorEnd() const { return getRotatedPoint(_internal->_sectorOEnd); } /* RubberbandEllipse::sectorEnd */ bool RubberbandEllipse::hasSector() const { auto getAngle = [](qreal sinPhi, qreal cosPhi) -> int { qreal phi = acos( cosPhi ); if (sinPhi < 0) phi = MathConst::pi_two - phi; return int( (phi * 180.0 / MathConst::pi) * 16); }; // getAngle int phiStart = getAngle(_internal->_sectorOStart.y(),_internal->_sectorOStart.y()); int phiEnd = getAngle( _internal->_sectorOEnd.y(), _internal->_sectorOEnd.y()); return phiStart != phiEnd; } /* RubberbandEllipse::hasSector */ void RubberbandEllipse::enableSector(bool on) { _internal -> _sectorEnabled = on; } bool RubberbandEllipse::isSectorEnabled() const { return _internal -> _sectorEnabled; } void RubberbandEllipse::setSectorAngleFixed(bool on) { _internal -> _sectorFixedAngleEnabled = on; } bool RubberbandEllipse::isSectorFixedAngeEnabled() const { return _internal->_sectorFixedAngleEnabled; } bool RubberbandEllipse::selectAction(AbstractEventDispatcher * causer, int x, int y) { if (rubberbandIsAlive()) { QPointF scenePos(pixel2ScenePosition(x,y)); _baseInternal->_lastClickPosition = scenePos; if (_internal -> _sectorEnabled) { if ((_internal->_sectorEndHandle.valid()) && (_internal->_sectorEndHandle->isPositionInside(scenePos))) { _internal->_sectorEndHandle->setSelection(true); enableSelectedState(); update(); deactivateButtons(); return true; } if ((_internal->_sectorStartHandle.valid()) && (_internal->_sectorStartHandle->isPositionInside(scenePos))) { _internal->_sectorStartHandle->setSelection(true); enableSelectedState(); update(); deactivateButtons(); return true; } } } return RubberbandRectangle::selectAction(causer,x,y); } /* RubberbandEllipse::selectAction */ bool RubberbandEllipse::moveAction(AbstractEventDispatcher * causer, int x, int y) { if (rubberbandIsAlive()) { QPointF scenePos(pixel2ScenePosition(x,y)); if (_internal -> _sectorEnabled) { if (_internal->_sectorEndHandle.valid()) { if (_internal->_sectorEndHandle->isPositionInside(scenePos)) { _internal->_sectorEndHandle->setSelection(true); update(); return false; } else _internal->_sectorEndHandle->setSelection(false); } if (_internal->_sectorStartHandle.valid()) { if (_internal->_sectorStartHandle->isPositionInside(scenePos)) { _internal->_sectorStartHandle->setSelection(true); update(); return false; } else _internal->_sectorStartHandle->setSelection(false); } } } return RubberbandRectangle::moveAction(causer,x,y); } /* RubberbandEllipse::moveAction */ bool RubberbandEllipse::setCoordinateAction(AbstractEventDispatcher * causer, int x, int y) { if (rubberbandIsAlive()) { if (_internal -> _sectorEnabled) { if ((_internal->_sectorEndHandle.valid()) && (_internal->_sectorEndHandle->isSelected())) { _internal->_sectorEndHandle->setSelection(false); update(); activateButtons(); return true; } if ((_internal->_sectorStartHandle.valid()) && (_internal->_sectorStartHandle->isSelected())) { _internal->_sectorStartHandle->setSelection(false); update(); activateButtons(); return true; } } } return RubberbandRectangle::setCoordinateAction(causer,x,y); } /* RubberbandEllipse::setCoordinateAction */ bool RubberbandEllipse::movePickedAction(AbstractEventDispatcher * causer, int x, int y) { if (rubberbandIsAlive()) { auto updateHandle = [this](RubberbandPointHandle * handle, const RubberbandHandle::RubberbandCoordinate & pos, QPointF & oVector ) -> bool { if (handle == nullptr) return false; if (!handle->isSelected()) return false; if (isSectorFixedAngeEnabled()) return false; qreal sinRect = 0; qreal cosRect = 1; const QPointF & center = getCenter(); getSceneRotation( sinRect, cosRect); qreal cosA = pos._mapCoordinate.x() - center.x(); qreal sinA = pos._mapCoordinate.y() - center.y(); qreal len = sqrt(cosA*cosA+sinA*sinA); cosA /= len; sinA /= len; oVector.setX( cosA*cosRect + sinA*sinRect ); oVector.setY( sinA*cosRect - cosA*sinRect ); update(); return true; }; // updateHandle QPointF scenePos(pixel2ScenePosition(x,y)); RubberbandHandle::RubberbandCoordinate pos(Rubberband::_internal->_mapView, scenePos ); if (updateHandle( _internal->_sectorEndHandle.get() , pos, _internal->_sectorOEnd )) return false; if (updateHandle( _internal->_sectorStartHandle.get(), pos, _internal->_sectorOStart )) return false; } return RubberbandRectangle::movePickedAction(causer,x,y); } /* RubberbandEllipse::movePickedAction */ void RubberbandEllipse::updateSectorHandles() { auto ellipse = []( qreal a, qreal b, qreal sinA, qreal cosA ) -> QPointF { /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Qt does not use the paramater equation of a ellipse to draw sectors. Instead of a simpler term * is used. For simpleness we use the same of. * qreal e2 = 1 - (b*b) / (a*a); qreal cos2 = cosA * cosA; qreal f = b / sqrt(1-e2*cos2); return QPointF( f*cosA, f*sinA ); * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ return QPointF(a*cosA,b*sinA); }; 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 a = getWidth() * 0.5; qreal b = getHeight() * 0.5; QPointF startP = ellipse(a,b,_internal->_sectorOStart.y(),_internal->_sectorOStart.x() ); QPointF endP = ellipse(a,b,_internal->_sectorOEnd.y() ,_internal->_sectorOEnd.x() ); qreal sinRect = 0; qreal cosRect = 1; getSceneRotation( sinRect, cosRect); startP = rotate(startP, sinRect, cosRect) + getCenter(); endP = rotate( endP, sinRect, cosRect) + getCenter(); const Esri::ArcGISRuntime::Point &mapStartP = convert( startP, Rubberband::_internal->_mapView->map()->spatialReference() ); const Esri::ArcGISRuntime::Point &mapEndP = convert( endP, Rubberband::_internal->_mapView->map()->spatialReference() ); if (!_internal->_sectorStartHandle.valid()) { _internal->_sectorStartHandle = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapStartP ); } else _internal->_sectorStartHandle->setCoordinate( RubberbandHandle::RubberbandCoordinate(Rubberband::_internal->_mapView,mapStartP) ); _internal->_sectorStartHandle -> setVisibility((isSectorEnabled() && !isSectorFixedAngeEnabled())/*_internal->_sectorEnabled*/); _internal->_sectorStartHandle -> setDrawColor(_handleDrawColor); _internal->_sectorStartHandle -> setFillColor( _handleFillColor ); _internal->_sectorStartHandle -> setSelectionColor(_handleSelectionColor); if (!_internal->_sectorEndHandle.valid()) { _internal->_sectorEndHandle = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapEndP ); } else _internal->_sectorEndHandle->setCoordinate( RubberbandHandle::RubberbandCoordinate(Rubberband::_internal->_mapView,mapEndP) ); _internal->_sectorEndHandle -> setVisibility(isSectorEnabled() && !isSectorFixedAngeEnabled()/*_internal->_sectorEnabled*/); _internal->_sectorEndHandle -> setDrawColor(_handleDrawColor); _internal->_sectorEndHandle -> setFillColor( _handleFillColor ); _internal->_sectorEndHandle -> setSelectionColor(_handleSelectionColor); } /* RubberbandEllipse::updateSectorHandles */ void RubberbandEllipse::update() { RubberbandRectangle::update(); if (isEnabledModificationState()) { updateSectorHandles(); QPointF center = getCenter(); qreal width2 = getWidth()*0.5; qreal height2 = getHeight()*0.5; qreal sinRect = 0.0; qreal cosRect = 1.0; getSceneRotation( sinRect, cosRect ); if (_internal->_ellipseSymbol == nullptr) { _internal->_ellipseSymbol = new DrawableEllipse( Rubberband::_internal->_mapView ); } QColor drawColor = _drawColor; _internal->_ellipseSymbol->setDrawColor( drawColor ); drawColor.setAlpha(_internal->_alphaFill); if (_internal -> _sectorEnabled){ _fillColor.setAlpha(_internal->_alphaFill+10); _internal->_ellipseSymbol->setFillColor( _fillColor ); } else { _fillColor.setAlpha(0); _internal->_ellipseSymbol->setFillColor( _fillColor ); } _internal->_ellipseSymbol->setRect( center, width2, height2, sinRect, cosRect, _internal->_sectorOStart, _internal->_sectorOEnd ); } } /* RubberbandEllipse::update */ void RubberbandEllipse::buildTmpConstruction( SituationMap::RefPointer &tmpGraphic, QPointF &start, QPointF &end ) { if (!tmpGraphic.valid()) { tmpGraphic = new DrawableEllipse( 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; qreal sinMap; qreal cosMap; getMapRotation( sinMap, cosMap ); auto rotate = [sinMap, cosMap](qreal x, qreal y) -> QPointF { qreal px = x*cosMap - y*sinMap; qreal py = y*cosMap + x*sinMap; return QPointF(px,py); }; // rotate buildTmpConstructionPath( start-end + start, end, path, sceneCenter, sceneWidth, sceneHeight ); DrawableEllipse * tmpEllipse = static_cast(tmpGraphic.get()); RubberbandHandle::RubberbandCoordinate mapCenter( Rubberband::_internal->_mapView, start ); RubberbandHandle::RubberbandCoordinate mapRadius( Rubberband::_internal->_mapView, start+rotate(sceneWidth*0.5,sceneHeight*0.5) ); qreal w = mapRadius._mapCoordinate.x() - mapCenter._mapCoordinate.x(); qreal h = mapRadius._mapCoordinate.y() - mapCenter._mapCoordinate.y(); tmpEllipse->setRect( QPointF(mapCenter._mapCoordinate.x(),mapCenter._mapCoordinate.y()), w, h, 0, 1 ); } /* RubberbandEllipse::buildTmpConstruction */ void RubberbandEllipse::getTmpConstructionParameter(const QPointF & start, const QPointF & end, QPointF & sceneCenter, qreal & sceneWidth, qreal & sceneHeight) { QVector path; qreal sinMap; qreal cosMap; getMapRotation( sinMap, cosMap ); auto rotate = [sinMap, cosMap]( qreal x, qreal y ) -> QPointF { qreal px = x*cosMap - y*sinMap; qreal py = y*cosMap + x*sinMap; return QPointF(px,py); }; // rotate auto length = [](qreal x, qreal y) -> qreal { return sqrt(x*x+y*y); }; buildTmpConstructionPath( start, end, path, sceneCenter, sceneWidth, sceneHeight ); RubberbandHandle::RubberbandCoordinate mapCenter ( Rubberband::_internal->_mapView, start ); RubberbandHandle::RubberbandCoordinate mapRadiusX( Rubberband::_internal->_mapView, start+rotate(sceneWidth,0) ); RubberbandHandle::RubberbandCoordinate mapRadiusY( Rubberband::_internal->_mapView, start+rotate(0,sceneHeight) ); qreal w = length( mapRadiusX._sceneCoordinate.x()-mapCenter._sceneCoordinate.x(), mapRadiusX._sceneCoordinate.y()-mapCenter._sceneCoordinate.y() ); qreal h = length( mapRadiusY._sceneCoordinate.x()-mapCenter._sceneCoordinate.x(), mapRadiusY._sceneCoordinate.y()-mapCenter._sceneCoordinate.y() ); sceneCenter = start; sceneWidth = 2*w; sceneHeight = 2*h; } /* RubberbandEllipse::getTmpConstructionParameter */ } } /* namespace SituationMap::RB */