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

456 lines
17 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_ellipse.h"
#include "drawable_ellipse.h"
#include "rubberband_point_handle.h"
#include "rubberband_internal.h"
#include "rubberband_base_internal.h"
#include <MathUtils/math_const.h>
#include <MathUtils/simple_helper.h>
#include <LtsCoordTrans/lts_coord_trans.h>
namespace SituationMap { namespace RB
{
struct RubberbandEllipse::InternalData
{
SituationMap::RefPointer<DrawableEllipse> _ellipseSymbol;
int _alphaFill = 50; // alpha value used for fill color of ellipse
QPointF _sectorOStart = QPointF(1,0);
QPointF _sectorOEnd = QPointF(1,0);
SituationMap::RefPointer<RubberbandPointHandle> _sectorStartHandle;
SituationMap::RefPointer<RubberbandPointHandle> _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<RubberbandDrawable> &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<QPointF> 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<DrawableEllipse*>(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<QPointF> 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 */