Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Vertex tool improvement #5797

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions src/core/vertexmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ VertexModel::VertexModel( QObject *parent )
connect( this, &VertexModel::vertexCountChanged, this, &VertexModel::updateCanPreviousNextVertex );
connect( this, &VertexModel::currentVertexIndexChanged, this, &VertexModel::updateCanPreviousNextVertex );
connect( this, &VertexModel::currentVertexIndexChanged, this, &VertexModel::updateCanRemoveVertex );
connect( this, &VertexModel::currentVertexIndexChanged, this, &VertexModel::updateAngleFound );
}

void VertexModel::setMapSettings( QgsQuickMapSettings *mapSettings )
Expand Down Expand Up @@ -562,6 +563,11 @@ void VertexModel::next()
}
}

QgsPoint VertexModel::getPoint( int index )
{
return mVertices[index].point;
}

void VertexModel::addVertexNearestToPosition( const QgsPoint &mapPoint )
{
double closestDistance = std::numeric_limits<double>::max();
Expand Down Expand Up @@ -685,6 +691,31 @@ QgsPoint VertexModel::currentPoint() const
{
return mVertices.value( mCurrentIndex ).point;
}
// Function to calculate the angle between two vectors
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, we don't add comments above functions, we document in the .h[eader] :)

double VertexModel::calculateAngle( const QgsPoint &a, const QgsPoint &b, const QgsPoint &c )
{
// Create vectors AB and BC
double AB_x = b.x() - a.x();
double AB_y = b.y() - a.y();
double BC_x = c.x() - b.x();
double BC_y = c.y() - b.y();

// Calculate dot product
double dotProduct = AB_x * BC_x + AB_y * BC_y;

// Calculate magnitudes
double magnitudeAB = std::sqrt( AB_x * AB_x + AB_y * AB_y );
double magnitudeBC = std::sqrt( BC_x * BC_x + BC_y * BC_y );

// Calculate cosine of the angle
double cosTheta = dotProduct / ( magnitudeAB * magnitudeBC );

// Calculate the angle in radians
double angle = std::acos( cosTheta );

// Convert to degrees
return std::abs( angle * ( 180.0 / M_PI ) - 180 );
}

void VertexModel::setCurrentPoint( const QgsPoint &point )
{
Expand All @@ -696,6 +727,25 @@ void VertexModel::setCurrentPoint( const QgsPoint &point )

Vertex &vertex = mVertices[mCurrentIndex];

// Calculate angles between edges

int startPoint = mCurrentIndex - 2;
if ( startPoint < 0 )
{
startPoint = vertexCount() + startPoint;
}
int endPoint = mCurrentIndex + 2;
if ( endPoint >= vertexCount() )
{
endPoint = endPoint - vertexCount();
}

double angle = calculateAngle( mVertices[startPoint].point, mVertices[mCurrentIndex].point, mVertices[endPoint].point );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, as discussed yesterday, here I think we shouldn't calculate the angle but rather the point location of the point that'd form the angle we're looking for.

Then, we will snap to that point whenever we are close to it.


qDebug() << "--------- angle = " << angle;

setAngleFonud( angle > snapToCommonAngleDegrees() - 1 && angle < snapToCommonAngleDegrees() + 1 );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed this yesterday. This wouldn't really work as a/ we wouldn't get a perfect coordinate snapped to angle in any case, and b/ we'd allow for angle 89 and 91 😱 😉


if ( mMapSettings && vertex.point.distance( point ) / mMapSettings->mapSettings().mapUnitsPerPixel() < 1 )
return;

Expand Down Expand Up @@ -799,6 +849,11 @@ bool VertexModel::dirty() const
return mDirty;
}

bool VertexModel::angleFonud() const
{
return mAngleFonud;
}

bool VertexModel::canRemoveVertex()
{
return mCanRemoveVertex;
Expand Down Expand Up @@ -881,6 +936,15 @@ void VertexModel::setDirty( bool dirty )
emit dirtyChanged();
}

void VertexModel::setAngleFonud( bool angleFonud )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This becomes a int commented int he header. But also, keep the getter and setter functions together in the source code (ATM they are disjointed and it just makes the code harder to skim through).

{
if ( mAngleFonud == angleFonud )
return;

mAngleFonud = angleFonud;
emit angleFonudChanged();
}

void VertexModel::updateCanRemoveVertex()
{
bool canRemoveVertex = false;
Expand Down Expand Up @@ -1032,3 +1096,24 @@ void VertexModel::setEditingMode( VertexModel::EditingMode mode )

emit editingModeChanged();
}

void VertexModel::updateAngleFound()
{
if ( currentVertexIndex() == -1 )
{
setAngleFonud( false );
}
}

int VertexModel::snapToCommonAngleDegrees() const
{
return mSnapToCommonAngleDegrees;
}

void VertexModel::setSnapToCommonAngleDegrees( int snapToCommonAngleDegrees )
{
if ( mSnapToCommonAngleDegrees == snapToCommonAngleDegrees )
return;
mSnapToCommonAngleDegrees = snapToCommonAngleDegrees;
emit snapToCommonAngleDegreesChanged();
}
20 changes: 20 additions & 0 deletions src/core/vertexmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ class QFIELD_CORE_EXPORT VertexModel : public QAbstractListModel
Q_PROPERTY( int ringCount READ ringCount NOTIFY ringCountChanged )
//! determines if the model has changes
Q_PROPERTY( bool dirty READ dirty NOTIFY dirtyChanged )

Q_PROPERTY( bool angleFonud READ angleFonud NOTIFY angleFonudChanged )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, this should be an int property called snappedAngle.

Also, don't forget documentation.


//! determines if the model allows editing the geometry
Q_PROPERTY( bool editingAllowed READ editingAllowed )
//! determines if one can remove current vertex
Expand All @@ -70,6 +73,9 @@ class QFIELD_CORE_EXPORT VertexModel : public QAbstractListModel
*/
Q_PROPERTY( int currentVertexIndex READ currentVertexIndex WRITE setCurrentVertexIndex NOTIFY currentVertexIndexChanged )

Q_PROPERTY( int snapToCommonAngleDegrees READ snapToCommonAngleDegrees WRITE setSnapToCommonAngleDegrees NOTIFY snapToCommonAngleDegreesChanged )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation



/**
* The geometry in layer coordinates
*/
Expand Down Expand Up @@ -187,6 +193,8 @@ class QFIELD_CORE_EXPORT VertexModel : public QAbstractListModel
//! next vertex or segment
Q_INVOKABLE void next();

Q_INVOKABLE QgsPoint getPoint( int index );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to expose this to QML? Should it be a private function?


//! Selects the vertex at the given screen \a point within a given \a threshold
Q_INVOKABLE void selectVertexAtPosition( const QPointF &point, double threshold, bool autoInsert = true );

Expand Down Expand Up @@ -223,6 +231,7 @@ class QFIELD_CORE_EXPORT VertexModel : public QAbstractListModel

//! \copydoc dirty
bool dirty() const;
bool angleFonud() const;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation


//! \copydoc canRemoveVertex
bool canRemoveVertex();
Expand Down Expand Up @@ -265,6 +274,10 @@ class QFIELD_CORE_EXPORT VertexModel : public QAbstractListModel

bool canUndo();

double calculateAngle( const QgsPoint &a, const QgsPoint &b, const QgsPoint &c );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we still use this function, let's move it to GeometryUtils

int snapToCommonAngleDegrees() const;
void setSnapToCommonAngleDegrees( int snapToCommonAngleDegrees );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentaton


signals:
//! \copydoc editingMode
void editingModeChanged();
Expand All @@ -278,6 +291,7 @@ class QFIELD_CORE_EXPORT VertexModel : public QAbstractListModel
void ringCountChanged();
//! \copydoc dirty
void dirtyChanged();
void angleFonudChanged();
//! \copydoc canRemoveVertex
void canRemoveVertexChanged();
//! \copydoc canAddVertex
Expand All @@ -301,15 +315,19 @@ class QFIELD_CORE_EXPORT VertexModel : public QAbstractListModel
//! Emitted when the history has been modified
void historyChanged();

void snapToCommonAngleDegreesChanged();

private:
void refreshGeometry();
//! Add the candidates of new vertices (extending or segment)
//! This will not emit the reset signals, it's up to the caller to do so
void createCandidates();
void setDirty( bool dirty );
void setAngleFonud( bool angleFonud );
void updateCanRemoveVertex();
void updateCanAddVertex();
void updateCanPreviousNextVertex();
void updateAngleFound();
void setGeometryType( const Qgis::GeometryType &geometryType );

QList<Vertex> mVertices;
Expand All @@ -322,6 +340,7 @@ class QFIELD_CORE_EXPORT VertexModel : public QAbstractListModel
QgsCoordinateTransform mTransform = QgsCoordinateTransform();
bool mIsMulti = false;
bool mDirty = false;
bool mAngleFonud = false;

QVector<QgsPoint> mVerticesDeleted;

Expand Down Expand Up @@ -350,6 +369,7 @@ class QFIELD_CORE_EXPORT VertexModel : public QAbstractListModel
bool mHistoryTraversing = false;

friend class VertexModelTest;
int mSnapToCommonAngleDegrees;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep the friend class line at the very bottom of the class definition, and make sure you initialize your members (i.e. in this case int mSnapToCommonAngleDegrees = 0;

};

Q_DECLARE_METATYPE( VertexModel::Vertex );
Expand Down
116 changes: 115 additions & 1 deletion src/qml/CoordinateLocator.qml
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,37 @@ Item {
inputCoordinate: {
// Get the current crosshair location in screen coordinates. If `undefined`, then we use the center of the screen as input point.
const location = sourceLocation === undefined ? Qt.point(locator.width / 2, locator.height / 2) : sourceLocation;
if (snapToCommonAngleButton.isSnapToCommonAngleEnabled) {
if (snapToCommonAngleButton.isSnapToCommonAngleEnabled && geometryEditingVertexModel.angleFonud) {
const vertexCount = geometryEditingVertexModel.vertexCount;
const currentIndex = geometryEditingVertexModel.currentVertexIndex;
let startPoint = currentIndex - 2;
if (startPoint < 0) {
startPoint = vertexCount + startPoint;
}
let endPoint = currentIndex + 2;
if (endPoint >= vertexCount) {
endPoint = endPoint - vertexCount;
}
const start = mapSettings.coordinateToScreen(geometryEditingVertexModel.currentPoint);
const p1 = mapSettings.coordinateToScreen(geometryEditingVertexModel.getPoint(startPoint));
const p2 = mapSettings.coordinateToScreen(geometryEditingVertexModel.getPoint(endPoint));
const intersections = getIntersectionPoints(start, p1, p2, 1000, 1000);
vertexSnapToCommonAngleLines.endCoordX1 = intersections.x1 || 0;
vertexSnapToCommonAngleLines.endCoordY1 = intersections.y1 || 0;
vertexSnapToCommonAngleLines.endCoordX2 = intersections.x2 || 0;
vertexSnapToCommonAngleLines.endCoordY2 = intersections.y2 || 0;
} else if (snapToCommonAngleButton.isSnapToCommonAngleEnabled) {
locator.commonAngleInDegrees = getCommonAngleInDegrees(location, locator.rubberbandModel, snapToCommonAngleButton.snapToCommonAngleDegrees, snapToCommonAngleButton.isSnapToCommonAngleRelative);
const coords = calculateSnapToAngleLineEndCoords(snappedPoint, locator.commonAngleInDegrees, snapToCommonAngleButton.isSnapToCommonAngleRelative, 1000);
snapToCommonAngleLines.endCoordX = coords.x || 0;
snapToCommonAngleLines.endCoordY = coords.y || 0;
vertexSnapToCommonAngleLines.reset();
return snapPointToCommonAngle(location, locator.rubberbandModel, locator.commonAngleInDegrees, snapToCommonAngleButton.isSnapToCommonAngleRelative);
} else {
locator.commonAngleInDegrees = null;
snapToCommonAngleLines.endCoordX = 0;
snapToCommonAngleLines.endCoordY = 0;
vertexSnapToCommonAngleLines.reset();
}
return location;
}
Expand Down Expand Up @@ -280,6 +301,86 @@ Item {
}
}

Shape {
id: vertexSnapToCommonAngleLines

property double endCoordX1: 0
property double endCoordY1: 0
property double endCoordX2: 0
property double endCoordY2: 0

visible: (endCoordX1 + endCoordY1 + endCoordX2 + endCoordY2) != 0
width: parent.width
height: parent.height
anchors.centerIn: parent
opacity: 0.5

function reset() {
vertexSnapToCommonAngleLines.endCoordX1 = 0;
vertexSnapToCommonAngleLines.endCoordY1 = 0;
vertexSnapToCommonAngleLines.endCoordX2 = 0;
vertexSnapToCommonAngleLines.endCoordY2 = 0;
}

ShapePath {
id: firstLineOuter
strokeWidth: 4
strokeColor: "#fff"
strokeStyle: ShapePath.DashLine
dashPattern: [5, 3]
startX: snappedPoint.x
startY: snappedPoint.y

PathLine {
x: vertexSnapToCommonAngleLines.endCoordX1
y: vertexSnapToCommonAngleLines.endCoordY1
}
}
// inner line
ShapePath {
strokeWidth: firstLineOuter.strokeWidth / 2
strokeColor: "#000"
strokeStyle: ShapePath.DashLine
dashPattern: firstLineOuter.dashPattern.map(v => v * 2)
startX: snappedPoint.x
startY: snappedPoint.y

PathLine {
x: vertexSnapToCommonAngleLines.endCoordX1
y: vertexSnapToCommonAngleLines.endCoordY1
}
}

ShapePath {
id: secondLineOuter
strokeWidth: 4
strokeColor: "#fff"
strokeStyle: ShapePath.DashLine
dashPattern: [5, 3]
startX: snappedPoint.x
startY: snappedPoint.y

PathLine {
x: vertexSnapToCommonAngleLines.endCoordX2
y: vertexSnapToCommonAngleLines.endCoordY2
}
}
// inner line
ShapePath {
strokeWidth: secondLineOuter.strokeWidth / 2
strokeColor: "#000"
strokeStyle: ShapePath.DashLine
dashPattern: secondLineOuter.dashPattern.map(v => v * 2)
startX: snappedPoint.x
startY: snappedPoint.y

PathLine {
x: vertexSnapToCommonAngleLines.endCoordX2
y: vertexSnapToCommonAngleLines.endCoordY2
}
}
}

ParallelAnimation {
id: flashAnimation

Expand Down Expand Up @@ -439,4 +540,17 @@ Item {
"y": y2
};
}

function getIntersectionPoints(centeralPoint, pointB, pointC) {
const xDiffAB = centeralPoint.x - pointB.x;
const yDiffAB = centeralPoint.y - pointB.y;
const xDiffAC = centeralPoint.x - pointC.x;
const yDiffAC = centeralPoint.y - pointC.y;
return {
"x1": centeralPoint.x - (10 * xDiffAB),
"y1": centeralPoint.y - (10 * yDiffAB),
"x2": centeralPoint.x - (10 * xDiffAC),
"y2": centeralPoint.y - (10 * yDiffAC)
};
}
}
1 change: 1 addition & 0 deletions src/qml/qgismobileapp.qml
Original file line number Diff line number Diff line change
Expand Up @@ -4000,6 +4000,7 @@ ApplicationWindow {
currentPoint: coordinateLocator.currentCoordinate
mapSettings: mapCanvas.mapSettings
isHovering: mapCanvasMap.hovered
snapToCommonAngleDegrees: snapToCommonAngleButton.snapToCommonAngleDegrees
}

ScreenLocker {
Expand Down
Loading