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

1748 lines
61 KiB
C++
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @RDE_HEADER@
* Rubberband class to define multiple ellipse/circle
* $Id$
* $HeadURL$
* $Author$
*/
#include "rubberband_multi_ellipse_feature.h"
#include "rubberband_frame_handle.h"
#include "rubberband_line_handle.h"
//#include "rubberband_ellipse.h"
#include "rubberband_ellipse_handle.h"
#include "rubberband_point_handle.h"
#include "rubberband_drawable.h"
#include "rubberband_handle_proxy.h"
#include "drawable_line.h"
#include "drawable_ellipse.h"
#include "drawable_painter_path.h"
#include "transient_tactical_symbol.h"
#include "rubberband_internal.h"
#include "rubberband_base_internal.h"
#include "winkel_icon.xpm"
//#include <MathUtils/math_const.h>
#include <MathUtils/simple_helper.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <MathUtils/matrix2d.h>
#include <arcgis/Map.h>
#include <QtCore/QMetaMethod>
namespace SituationMap
{
namespace RB
{
struct RubberbandMultiEllipseFeature::InternalData
{
Q_DISABLE_COPY(InternalData)
QList<SituationMap::RefPointer<RubberbandEllipseHandle>> _ellipseHandleList;
SituationMap::RefPointer<RubberbandFrameHandle> _frameHandle;
// sector :
QPointF _sectorOStart{1, 0};
QPointF _sectorOEnd{0, 1};
SituationMap::RefPointer<RubberbandPointHandle> _sectorStartHandle;
SituationMap::RefPointer<RubberbandPointHandle> _sectorEndHandle;
bool _sectorEnabled = false;
bool _sectorFixedAngleEnabled{false};
QPointF _oVec{1,0}; // rb: // normalized orientation vector according to scene coordinate system
// : sector
// rotationHandle :
SituationMap::RefPointer<RubberbandPointHandle> _rotationHandle;
unsigned int _rotationHandleSize{0};
// : rotationHandle
// Coordinate matrix to describe the change of coordinates of the last user input
MathUtils::Matrix2D _lastCoordinateChange;
SituationMap::RefPointer<DrawableEllipse> _previewEllipse;
SituationMap::RefPointer<TransientTacticalSymbol> _previewSymbol;
QVector<RubberbandMultiEllipseFeature::RadiusWithContraints> _preparedRadiiConstraints;
bool _complexGeometry;
bool _addVertexAtEnd;
bool _showCursorPreviewEllipse;
bool _constrainQuadratic{true};
InternalData(QGraphicsScene *scene, bool complexGeometry)
: _complexGeometry(complexGeometry)
, _addVertexAtEnd(false)
, _showCursorPreviewEllipse(false)
{
Q_UNUSED(scene);
} // InternalData
}; // RubberbandMultiEllipseFeature::InternalData
QPointF rotateVertex(const QPointF &center, qreal x, qreal y, qreal sinA, qreal cosA)
{
return QPointF(x*cosA-y*sinA,y*cosA+x*sinA) + center;
}
RubberbandMultiEllipseFeature::RubberbandMultiEllipseFeature(SituationMap::Map *map, QObject *parent)
: RubberbandMultiEllipseFeature(map, false, parent)
{
}
RubberbandMultiEllipseFeature::RubberbandMultiEllipseFeature(SituationMap::Map *map, bool complexGeometry, QObject *parent)
: Rubberband(map, parent)
, _internal(new InternalData(Rubberband::_internal->_mapScene, complexGeometry))
, _isClosed(false)
, _minimumPointCount(2)
, _maximumPointCount(0)
, _maximumSegmentLength(0.0)
, _maximumLength(0.0)
{
}
RubberbandMultiEllipseFeature::~RubberbandMultiEllipseFeature()
{
delete _internal;
}
RubberbandFrameHandle *RubberbandMultiEllipseFeature::getFrameHandle() const
{
return _internal->_frameHandle.get();
}
QPointF RubberbandMultiEllipseFeature::centerCoordinate(CoordinateSystem cs) const
{
if (Rubberband::_internal->_handleList.isEmpty())
return {};
RubberbandPointHandle *handlePointCenter = dynamic_cast<RubberbandPointHandle *>(Rubberband::_internal->_handleList.first().get());
if (!handlePointCenter)
return {};
return projectCoordinateFromMap(cs, handlePointCenter->getCoordinate()._mapCoordinate);
}
QVector<QPointF> RubberbandMultiEllipseFeature::radii() const
{
QPointF center = centerCoordinate(CoordinateSystem::Map);
if (center.isNull())
return {};
if (_internal->_ellipseHandleList.isEmpty())
return {};
QVector<QPointF> ret;
ret.reserve(_internal->_ellipseHandleList.size());
for (SituationMap::RefPointer<RubberbandEllipseHandle> handle : qAsConst(_internal->_ellipseHandleList))
{
if (!handle)
continue;
ret.append(handle->getRadius());
}
return ret;
}
QPointF RubberbandMultiEllipseFeature::getBiggestRadius() const
{
QPointF max(0,0);
const QVector<QPointF> &radiiCont = radii();
for (const QPointF &item : qAsConst(radiiCont))
{
max.setX( std::max(max.x(), item.x()) );
max.setY( std::max(max.y(), item.y()) );
}
return max;
}
bool RubberbandMultiEllipseFeature::setCenterAndRadii(const QPointF &center, CoordinateSystem cs,
const QVector<QPointF> &radii)
{
if (center.isNull() || radii.isEmpty())
return false;
const QPointF &myCenter = projectCoordinateToMap(cs, center);
QVector<QPointF> coordinateVector;
coordinateVector.reserve(radii.size()+1);
// Center:
coordinateVector.append(myCenter);
// radii:
for (const QPointF& radius : qAsConst(radii))
coordinateVector.append(myCenter + radius);
setCoordinateVector(coordinateVector, CoordinateSystem::Map);
return true;
}
bool RubberbandMultiEllipseFeature::setCenterAndRadii2(const QPointF &center, CoordinateSystem cs,
const QVector<RubberbandMultiEllipseFeature::RadiusWithContraints> &radiiWithConstraints)
{
if (center.isNull() || radiiWithConstraints.isEmpty())
return false;
const QPointF &myCenter = projectCoordinateToMap(cs, center);
QVector<QPointF> coordinateVector;
coordinateVector.reserve(radiiWithConstraints.size()+1);
// Center:
coordinateVector.append(myCenter);
// Center with the radii:
for (const RubberbandMultiEllipseFeature::RadiusWithContraints& radiusItem : qAsConst(radiiWithConstraints))
if (radiusItem.m_radius > 0)
coordinateVector.append(myCenter + QPointF(radiusItem.m_radius, radiusItem.m_radius));
Rubberband::_internal->_handleList.clear();
for (const auto &p : coordinateVector)
constructPoint(p, CoordinateSystem::Map);
// are we still in construction or in modification?
// from scratch: constraints are provided, but no radius
// predefined: radius is provided, maybe constraints
if (coordinateVector.size() > 1)
enableModificationState();
update();
activateButtons();
return true;
}
void RubberbandMultiEllipseFeature::prepareFixedNumberOfRadiiWithConstraints(const QVector<RubberbandMultiEllipseFeature::RadiusWithContraints> &radiiContraints)
{
// for creation from scratch, just min/max should be prepared.
// for creation with predefined values, the m_radius is relevant.
_internal->_preparedRadiiConstraints = radiiContraints;
}
QPointF RubberbandMultiEllipseFeature::getRotatedPoint(const QPointF &sector) const
{
// Rotation of entire Rubberband
qreal sinRect = 0, cosRect = 1;
getSceneRotation(sinRect, cosRect);
const double angleRad = SituationMap::MathUtils::getMathAngle(QPointF(cosRect, sinRect)); // angle rad
const double clockwiseAngleRad = ((2*LtsCoordTrans::pi()) - angleRad); // angle in rad;
// map clockwise rotation
QPointF point;
point.setX( sector.x()*std::cos(clockwiseAngleRad) + sector.y()*std::sin(clockwiseAngleRad));
point.setY( -sector.x()*std::sin(clockwiseAngleRad) + sector.y()*std::cos(clockwiseAngleRad));
return point;
}
void RubberbandMultiEllipseFeature::enableSector(bool on)
{
_internal->_sectorEnabled = on;
}
bool RubberbandMultiEllipseFeature::isSectorEnabled() const
{
return _internal->_sectorEnabled;
}
QPointF RubberbandMultiEllipseFeature::sectorStart() const
{
return getRotatedPoint(_internal->_sectorOStart);
}
qreal RubberbandMultiEllipseFeature::sectorStartDegMath() const
{
const QPointF &point = sectorStart();
return MathUtils::getMathAngle(point) * LtsCoordTrans::rad2deg();
}
QPointF RubberbandMultiEllipseFeature::sectorEnd() const
{
return getRotatedPoint(_internal->_sectorOEnd);
}
qreal RubberbandMultiEllipseFeature::sectorEndDegMath() const
{
const QPointF &point = sectorEnd();
return MathUtils::getMathAngle(point) * LtsCoordTrans::rad2deg();
}
void RubberbandMultiEllipseFeature::setSectorStart(qreal sinPhi, qreal cosPhi)
{
_internal->_sectorOStart = QPointF(cosPhi, sinPhi);
enableSelectedState();
update();
}
void RubberbandMultiEllipseFeature::setSectorEnd(qreal sinPhi, qreal cosPhi)
{
_internal->_sectorOEnd = QPointF(cosPhi, sinPhi);
enableSelectedState();
update();
}
void RubberbandMultiEllipseFeature::setSectorAngleFixed(bool on)
{
_internal->_sectorFixedAngleEnabled = on;
update();
}
bool RubberbandMultiEllipseFeature::isSectorFixedAngeEnabled() const
{
return _internal->_sectorFixedAngleEnabled;
}
QVector<QPointF> RubberbandMultiEllipseFeature::coordinateVector(CoordinateSystem cs) const
{
QVector<QPointF> coordinateVector;
coordinateVector.reserve(Rubberband::_internal->_handleList.size());
for (auto handle : qAsConst(Rubberband::_internal->_handleList))
{
RubberbandPointHandle *handlePointPtr = dynamic_cast<RubberbandPointHandle *>(handle.get());
coordinateVector.push_back(projectCoordinateFromMap(cs, handlePointPtr->getCoordinate()._mapCoordinate));
}
return coordinateVector;
}
QPointF RubberbandMultiEllipseFeature::singleCoordinate(unsigned int idx, CoordinateSystem cs ) const
{
if (idx < (unsigned int)Rubberband::_internal->_handleList.size())
{
const RubberbandPointHandle * handle = dynamic_cast<const RubberbandPointHandle*>(Rubberband::_internal->_handleList[idx].get());
return projectCoordinateFromMap( cs, handle->getCoordinate()._mapCoordinate );
}
return QPointF();
}
void RubberbandMultiEllipseFeature::setCoordinateVector(const QVector<QPointF> &coordinateVector, CoordinateSystem cs)
{
Rubberband::_internal->_handleList.clear();
for (const auto &p : coordinateVector)
constructPoint(p, cs);
enableModificationState();
update();
activateButtons();
}
bool RubberbandMultiEllipseFeature::getRadiusXY(const QPointF &scenePos, qreal &radiusX_out, qreal &radiusY_out)
{
if (Rubberband::_internal->_handleList.isEmpty())
return false;
RubberbandPointHandle *handlePointCenter = dynamic_cast<RubberbandPointHandle *>(Rubberband::_internal->_handleList.first().get());
if (!handlePointCenter)
return false;
const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointCenter->getCoordinate();
RubberbandHandle::RubberbandCoordinate currentMapPos(Rubberband::_internal->_mapView, scenePos);
radiusX_out = fabs(mapCenter._mapCoordinate.x() - currentMapPos._mapCoordinate.x());
radiusY_out = fabs(mapCenter._mapCoordinate.y() - currentMapPos._mapCoordinate.y());
return true;
}
void RubberbandMultiEllipseFeature::constructPoint(const QPointF &point, CoordinateSystem cs)
{
const QPointF &mapPos = projectCoordinateToMap(cs, point);
RubberbandHandle::RubberbandCoordinate coordinate(Rubberband::_internal->_mapView, convert(mapPos, Rubberband::_internal->_mapView->map()->spatialReference()));
RubberbandHandle *handlePtr = constructPoint(coordinate._sceneCoordinate);
if (handlePtr)
{
static_cast<RubberbandPointHandle *>(handlePtr)->setCoordinate(coordinate);
handlePtr->setSelection(false);
}
}
RubberbandHandle *RubberbandMultiEllipseFeature::constructPoint(const QPointF &scenePos)
{
DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION
<< " RubberbandMultiEllipseFeature::constructPoint, _handleList.size " << Rubberband::_internal->_handleList.size();
RubberbandHandle *handlePtr{nullptr};
if ((_maximumPointCount>0) && ((unsigned int)Rubberband::_internal->_handleList.size() >= _maximumPointCount))
{
DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " skip due to _maximumPointCount (_sectorInputIsRunning)";
return nullptr;
}
if (!Rubberband::_internal->_handleList.empty())
{
handlePtr = Rubberband::_internal->_handleList.first().get();
if (handlePtr)
{
// center == first
const RubberbandHandle::RubberbandCoordinate &firstPosCenterPos = static_cast<RubberbandPointHandle *>(handlePtr)->getCoordinate();
qreal radiusX_out, radiusY_out;
if (getRadiusXY(scenePos, radiusX_out, radiusY_out))
{
bool minMaxOk = checkMinMaxRadiusConstraint(radiusX_out, radiusY_out);
if (minMaxOk)
{
RubberbandEllipseHandle *temp = new RubberbandEllipseHandle(Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene);
if (_internal->_constrainQuadratic)
radiusX_out = radiusY_out = std::max(radiusX_out, radiusY_out);
temp->setRect(firstPosCenterPos, radiusX_out, radiusY_out, 0, 1);
QColor fillColor = _fillColor;
fillColor.setAlphaF(_fillColor.alphaF()*0.5);
temp->setFillColor(fillColor);
QColor selectionColor = _handleSelectionColor;
selectionColor.setAlphaF(_handleSelectionColor.alphaF()*0.5);
temp->setSelectionColor(selectionColor);
_internal->_ellipseHandleList.append(RefPointer<RubberbandEllipseHandle>(static_cast<RubberbandEllipseHandle *>(temp)));
} else {
DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION
<< " SKIP RubberbandMultiEllipseFeature::constructPoint, "
<< ", radiusX_out" << radiusX_out << ", radiusY_out" << radiusY_out;
return nullptr;
}
}
}
}
//
// RubberbandPointHandle einfügen
//
handlePtr = new RubberbandPointHandle(Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, scenePos);
handlePtr->setSelection();
handlePtr->setIsFirst(Rubberband::_internal->_handleList.empty()); // mark centerPoint with 'isFirst' to simplify later recognation
Rubberband::_internal->_handleList.append(SituationMap::RefPointer<RubberbandHandle>(handlePtr));
// activateButtons?
if ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount)
activateButtons();
if ((!_maximumPointCount) || ((unsigned int)Rubberband::_internal->_handleList.size() < _maximumPointCount))
{
if (!_internal->_previewEllipse)
_internal->_previewEllipse = new DrawableEllipse(Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene);
_internal->_previewEllipse->setVisibility(true);
}
else
{
_internal->_previewEllipse = nullptr;
}
return handlePtr;
}
bool RubberbandMultiEllipseFeature::checkLengthConstrains(const QPointF &scenePos, const QPointF &deltaScenePos)
{
Q_UNUSED(scenePos);
Q_UNUSED(deltaScenePos);
return true;
/*
if (_internal->_complexGeometry)
{
markFailedConstrains(QPointF(), false);
return true;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Checking constrains for moving selected coordinates (handles)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool allHandlesSelected = true;
QVector<RubberbandHandle::RubberbandCoordinate> coordinateVector;
coordinateVector.reserve(Rubberband::_internal->_handleList.size());
for (auto handle : qAsConst(Rubberband::_internal->_handleList))
{
RubberbandPointHandle *handlePointPtr = dynamic_cast<RubberbandPointHandle *>(handle.get());
if (handlePointPtr)
{
coordinateVector.append(handlePointPtr->getCoordinate());
if (handlePointPtr->isSelected())
{
RubberbandHandle::RubberbandCoordinate &coordinate = coordinateVector.back();
coordinate.updateSceneCoordinate(Rubberband::_internal->_mapView, coordinate._sceneCoordinate + deltaScenePos);
}
else allHandlesSelected = false;
}
}
if ((allHandlesSelected) || ((_internal->_frameHandle) && (_internal->_frameHandle->isSelected())))
{
markFailedConstrains(QPointF(), false);
return true;
}
//
// check totalLength
//
if (_maximumLength > 0.0)
{
qreal totalLength = 0.0;
const Esri::ArcGISRuntime::Point *lastMapCoord = nullptr; // Check Quartz
for (int i = 0; i < coordinateVector.size(); ++i)
{
const RubberbandHandle::RubberbandCoordinate &coordinate = coordinateVector[i];
if (lastMapCoord)
{
qreal x = coordinate._mapCoordinate.x() - lastMapCoord->x();
qreal y = coordinate._mapCoordinate.y() - lastMapCoord->y();
totalLength += sqrt(x * x + y * y);
}
lastMapCoord = &coordinateVector.at(i)._mapCoordinate;
}
if (totalLength > _maximumLength)
{
markFailedConstrains(scenePos, true);
return false;
}
}
//
// check maximumSegmentLength
//
const RubberbandHandle::RubberbandCoordinate *mapPointPtr = nullptr;
bool lastSelected = false;
auto handleIter = Rubberband::_internal->_handleList.constBegin();
for (int i = 0; i < coordinateVector.size(); ++i)
{
const RubberbandHandle::RubberbandCoordinate &coordinate = coordinateVector[i];
bool isSelected = (*handleIter)->isSelected();
if ((isSelected || lastSelected) && (mapPointPtr))
{
if (_maximumSegmentLength > 0.0)
{
qreal x = coordinate._mapCoordinate.x() - mapPointPtr->_mapCoordinate.x();
qreal y = coordinate._mapCoordinate.y() - mapPointPtr->_mapCoordinate.y();
if (sqrt(x * x + y * y) > _maximumSegmentLength)
{
markFailedConstrains(scenePos, true);
return false;
}
}
// looking for identical coordinate with last point
qreal x = coordinate._sceneCoordinate.x() - mapPointPtr->_sceneCoordinate.x();
qreal y = coordinate._sceneCoordinate.y() - mapPointPtr->_sceneCoordinate.y();
if ((x * x + y * y) <= 1)
{
markFailedConstrains(scenePos, true);
return false;
}
}
++handleIter;
mapPointPtr = &coordinateVector.at(i);
lastSelected = isSelected;
} // for
markFailedConstrains(QPointF(), false);
return true;
*/
}
bool RubberbandMultiEllipseFeature::checkMinMaxRadiusConstraint(const QPointF &curScenePos)
{
qreal radiusX_out, radiusY_out;
if (!getRadiusXY(curScenePos, radiusX_out, radiusY_out))
return true;
if (_internal->_constrainQuadratic)
radiusX_out = radiusY_out = std::max(radiusX_out, radiusY_out);
return checkMinMaxRadiusConstraint(radiusX_out, radiusY_out);
}
bool RubberbandMultiEllipseFeature::checkMinMaxRadiusConstraint(qreal radiusX, qreal radiusY, int idxOfConstraint)
{
// Which radius is relevant?
if (idxOfConstraint < 0)
idxOfConstraint = Rubberband::_internal->_handleList.size() - 1; // '-1' - keep handle for center (isFirst) in mind
if ((idxOfConstraint < 0) || (idxOfConstraint >= _internal->_preparedRadiiConstraints.size()))
return true;
const RadiusWithContraints &curConstraints = _internal->_preparedRadiiConstraints[idxOfConstraint];
// qCritical() << " radiusX = " << radiusX;
// qCritical() << " radiusY = " << radiusY;
bool bMinOk = true;
double minRadius = curConstraints.m_minRadius;
if (minRadius > 0)
bMinOk = ((radiusX >= minRadius) && (radiusY >= minRadius));
bool bMaxOk = true;
double maxRadius = curConstraints.m_maxRadius;
if (maxRadius > 0)
bMaxOk = ((radiusX <= maxRadius) && (radiusY <= maxRadius));
if (!bMinOk)
INFO_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " checkMinMaxRadiusConstraintNew false by bMinOk"
<< ", idxOfConstraint=" << idxOfConstraint
<< ", radiusX=" << radiusX << ", radiusY=" << radiusY
<< ", minRadius=" << minRadius << ", maxRadius=" << maxRadius;
if (!bMaxOk)
INFO_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " checkMinMaxRadiusConstraintNew false by bMaxOk"
<< ", idxOfConstraint=" << idxOfConstraint
<< ", radiusX=" << radiusX << ", radiusY=" << radiusY
<< ", minRadius=" << minRadius << ", maxRadius=" << maxRadius;
return bMinOk && bMaxOk;
}
bool RubberbandMultiEllipseFeature::checkLengthConstrains(unsigned int checkHandleIdx, const QPointF &scenePos)
{
Q_UNUSED(checkHandleIdx);
if (!checkMinMaxRadiusConstraint(scenePos))
{
markFailedConstrains(scenePos, true);
return false;
}
markFailedConstrains(QPoint(), true);
return true;
// return true -> construct point
// return false -> don not construct point
/*
if (_internal->_complexGeometry)
{
markFailedConstrains(QPointF(), false);
return true;
}
bool markFailedConstrainsFlag = false;
// calculate pixel size for later use
RubberbandDrawable::RubberbandCoordinate pi1(Rubberband::_internal->_mapView, scenePos);
RubberbandDrawable::RubberbandCoordinate pi2(Rubberband::_internal->_mapView, scenePos + QPointF(1, 1));
qreal piX = pi2._mapCoordinate.x() - pi1._mapCoordinate.x();
qreal piY = pi2._mapCoordinate.y() - pi1._mapCoordinate.y();
qreal pixelSize = sqrt(piX * piX + piY * piY);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Checking constrains for adding single coordinate at end of polyline
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RubberbandHandle::RubberbandCoordinate mapPos(Rubberband::_internal->_mapView, scenePos);
if ((!Rubberband::_internal->_handleList.empty()) && (_maximumLength > 0.0))
{
qreal totalLength = 0.0;
RubberbandHandle::RubberbandCoordinate mapPoint;
QList<SituationMap::RefPointer<RubberbandHandle>>::iterator it = Rubberband::_internal->_handleList.begin();
for (; it != Rubberband::_internal->_handleList.end(); ++it)
{
RubberbandPointHandle *handlePointPtr = dynamic_cast<RubberbandPointHandle *>(it->get());
if (handlePointPtr)
{
if (it != Rubberband::_internal->_handleList.begin())
{
qreal x = handlePointPtr->getCoordinate()._mapCoordinate.x() - mapPoint._mapCoordinate.x();
qreal y = handlePointPtr->getCoordinate()._mapCoordinate.y() - mapPoint._mapCoordinate.y();
totalLength += sqrt(x * x + y * y);
}
mapPoint = handlePointPtr->getCoordinate();
}
}
if (totalLength >= _maximumLength)
{
markFailedConstrains(scenePos, true);
return false;
}
qreal x = mapPos._mapCoordinate.x() - mapPoint._mapCoordinate.x();
qreal y = mapPos._mapCoordinate.y() - mapPoint._mapCoordinate.y();
qreal l = sqrt(x * x + y * y);
qreal d = _maximumLength - totalLength - l - pixelSize;
if (d < 0.0)
{
qreal scale = (1.0 + d / l);
const Esri::ArcGISRuntime::Point newMapCoordinate(mapPoint._mapCoordinate.x() + x * scale,
mapPoint._mapCoordinate.y() + y * scale,
mapPos._mapCoordinate.spatialReference());
mapPos._mapCoordinate = newMapCoordinate;
markFailedConstrainsFlag = true;
scenePos = mapPos.mapToScene(Rubberband::_internal->_mapView);
}
}
// check radius
if (!checkMinMaxRadiusConstraint(scenePos))
{
markFailedConstrains(scenePos, true);
return false;
// markFailedConstrainsFlag = true;
// return true;
}
markFailedConstrains(scenePos, markFailedConstrainsFlag);
return true;
*/
}
bool RubberbandMultiEllipseFeature::selectHandles(int x, int y, const QPointF &scenePos)
{
bool eventHandled = false;
RubberbandHandle *handlePtr = pickHandle(x, y);
_internal->_addVertexAtEnd = false;
if (handlePtr && handlePtr->isVisible())
{
handlePtr->setSelection();
enableSelectedState();
eventHandled = true;
}
if (!isEnabledSelectedState())
{
// ok doesn't hit any point or line handle now looking to frame handle
if (_internal->_frameHandle && _internal->_frameHandle->isPositionInside(scenePos))
{
_internal->_frameHandle->setSelection(true);
enableSelectedState();
eventHandled = true;
}
if (_internal->_rotationHandle && _internal->_rotationHandle->isPositionInside(scenePos))
{
_internal->_rotationHandle->setSelection(true);
enableSelectedState();
eventHandled = true;
}
}
return eventHandled;
}
void RubberbandMultiEllipseFeature::updateHandles(const QPointF &scenePos, const QPointF &deltaMove)
{
Q_UNUSED(deltaMove);
bool constrainsComplied = true;
if (Rubberband::_internal->_handleList.size() < 2)
return;
if ((_internal->_rotationHandle.valid()) && _internal->_rotationHandle->isSelected())
{
const RubberbandPointHandle *handlePointPtr = dynamic_cast<RubberbandPointHandle *>(Rubberband::_internal->_handleList.first().get());
if (!handlePointPtr)
return;
const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointPtr->getCoordinate();
// _internal->_centerScene --> centerScene
const QPointF &centerScene = mapCenter._sceneCoordinate;
const Esri::ArcGISRuntime::Point &centerMapCoords = mapCenter._mapCoordinate;
const QPointF centerMapCoordsAsPointF(centerMapCoords.x(), centerMapCoords.y());
qreal sinA = scenePos.x() - centerScene.x(); // _internal->_centerScene.x();
qreal cosA = - scenePos.y() + centerScene.y();
qreal len = sqrt(cosA*cosA+sinA*sinA);
cosA /= len;
sinA /= len;
// for circle it´s not really helpfull to rotate the handles -> skip here
// for (auto handle : qAsConst(Rubberband::_internal->_handleList))
// {
// RubberbandPointHandle *handlePointPtr = dynamic_cast<RubberbandPointHandle*>(handle.get());
// if (!handlePointPtr)
// continue;
// QPointF p = MathUtils::rotate( handlePointPtr->getCoordinate()._sceneCoordinate, centerScene, -_internal->_oVec.y(), _internal->_oVec.x() );
// p = MathUtils::rotate( p, centerScene, sinA, cosA );
// handlePointPtr->updateCoordinate( p );
// p.setX( handlePointPtr->getCoordinate()._mapCoordinate.x() );
// p.setY( handlePointPtr->getCoordinate()._mapCoordinate.y() );
// } // for
qreal dSin = sinA*_internal->_oVec.x() - cosA*_internal->_oVec.y(); // addition theorem
qreal dCos = cosA*_internal->_oVec.x() + sinA*_internal->_oVec.y();
_internal->_lastCoordinateChange.setToIdentity(); // clear matrix
_internal->_lastCoordinateChange.putTranslation( -centerMapCoordsAsPointF ); // move center to (0,0)
_internal->_lastCoordinateChange.putRotate( QPointF(dCos, dSin) ); // do rotation
_internal->_lastCoordinateChange.putTranslation( centerMapCoordsAsPointF ); // move center back
_internal->_oVec.setX(cosA);
_internal->_oVec.setY(sinA);
_baseInternal->_lastClickPosition = scenePos;
}
markFailedConstrains( scenePos, !constrainsComplied );
update();
}
void RubberbandMultiEllipseFeature::resetAction(AbstractEventDispatcher *causer, int x, int y)
{
if (rubberbandIsAlive())
{
if (isEnabledConstructionState())
{
if (_internal->_addVertexAtEnd)
{
Rubberband::_internal->_handleList.removeLast();
//_internal->_lineHandleList.removeLast();
}
}
}
Rubberband::resetAction(causer, x, y);
}
bool RubberbandMultiEllipseFeature::selectAction(AbstractEventDispatcher *, int x, int y)
{
if (rubberbandIsAlive())
{
DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " selectAction";
QPointF scenePos(pixel2ScenePosition(x, y));
// starting a rubberband with radii prepared but without center?
if (!_internal->_preparedRadiiConstraints.isEmpty() && Rubberband::_internal->_handleList.isEmpty())
{
RubberbandDrawable::RubberbandCoordinate rbC(Rubberband::_internal->_mapView, scenePos);
setCenterAndRadii2(QPointF(rbC._mapCoordinate.x(), rbC._mapCoordinate.y()), CoordinateSystem::Map,
_internal->_preparedRadiiConstraints);
enableSelectedState();
_internal->_addVertexAtEnd = true;
_internal->_showCursorPreviewEllipse = true;
return true;
}
if (_baseInternal->buttonProxyActive(scenePos))
return false;
if (isEnabledConstructionState())
disenableMultiSelectedState();
_baseInternal->_lastClickPosition = scenePos;
bool eventHandled = selectHandles(x, y, scenePos);
if ((!eventHandled) && (isEnabledConstructionState()))
{
_internal->_addVertexAtEnd = true;
_internal->_showCursorPreviewEllipse = true;
deSelectHandles();
if (checkLengthConstrains(1, scenePos))
{
constructPoint(scenePos);
enableSelectedState();
_baseInternal->_lastClickPosition = scenePos;
}
eventHandled = true;
}
else
_internal->_addVertexAtEnd = false;
if (eventHandled)
{
update();
// deactivateButtons();
}
return eventHandled;
}
return false;
}
bool RubberbandMultiEllipseFeature::setCoordinateAction(AbstractEventDispatcher *causer, int x, int y)
{
Q_UNUSED(causer);
if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene))
{
QPointF scenePos(pixel2ScenePosition(x, y));
Rubberband::_internal->_lastClickPosition = scenePos;
if (Rubberband::_internal->_handleList.isEmpty())
{
disenableSelectedState();
disenableMultiSelectedState();
return false;
}
}
return false;
// -----------------------------------
/*
markFailedConstrains(QPointF(), false);
if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene))
{
bool eventHandled = false;
QPointF scenePos(pixel2ScenePosition(x, y));
Rubberband::_internal->_lastClickPosition = scenePos;
if (_internal->_addVertexAtEnd) // isEnabledConstructionState())
{
if (Rubberband::_internal->_handleList.isEmpty())
{
disenableSelectedState();
disenableMultiSelectedState();
return false;
}
if ((!isEnabledSelectedState()) && (_baseInternal->buttonProxyActive(scenePos)))
{
deSelectHandles();
return false;
}
if (checkLengthConstrains(2, scenePos))
{
QPointF deltaMove = scenePos - _baseInternal->_lastClickPosition;
for (auto handle : qAsConst(Rubberband::_internal->_handleList))
{
RubberbandPointHandle *handlePointPtr = dynamic_cast<RubberbandPointHandle *>(handle.get());
if ((handlePointPtr) && ((handlePointPtr->isSelected())))
{
handlePointPtr->setSelection(false);
handlePointPtr->moveCoordinate(deltaMove);
eventHandled = true;
}
}
}
markFailedConstrains(QPointF(), false);
if ((_maximumPointCount > 0) && ((unsigned int)Rubberband::_internal->_handleList.size() >= _maximumPointCount))
{
INFO_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " do Sector now...";
_internal->_showCursorPreviewEllipse = false;
// continue contructionState, so we are able to move the whole object by moving the first handle (center)
// reset _lastClickPosition to improve ButtonPosition
_baseInternal->_lastClickPosition = QPointF(-1,-1);
// remove former selection:
for (SituationMap::RefPointer<RubberbandEllipseHandle> handle : qAsConst(_internal->_ellipseHandleList))
if (handle)
handle->setSelection(false);
deSelectHandles();
update();
// do not complete
// return false;
// completeStateAction(causer, x, y);
}
if (eventHandled)
update();
if (_baseInternal->buttonProxyActive(scenePos))
return false;
updateButtonPosition();
activateButtons();
}
else
{
if (isEnabledSelectedState())
{
bool frameIsSelected(((_internal->_frameHandle != nullptr) && (_internal->_frameHandle->isSelected())));
QPointF deltaMove = scenePos - _baseInternal->_lastClickPosition;
if (!checkLengthConstrains(scenePos, deltaMove))
{
for (auto handle : qAsConst(Rubberband::_internal->_handleList))
{
RubberbandPointHandle *handlePointPtr = dynamic_cast<RubberbandPointHandle *>(handle.get());
if (handlePointPtr)
handlePointPtr->setSelection(false);
}
if (_internal->_frameHandle)
_internal->_frameHandle->setSelection(false);
updateButtonPosition();
activateButtons();
disenableSelectedState();
disenableMultiSelectedState();
markFailedConstrains(QPointF(), false);
return true;
}
markFailedConstrains(QPointF(), false);
for (auto handle : qAsConst(Rubberband::_internal->_handleList))
{
RubberbandPointHandle *handlePointPtr = dynamic_cast<RubberbandPointHandle *>(handle.get());
if ((handlePointPtr) && ((handlePointPtr->isSelected()) || (frameIsSelected)))
{
handlePointPtr->setSelection(false);
handlePointPtr->moveCoordinate(deltaMove);
eventHandled = true;
}
}
if (frameIsSelected)
{
_internal->_frameHandle->setSelection(false);
eventHandled = true;
}
disenableSelectedState();
disenableMultiSelectedState();
}
if (isEnabledConstructionState())
{
_internal->_addVertexAtEnd = true;
_internal->_showCursorPreviewEllipse = true;
updatePreviewEllipse(scenePos);
}
else if (_baseInternal->buttonProxyActive(scenePos))
return false;
if (eventHandled)
update();
updateButtonPosition();
activateButtons();
}
return eventHandled;
}
return false;
*/
}
void RubberbandMultiEllipseFeature::updatePreviewEllipse(const QPointF &scenePosition)
{
DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " START";
// qCritical() << " RubberbandMultiEllipseFeature::updatePreviewEllipse"
// << ", scenePosition " << scenePosition
// << ", _addVertexAtEnd " << _internal->_addVertexAtEnd
// << ", isEnabledConstructionState " << isEnabledConstructionState()
// << " _internal->_handleList.size() " <<Rubberband::_internal->_handleList.size()
// << " _ellipseHandleList.size() " <<_internal->_ellipseHandleList.size();
if (isEnabledConstructionState() && (!Rubberband::_internal->_handleList.isEmpty()) && (_internal->_previewEllipse))
{
// center / first point
RubberbandPointHandle *handlePointPtr = dynamic_cast<RubberbandPointHandle *>(Rubberband::_internal->_handleList.first().get());
if ((handlePointPtr) && (_internal->_addVertexAtEnd))
{
RubberbandHandle::RubberbandCoordinate mapCenter = handlePointPtr->getCoordinate();
RubberbandDrawable::RubberbandCoordinate rbCoordinate(Rubberband::_internal->_mapView, scenePosition);
_internal->_previewEllipse->setVisibility(_internal->_showCursorPreviewEllipse);
_internal->_previewEllipse->setDrawColor(_handleSelectionColor);
int distX = fabs(mapCenter._mapCoordinate.x() - rbCoordinate._mapCoordinate.x());
int distY = fabs(mapCenter._mapCoordinate.y() - rbCoordinate._mapCoordinate.y());
if (_internal->_constrainQuadratic)
distX = distY = std::max(distX, distY);
_internal->_previewEllipse->setRect(QPointF(mapCenter._mapCoordinate.x(), mapCenter._mapCoordinate.y()),
distX, distY, 0, 1);
// sync FrameHandle:
updateFrameHandle(scenePosition);
}
else
{
_internal->_previewEllipse->setVisibility(false);
}
return;
}
else
{
_internal->_previewEllipse = nullptr;
}
}
bool RubberbandMultiEllipseFeature::moveAction(AbstractEventDispatcher *, int x, int y)
{
DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " START";
adjustConnectedPreviewPixmap(x, y);
if (rubberbandIsAlive())
{
QPointF scenePosition(pixel2ScenePosition(x, y));
//------------------------------
// sectorStartHand, _sectorEndHandle
{
if (_internal->_sectorStartHandle.valid())
{
if (_internal->_sectorStartHandle->isPositionInside(scenePosition))
{
_internal->_sectorStartHandle->setSelection(true);
_internal->_frameHandle = nullptr;
update();
return false;
}
else
_internal->_sectorStartHandle->setSelection(false);
}
if (_internal->_sectorEndHandle.valid())
{
if (_internal->_sectorEndHandle->isPositionInside(scenePosition))
{
_internal->_sectorEndHandle->setSelection(true);
_internal->_frameHandle = nullptr;
update();
return false;
}
else
_internal->_sectorEndHandle->setSelection(false);
}
}
//------------------------------
//------------------------------
//------------------------------
//------------------------------
if (_internal->_rotationHandle.valid())
_internal->_rotationHandle->setSelection(_internal->_rotationHandle->isPositionInside(scenePosition));
if (_baseInternal->buttonProxyActive(scenePosition))
return false;
if (!Rubberband::_internal->_mapView)
return false;
if (!isEnabledMultiSelectedState())
deSelectHandles();
RubberbandHandle *handlePtr = pickHandle(x, y);
if (!handlePtr)
{
_internal->_showCursorPreviewEllipse = isEnabledConstructionState();
}
else
{
_internal->_showCursorPreviewEllipse = false;
// set handlePtr selected...
handlePtr->setSelection();
// and updateFrameHandle if not first handle:
RubberbandPointHandle *handlePointPtr = dynamic_cast<RubberbandPointHandle *>(handlePtr);
if (handlePointPtr)
{
if (handlePointPtr->isFirst())
updateFrameHandle({});
else
updateFrameHandle(handlePointPtr->getCoordinate()._sceneCoordinate);
}
}
if (isEnabledConstructionState())
{
if (checkLengthConstrains(1, scenePosition))
{
updatePreviewEllipse(scenePosition);
if (isSignalConnected(QMetaMethod::fromSignal(&RubberbandMultiEllipseFeature::currentCursorPositionChanged)))
{
RubberbandDrawable::RubberbandCoordinate mapCoord(Rubberband::_internal->_mapView, scenePosition);
emit currentCursorPositionChanged(Point3D(mapCoord._mapCoordinate.x(), mapCoord._mapCoordinate.y(), 0.0));
}
}
}
return false;
}
return false;
}
bool RubberbandMultiEllipseFeature::movePickedAction(AbstractEventDispatcher *, int x, int y)
{
DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " START"
<< ", isEnabledSelectedState " << isEnabledSelectedState();
adjustConnectedPreviewPixmap(x, y);
if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene))
{
QPointF scenePos(pixel2ScenePosition(x,y));
updateHandles(scenePos, scenePos-_baseInternal->_lastClickPosition);
if (isEnabledSelectedState())
{
// sectorHandles:
if (!isSectorFixedAngeEnabled())
{
auto updateHandle = [this](RubberbandPointHandle * handle, const RubberbandHandle::RubberbandCoordinate & pos, QPointF & oVector ) -> bool
{
if (!handle)
return false;
if (!handle->isSelected())
return false;
RubberbandPointHandle *handlePointCenter = dynamic_cast<RubberbandPointHandle *>(Rubberband::_internal->_handleList.first().get());
if (!handlePointCenter)
return false;
const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointCenter->getCoordinate();
qreal sinRect = 0;
qreal cosRect = 1;
getSceneRotation( sinRect, cosRect);
qreal cosA = pos._mapCoordinate.x() - mapCenter._mapCoordinate.x();
qreal sinA = pos._mapCoordinate.y() - mapCenter._mapCoordinate.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
RubberbandHandle::RubberbandCoordinate pos(Rubberband::_internal->_mapView, scenePos);
updateHandle( _internal->_sectorStartHandle.get(), pos, _internal->_sectorOStart);
updateHandle( _internal->_sectorEndHandle.get() , pos, _internal->_sectorOEnd );
}
// // return true;
//------------------------------
//------------------------------
//------------------------------
// look for (first) selected PointHandle:
RubberbandPointHandle *selectedPointHandlePtr{nullptr};
for (auto handle : qAsConst(Rubberband::_internal->_handleList))
{
RubberbandPointHandle *handlePointPtr = dynamic_cast<RubberbandPointHandle *>(handle.get());
if (!handlePointPtr)
continue;
if (handlePointPtr->isSelected())
selectedPointHandlePtr = handlePointPtr;
} // for
const bool bFirstHandlePointSelected = (selectedPointHandlePtr && selectedPointHandlePtr->isFirst());
RubberbandHandle::RubberbandCoordinate rcNewCorner;
// nur wenn _constrainQuadratic UND nicht der 'isFirst'/centerPoint
if (_internal->_constrainQuadratic && !bFirstHandlePointSelected)
{
RubberbandPointHandle *handlePointPtr = dynamic_cast<RubberbandPointHandle *>(Rubberband::_internal->_handleList.first().get());
if (!handlePointPtr)
return false;
const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointPtr->getCoordinate();
qreal radiusX, radiusY;
if (getRadiusXY(scenePos, radiusX, radiusY))
radiusX = radiusY = std::max(radiusX, radiusY);
RefPointer<RubberbandHandle> refPtr(selectedPointHandlePtr);
int idx = Rubberband::_internal->_handleList.indexOf(refPtr);
if (idx > 0)
{
if (!checkMinMaxRadiusConstraint(radiusX, radiusY, idx-1))
{
update(); // show/create preview!?
return false;
}
}
// adjusted coord: (prefer topRight)
Esri::ArcGISRuntime::Point newCorner(mapCenter._mapCoordinate.x()+radiusX,
mapCenter._mapCoordinate.y()+radiusY,
mapCenter._mapCoordinate.spatialReference());
rcNewCorner = RubberbandHandle::RubberbandCoordinate(Rubberband::_internal->_mapView, newCorner);
}
QPointF deltaMove(scenePos - _baseInternal->_lastClickPosition);
if (checkLengthConstrains(scenePos, deltaMove))
{
const bool frameIsSelected(((_internal->_frameHandle) && (_internal->_frameHandle->isSelected())));
const bool moveAll = bFirstHandlePointSelected;
_baseInternal->_lastClickPosition = scenePos;
for (auto handle : qAsConst(Rubberband::_internal->_handleList))
{
RubberbandPointHandle *handlePointPtr = dynamic_cast<RubberbandPointHandle *>(handle.get());
if ((handlePointPtr) && ((handlePointPtr->isSelected()) || (frameIsSelected||moveAll)))
{
if (rcNewCorner.isNull())
handlePointPtr->moveCoordinate(deltaMove);
else
handlePointPtr->setCoordinate(rcNewCorner);
}
}
updatePreviewEllipse(scenePos);
update();
}
return true;
}
}
return false;
}
bool RubberbandMultiEllipseFeature::deleteAction(AbstractEventDispatcher *, int x, int y)
{
if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene))
{
if (((unsigned int)Rubberband::_internal->_handleList.size() > _minimumPointCount) && (isEnabledSelectedState()))
{
QList<SituationMap::RefPointer<RubberbandHandle>> savedHandleList= Rubberband::_internal->_handleList;
QPointF scenePosition = pixel2ScenePosition(x, y);
QList<SituationMap::RefPointer<RubberbandHandle>>::iterator handleIter = Rubberband::_internal->_handleList.begin();
while (handleIter != Rubberband::_internal->_handleList.end())
{
if (handleIter->get()->isPositionInside(scenePosition))
{
handleIter = Rubberband::_internal->_handleList.erase(handleIter);
updateFrameHandle({});
continue;
}
++handleIter;
} // while handleIter
if (!checkLengthConstrains(scenePosition, QPointF(0, 0)))
{
// constrain failed, do not delete yet
Rubberband::_internal->_handleList = savedHandleList;
}
update();
activateButtons();
disenableSelectedState();
disenableMultiSelectedState();
}
return true;
}
return false;
}
bool RubberbandMultiEllipseFeature::contextMenuAction(AbstractEventDispatcher *, int, int)
{
return false;
}
bool RubberbandMultiEllipseFeature::completeStateAction(AbstractEventDispatcher *causer, int x, int y)
{
markFailedConstrains(QPointF(), false);
if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene))
{
if (isEnabledConstructionState())
{
if (!_finishAfterConstruction)
{
if ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount)
{
enableModificationState();
_internal->_previewEllipse = nullptr; // remove preview
}
update();
return true;
}
}
if ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount)
return applyAndEndAction(causer, x, y);
}
return false;
}
bool RubberbandMultiEllipseFeature::undoAction(AbstractEventDispatcher * /*causer*/, int, int)
{
if (rubberbandIsAlive())
{
if (!Rubberband::_internal->_handleList.empty())
{
if (isEnabledConstructionState())
{
Rubberband::_internal->_handleList.pop_back();
if (!_internal->_ellipseHandleList.empty())
_internal->_ellipseHandleList.pop_back();
if (_internal->_previewEllipse)
_internal->_previewEllipse->setVisibility(false);
update();
activateButtons();
}
}
return true;
}
return false;
}
bool RubberbandMultiEllipseFeature::applyAndEndAction(AbstractEventDispatcher *causer, int x, int y)
{
if ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount)
Rubberband::applyAndEndAction(causer, x, y);
else abortAction(causer, x, y);
return true;
}
void RubberbandMultiEllipseFeature::constrainQuadratic(bool on)
{
_internal->_constrainQuadratic = on;
}
void RubberbandMultiEllipseFeature::activateButtons()
{
_baseInternal->_hasUndoButton = isEnabledConstructionState();
_baseInternal->_applyIsPossible = ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount);
_baseInternal->_undoIsPossible = (isEnabledConstructionState() && ((unsigned int)Rubberband::_internal->_handleList.size() > _minimumPointCount));
Rubberband::activateButtons();
}
void RubberbandMultiEllipseFeature::update()
{
DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " START";
Rubberband::update();
// update _ellipseHandleList according _handleList: (context modification)
RubberbandHandle::RubberbandCoordinate centerCoord;
if (!Rubberband::_internal->_handleList.isEmpty())
{
RubberbandPointHandle *handlePointPtr = dynamic_cast<RubberbandPointHandle *>(Rubberband::_internal->_handleList.first().get());
centerCoord = handlePointPtr->getCoordinate();
}
//
// adjust EllipseHandles according size, selection, zOrder:
//
RubberbandPointHandle *selectedHandlePointPtrNotCenter{nullptr};
int handleIdxMax = 0;
const int handleListSize = Rubberband::_internal->_handleList.size();
for (int handleIdx=0; handleIdx < handleListSize; ++handleIdx)
{
if (handleIdx >= Rubberband::_internal->_handleList.size())
continue;
handleIdxMax = handleIdx;
RubberbandPointHandle *pointHandle = dynamic_cast<RubberbandPointHandle *>(Rubberband::_internal->_handleList[handleIdx].get());
if (!pointHandle)
continue;
if (pointHandle->isSelected() && handleIdx>0)
selectedHandlePointPtrNotCenter = pointHandle;
if (handleIdx==0)
continue;
// --- now we have finished the first iteration ---
// 2 pointHandles are required for an elli:
SituationMap::RefPointer<RubberbandEllipseHandle> ellipseHandle = _internal->_ellipseHandleList[handleIdx-1];
if (!ellipseHandle)
continue;
const RubberbandHandle::RubberbandCoordinate &curCoord = pointHandle->getCoordinate();
qreal distX = fabs(curCoord._mapCoordinate.x() - centerCoord._mapCoordinate.x());
qreal distY = fabs(curCoord._mapCoordinate.y() - centerCoord._mapCoordinate.y());
if (_internal->_constrainQuadratic)
distX = distY = std::max(distX, distY);
if (isSectorEnabled())
{
// _internal->_sectorOStart + _internal->_oVec
QPointF sectorStartRotated = getRotatedPoint(_internal->_sectorOStart);
QPointF sectorEndRotated = getRotatedPoint(_internal->_sectorOEnd);
ellipseHandle->setRect(centerCoord, distX, distY, 0, 1, sectorStartRotated, sectorEndRotated);
}
else
ellipseHandle->setRect(centerCoord, distX, distY, 0, 1);
// transfer selection:
ellipseHandle->setSelection(pointHandle->isSelected());
// update zOrder
ellipseHandle->setZValue(handleListSize-handleIdx);
} // for
// Maybe there has ist one RubberbandPointHandle deleted (deleteAction)
// remove superfluous ellipse handles
// like: 2 RubberbandPointHandle (handleIdxMax==1) -> 1 RubberbandEllipseHandle
while (_internal->_ellipseHandleList.size() > handleIdxMax)
_internal->_ellipseHandleList.removeLast();
if (selectedHandlePointPtrNotCenter)
updateFrameHandle(selectedHandlePointPtrNotCenter->getCoordinate()._sceneCoordinate);
// transfer selection:
if (_internal->_frameHandle && selectedHandlePointPtrNotCenter)
_internal->_frameHandle->setSelection( selectedHandlePointPtrNotCenter->isSelected() );
updateSectorHandles();
updateRotationHandle();
DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " END";
}
void RubberbandMultiEllipseFeature::updateFrameHandle(const QPointF &sceneCoordinate)
{
const RubberbandDrawable::RubberbandCoordinate vertex(Rubberband::_internal->_mapView, sceneCoordinate);
if (//isEnabledModificationState() &&
(!vertex.isNull()) &&
!Rubberband::_internal->_handleList.isEmpty()
)
{
if (!_internal->_frameHandle)
{
_internal->_frameHandle = SituationMap::RefPointer<RubberbandFrameHandle>(new RubberbandFrameHandle(Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, _frameHandleMargin));
_internal->_frameHandle->setSelectable(false);
_internal->_frameHandle->setDrawColor(_frameColor);
_internal->_frameHandle->setFillColor(_frameFillColor);
_internal->_frameHandle->setSelectionColor(_frameSelectionColor);
_internal->_frameHandle->updateFrameSceneMargin(_frameHandleMargin);
_internal->_frameHandle->drawDisabled((_frameHandleMargin < 0));
}
} else {
_internal->_frameHandle = nullptr;
}
if (_internal->_frameHandle)
{
const RubberbandPointHandle *handlePointPtr = dynamic_cast<RubberbandPointHandle *>(Rubberband::_internal->_handleList.first().get());
if (!handlePointPtr)
return;
const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointPtr->getCoordinate();
qreal radiusX, radiusY;
if (!getRadiusXY(sceneCoordinate, radiusX, radiusY))
return;
if (_internal->_constrainQuadratic)
radiusX = radiusY = std::max(radiusX, radiusY);
RubberbandHandle::RubberbandCoordinate rcNewCorner1(Rubberband::_internal->_mapView,
Esri::ArcGISRuntime::Point(mapCenter._mapCoordinate.x()+(radiusX),
mapCenter._mapCoordinate.y()+(radiusY),
mapCenter._mapCoordinate.spatialReference()));
RubberbandHandle::RubberbandCoordinate rcNewCorner2(Rubberband::_internal->_mapView,
Esri::ArcGISRuntime::Point(mapCenter._mapCoordinate.x()-(radiusX),
mapCenter._mapCoordinate.y()-(radiusY),
mapCenter._mapCoordinate.spatialReference()));
_internal->_frameHandle->updateFrameShape({rcNewCorner1, rcNewCorner2});
}
}
void RubberbandMultiEllipseFeature::getSceneRotation(qreal &sinA, qreal &cosA) const
{
sinA = -_internal->_oVec.y();
cosA = _internal->_oVec.x();
}
Esri::ArcGISRuntime::Point addOffsetToMapCoordinate(const Esri::ArcGISRuntime::Point &mapCoordinate,
const QPointF &offset)
{
return Esri::ArcGISRuntime::Point(mapCoordinate.x() + offset.x(),
mapCoordinate.y() + offset.y(),
mapCoordinate.spatialReference());
}
void RubberbandMultiEllipseFeature::updateSectorHandles()
{
if (Rubberband::_internal->_handleList.isEmpty())
return;
if ((unsigned int)Rubberband::_internal->_handleList.size() < _minimumPointCount)
return;
if (!isSectorEnabled())
return;
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 ); };
const QPointF &biggestRadius = getBiggestRadius();
qreal a = biggestRadius.x(); // getWidth() * 0.5;
qreal b = biggestRadius.y(); // 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();
RubberbandPointHandle *handlePointCenter = dynamic_cast<RubberbandPointHandle *>(Rubberband::_internal->_handleList.first().get());
if (!handlePointCenter)
return;
const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointCenter->getCoordinate();
// const Esri::ArcGISRuntime::Point &mapStartP = convert( startP, Rubberband::_internal->_mapView->map()->spatialReference() );
// const Esri::ArcGISRuntime::Point &mapEndP = convert( endP, Rubberband::_internal->_mapView->map()->spatialReference() );
const Esri::ArcGISRuntime::Point &mapStartP = addOffsetToMapCoordinate(mapCenter._mapCoordinate, startP);
const Esri::ArcGISRuntime::Point &mapEndP = addOffsetToMapCoordinate(mapCenter._mapCoordinate, endP);
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((!isSectorFixedAngeEnabled())/*_internal->_sectorEnabled*/);
_internal->_sectorStartHandle->setDrawColor(Qt::red);
_internal->_sectorStartHandle->setFillColor( _handleFillColor );
_internal->_sectorStartHandle->setSelectionColor(_handleSelectionColor);
_internal->_sectorStartHandle->setSelectable(true);
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(!isSectorFixedAngeEnabled()/*_internal->_sectorEnabled*/);
_internal->_sectorEndHandle->setDrawColor(Qt::red);
_internal->_sectorEndHandle->setFillColor( _handleFillColor );
_internal->_sectorEndHandle->setSelectionColor(_handleSelectionColor);
_internal->_sectorEndHandle->setSelectable(true);
}
void RubberbandMultiEllipseFeature::updateRotationHandle()
{
DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " START"
<< ", isSectorFixedAngeEnabled()=" << isSectorFixedAngeEnabled()
<< ", _handleList.size=" << Rubberband::_internal->_handleList.size();
if (!isSectorEnabled() || !isSectorFixedAngeEnabled() || (Rubberband::_internal->_handleList.size()<2))
{
_internal->_rotationHandle = nullptr; // destroy
}
else
{
static QPixmap *s_icon = nullptr;
if (!s_icon)
s_icon = new QPixmap( QPixmap( winkelIcon_xpm ).transformed(QTransform::fromScale(0.5,0.5)) );
if (!_internal->_rotationHandle)
{
_internal->_rotationHandleSize = std::max( s_icon->width(), s_icon->height() ) * 0.5;
_internal->_rotationHandle = new RubberbandPointHandle(Rubberband::_internal->_mapView
, Rubberband::_internal->_mapScene
, QPointF(100, 100) // , _internal->getScenePositionRotationHandle(),
, *s_icon );
_internal->_rotationHandle->setDrawColor(_handleDrawColor);
_internal->_rotationHandle->setFillColor(_handleFillColor);
_internal->_rotationHandle->setSelectionColor(_handleSelectionColor);
}
_internal -> _rotationHandle->setSceneOffset(getSceneOffsetRotationHandle( (_frameHandleMargin > 0) ? _frameHandleMargin : 0 ) );
_internal -> _rotationHandle->updateCoordinate(getScenePositionRotationHandle());
}
DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " END";
}
QPointF RubberbandMultiEllipseFeature::getScenePositionRotationHandle() const
{
if (Rubberband::_internal->_handleList.size()<2)
return {};
RubberbandPointHandle *handlePointCenter = dynamic_cast<RubberbandPointHandle *>(Rubberband::_internal->_handleList.first().get());
if (!handlePointCenter)
return {};
const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointCenter->getCoordinate();
const QPointF &biggestRadius = getBiggestRadius();
Esri::ArcGISRuntime::Point handlePosPoint(mapCenter._mapCoordinate.x() + 0,
mapCenter._mapCoordinate.y() + biggestRadius.y(),
mapCenter._mapCoordinate.spatialReference());
RubberbandHandle::RubberbandCoordinate handlePos(Rubberband::_internal->_mapView, handlePosPoint);
QPointF scenePoint (handlePos._sceneCoordinate);
scenePoint.ry() -= _internal->_rotationHandleSize; // keep center of sceneCoordSystem in mind
qreal sinRect = 0;
qreal cosRect = 1;
getSceneRotation(sinRect, cosRect);
scenePoint = MathUtils::rotate(scenePoint, mapCenter._sceneCoordinate, -sinRect, cosRect); // be carefull the scene coordinate system is contra clockwise -- sinA must be negated
return scenePoint;
}
QPointF RubberbandMultiEllipseFeature::getSceneOffsetRotationHandle(qreal margin)
{
qreal sinPhi = _internal->_oVec.y(); // * _mapOVec.x() + _oVec.x() * _mapOVec.y(); // Addition-Theorem
qreal cosPhi = _internal->_oVec.x(); // * _mapOVec.x() - _oVec.y() * _mapOVec.y();
return rotateVertex( QPointF(0,0), 0.0,-(margin+_internal->_rotationHandleSize), sinPhi, cosPhi );
}
}
} // namespace SituationMap::RB