diff --git a/app/src/tooloptionwidget.cpp b/app/src/tooloptionwidget.cpp index efa434a338..676d6192fa 100644 --- a/app/src/tooloptionwidget.cpp +++ b/app/src/tooloptionwidget.cpp @@ -128,8 +128,12 @@ void ToolOptionWidget::makeConnectionToEditor(Editor* editor) connect(ui->showInfoBox, &QCheckBox::clicked, toolManager, &ToolManager::setShowSelectionInfo); + connect(ui->snapAngleBox, &QCheckBox::clicked, toolManager, &ToolManager::setSnapAngleBox); + connect(ui->snapAngleDegrees, static_cast(&QSpinBox::valueChanged), toolManager, &ToolManager::setSnapAngleDegrees); + connect(toolManager, &ToolManager::toolChanged, this, &ToolOptionWidget::onToolChanged); connect(toolManager, &ToolManager::toolPropertyChanged, this, &ToolOptionWidget::onToolPropertyChanged); + } void ToolOptionWidget::onToolPropertyChanged(ToolType, ToolPropertyType ePropertyType) @@ -158,6 +162,7 @@ void ToolOptionWidget::onToolPropertyChanged(ToolType, ToolPropertyType ePropert case USEBUCKETFILLEXPAND: break; case BUCKETFILLLAYERREFERENCEMODE: break; case FILL_MODE: break; + case SNAPTOANGLE: (p.snapAngleState); break; default: Q_ASSERT(false); break; @@ -201,6 +206,8 @@ void ToolOptionWidget::setVisibility(BaseTool* tool) ui->inpolLevelsCombo->setVisible(tool->isPropertyEnabled(STABILIZATION)); ui->fillContourBox->setVisible(tool->isPropertyEnabled(FILLCONTOUR)); ui->showInfoBox->setVisible(tool->isPropertyEnabled(SHOWSELECTIONINFO)); + ui->snapAngleBox->setVisible(tool->isPropertyEnabled(SNAPTOANGLE)); + ui->snapAngleDegrees->setVisible(tool->isPropertyEnabled(SNAPTOANGLE)); auto currentLayerType = editor()->layers()->currentLayer()->type(); auto propertyType = editor()->tools()->currentTool()->type(); @@ -370,6 +377,18 @@ void ToolOptionWidget::setShowSelectionInfo(bool showSelectionInfo) ui->showInfoBox->setChecked(showSelectionInfo); } +void ToolOptionWidget::setSnapAngleBox(bool enabled) +{ + QSignalBlocker blocker(ui->snapAngleBox); + ui->snapAngleBox->setChecked(enabled); +} + +void ToolOptionWidget::setSnapAngleDegrees(int degrees) +{ + QSignalBlocker blocker(ui->snapAngleDegrees); + ui->snapAngleDegrees->setValue(degrees); +} + void ToolOptionWidget::disableAllOptions() { ui->sizeSlider->hide(); @@ -379,6 +398,8 @@ void ToolOptionWidget::disableAllOptions() ui->useFeatherBox->hide(); ui->useBezierBox->hide(); ui->useClosedPathBox->hide(); + ui->snapAngleBox->hide(); + ui->snapAngleDegrees->hide(); ui->usePressureBox->hide(); ui->makeInvisibleBox->hide(); ui->preserveAlphaBox->hide(); diff --git a/app/src/tooloptionwidget.h b/app/src/tooloptionwidget.h index baccec01fb..9393abb0dd 100644 --- a/app/src/tooloptionwidget.h +++ b/app/src/tooloptionwidget.h @@ -67,6 +67,8 @@ public slots: void setBezier(bool); void setClosedPath(bool); void setShowSelectionInfo(bool); + void setSnapAngleBox(bool); + void setSnapAngleDegrees(int); void disableAllOptions(); void setVisibility(BaseTool*); diff --git a/app/ui/tooloptions.ui b/app/ui/tooloptions.ui index ceae06cc47..a5f4f4cce1 100644 --- a/app/ui/tooloptions.ui +++ b/app/ui/tooloptions.ui @@ -6,8 +6,8 @@ 0 0 - 140 - 355 + 375 + 569 @@ -35,10 +35,10 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame - QFrame::Plain + QFrame::Shadow::Plain true @@ -48,8 +48,8 @@ 0 0 - 153 - 340 + 375 + 569 @@ -245,12 +245,32 @@ + + + + Snap To Angle: + + + + + + + 1 + + + 90 + + + 45 + + + - Qt::Vertical + Qt::Orientation::Vertical @@ -305,10 +325,10 @@ - Qt::Vertical + Qt::Orientation::Vertical - QSizePolicy::Expanding + QSizePolicy::Policy::Expanding diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 647898e7d7..81fd2a8a4b 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -198,7 +198,8 @@ void ScribbleArea::onTileCreated(TiledBuffer* tiledBuffer, Tile* tile) void ScribbleArea::updateFrame() { - if (currentTool()->isActive() && currentTool()->isDrawingTool()) { + if (currentTool()->type() == POLYLINE) { update();} + else if (currentTool()->isActive() && currentTool()->isDrawingTool()) { return; } @@ -259,7 +260,8 @@ void ScribbleArea::invalidateOnionSkinsCacheAround(int frameNumber) void ScribbleArea::invalidateAllCache() { - if (currentTool()->isDrawingTool() && currentTool()->isActive()) { return; } + if (currentTool()->type() == POLYLINE) { update(); } + else if (currentTool()->isDrawingTool() && currentTool()->isActive()) { return; } QPixmapCache::clear(); mPixmapCacheKeys.clear(); diff --git a/core_lib/src/managers/toolmanager.cpp b/core_lib/src/managers/toolmanager.cpp index 19e98b1334..95688418ef 100644 --- a/core_lib/src/managers/toolmanager.cpp +++ b/core_lib/src/managers/toolmanager.cpp @@ -434,3 +434,18 @@ void ToolManager::clearTemporaryTool() mTemporaryTriggerMouseButtons = Qt::NoButton; emit toolChanged(currentTool()->type()); } + +void ToolManager::setSnapAngleBox(bool enabled) +{ + if (currentTool() == nullptr) return; + currentTool()->properties.snapAngleState = enabled; + emit toolPropertyChanged(currentTool()->type(), SNAPTOANGLE); +} + +void ToolManager::setSnapAngleDegrees(int degrees) +{ + if (currentTool() == nullptr) return; + currentTool()->properties.snapAngleDegrees = degrees; + emit toolPropertyChanged(currentTool()->type(), SNAPDEGREE); +} + diff --git a/core_lib/src/managers/toolmanager.h b/core_lib/src/managers/toolmanager.h index 7713b1c4d3..0547b91bfe 100644 --- a/core_lib/src/managers/toolmanager.h +++ b/core_lib/src/managers/toolmanager.h @@ -84,6 +84,8 @@ public slots: void resetCameraPath(); void setCameraPathDotColor(int); void resetCameraTransform(CameraFieldOption option); + void setSnapAngleBox(bool); + void setSnapAngleDegrees(int); /// Layer mode will be enforced by the the choice the reference mode selected. /// @return Returns true if reference mode is ``current layer`, otherwise false. diff --git a/core_lib/src/tool/basetool.h b/core_lib/src/tool/basetool.h index 77c8ab1c07..ee886d68ee 100644 --- a/core_lib/src/tool/basetool.h +++ b/core_lib/src/tool/basetool.h @@ -59,6 +59,8 @@ class Properties bool showSelectionInfo = true; bool cameraShowPath = true; DotColorType cameraPathDotColorType = DotColorType::RED; + bool snapAngleState = false; + double snapAngleDegrees = 45; }; const int ON = 1; diff --git a/core_lib/src/tool/polylinetool.cpp b/core_lib/src/tool/polylinetool.cpp index b18539d88a..62528bbe8c 100644 --- a/core_lib/src/tool/polylinetool.cpp +++ b/core_lib/src/tool/polylinetool.cpp @@ -48,6 +48,7 @@ void PolylineTool::loadSettings() mPropertyEnabled[BEZIER] = true; mPropertyEnabled[CLOSEDPATH] = true; mPropertyEnabled[ANTI_ALIASING] = true; + mPropertyEnabled[SNAPTOANGLE] = true; QSettings settings(PENCIL2D, PENCIL2D); @@ -163,7 +164,13 @@ void PolylineTool::pointerPressEvent(PointerEvent* event) mScribbleArea->toggleThinLines(); } } - mPoints << getCurrentPoint(); + + QPointF currentPoint = getCurrentPoint(); + if (properties.snapAngleState && !mPoints.isEmpty()) { + currentPoint = getSnappedPoint(mPoints.last(), currentPoint); + } + + mPoints << currentPoint; emit isActiveChanged(POLYLINE, true); } } @@ -181,7 +188,12 @@ void PolylineTool::pointerMoveEvent(PointerEvent* event) Layer* layer = mEditor->layers()->currentLayer(); if (layer->type() == Layer::BITMAP || layer->type() == Layer::VECTOR) { - drawPolyline(mPoints, getCurrentPoint()); + QPointF currentPoint = getCurrentPoint(); + if (properties.snapAngleState && !mPoints.isEmpty()) { + currentPoint = getSnappedPoint(mPoints.last(), currentPoint); + } + drawPolyline(mPoints, currentPoint); + } StrokeTool::pointerMoveEvent(event); @@ -281,10 +293,25 @@ bool PolylineTool::keyReleaseEvent(QKeyEvent* event) return BaseTool::keyReleaseEvent(event); } + +QPointF PolylineTool::getSnappedPoint(const QPointF& lastPoint, const QPointF& currentPoint) +{ + QPointF delta = currentPoint - lastPoint; + qreal angle = qAtan2(delta.y(), delta.x()); + qreal snapAngle = qDegreesToRadians(properties.snapAngleDegrees); + angle = qRound(angle / snapAngle) * snapAngle; + qreal length = qSqrt(delta.x() * delta.x() + delta.y() * delta.y()); + return lastPoint + QPointF(qCos(angle) * length, qSin(angle) * length); +} + void PolylineTool::drawPolyline(QList points, QPointF endPoint) { if (points.size() > 0) { + if (properties.snapAngleState && !points.isEmpty()) { + endPoint = getSnappedPoint(points.last(), endPoint); + } + QPen pen(mEditor->color()->frontColor(), properties.width, Qt::SolidLine, diff --git a/core_lib/src/tool/polylinetool.h b/core_lib/src/tool/polylinetool.h index 18b77adcf3..5e3b51ad2d 100644 --- a/core_lib/src/tool/polylinetool.h +++ b/core_lib/src/tool/polylinetool.h @@ -55,11 +55,13 @@ class PolylineTool : public StrokeTool private: QList mPoints; bool mClosedPathOverrideEnabled = false; - + bool snapAngleEnabled = false; + int snapAngleDegrees; + QPointF getSnappedPoint(const QPointF& lastPoint, const QPointF& currentPoint); void drawPolyline(QList points, QPointF endPoint); void removeLastPolylineSegment(); void cancelPolyline(); - void endPolyline(QList points); + void endPolyline(QList points); }; #endif // POLYLINETOOL_H diff --git a/core_lib/src/util/pencildef.h b/core_lib/src/util/pencildef.h index a16f6f89c6..cfc57addf9 100644 --- a/core_lib/src/util/pencildef.h +++ b/core_lib/src/util/pencildef.h @@ -67,6 +67,8 @@ enum ToolPropertyType USEBUCKETFILLEXPAND, BUCKETFILLLAYERREFERENCEMODE, CAMERAPATH, + SNAPTOANGLE, + SNAPDEGREE, }; enum class DotColorType {