diff --git a/src/core/objects/object.cpp b/src/core/objects/object.cpp index 7343886f7..38cee1d69 100644 --- a/src/core/objects/object.cpp +++ b/src/core/objects/object.cpp @@ -544,26 +544,26 @@ void Object::move(const MapCoord& offset) void Object::scale(const MapCoordF& center, double factor) { - for (MapCoord& coord : coords) - { - coord.setX(center.x() + (coord.x() - center.x()) * factor); - coord.setY(center.y() + (coord.y() - center.y()) * factor); - } - - setOutputDirty(); + scale(center, factor, factor); } void Object::scale(double factor_x, double factor_y) +{ + scale(MapCoordF(0, 0), factor_x, factor_y); +} + +void Object::scale(const MapCoordF& center, double factor_x, double factor_y) { for (MapCoord& coord : coords) { - coord.setX(coord.x() * factor_x); - coord.setY(coord.y() * factor_y); + coord.setX(center.x() + (coord.x() - center.x()) * factor_x); + coord.setY(center.y() + (coord.y() - center.y()) * factor_y); } - + setOutputDirty(); } + // virtual void Object::rotatePatternOrigin(const MapCoordF& /*center*/, qreal /*sin_angle*/, qreal /*cos_angle*/) { diff --git a/src/core/objects/object.h b/src/core/objects/object.h index 816ad8aa8..c6a444f1e 100644 --- a/src/core/objects/object.h +++ b/src/core/objects/object.h @@ -208,6 +208,13 @@ friend class XMLImportExport; * @param factor_y vertical scaling factor */ virtual void scale(double factor_x, double factor_y); + + /** Scales all coordinates. + * @param center center of the scaling operation + * @param factor_x horizontal scaling factor + * @param factor_y vertical scaling factor + */ + virtual void scale(const MapCoordF& center, double factor_x, double factor_y); protected: /** Rotates the pattern origin around the center point. diff --git a/src/tools/scale_tool.cpp b/src/tools/scale_tool.cpp index 6710a2946..22c890a8f 100644 --- a/src/tools/scale_tool.cpp +++ b/src/tools/scale_tool.cpp @@ -66,7 +66,10 @@ void ScaleTool::updateStatusText() { if (editingInProgress()) { - setStatusBarText(tr("Scaling: %1%").arg(QLocale().toString(scaling_factor * 100, 'f', 1))); + if (uniform_scaling) + setStatusBarText(tr("Scaling: %1%").arg(QLocale().toString(qSqrt(scaling_factor.x()*scaling_factor.x() + scaling_factor.y()*scaling_factor.y()) * 100 / qSqrt(2), 'f', 1))); + else + setStatusBarText(tr("Scaling: x: %1% y: %2%").arg(QLocale().toString(scaling_factor.x() * 100, 'f', 1), QLocale().toString(scaling_factor.y() * 100, 'f', 1))); } else { @@ -75,7 +78,8 @@ void ScaleTool::updateStatusText() status_text = tr("Click: Set the scaling center. ") + status_text + tr("%1: Switch to individual object scaling. ").arg(ModifierKey::control()); - + if (uniform_scaling) + status_text = status_text + tr("%1: Enable non-uniform scaling. ").arg(ModifierKey::shift()); setStatusBarText(status_text); } } @@ -93,11 +97,19 @@ void ScaleTool::clickRelease() void ScaleTool::dragStart() { - // WARNING: reference_length may become 0. - reference_length = (click_pos_map - scaling_center).length(); + scaling_start_point = click_pos_map; startEditing(map()->selectedObjects()); } +double absmax(double v, double max) +{ + // a bipolar qMax() equivalent that returns v when v is larger than +max, + // or smaller than -max, otherwise returns +max or -max depending on sign of v + if (v >= 0) + return qMax(v, max); + else + return qMin(v, -max); +} void ScaleTool::dragMove() { @@ -107,18 +119,31 @@ void ScaleTool::dragMove() // in order to avoid extreme values and division by zero. auto minimum_length = 1.0 / cur_map_widget->getMapView()->getZoom(); - auto scaling_length = (cur_pos_map - scaling_center).length(); - scaling_factor = qMax(minimum_length, scaling_length) / qMax(minimum_length, reference_length); + auto scaling_point = cur_pos_map - scaling_center; + auto reference_point = scaling_start_point - scaling_center; + + if (uniform_scaling) + { + // scaling_point and reference_point are converted to contain the respective vector length in both x and y components, + // instead of having independent x and y components. + scaling_point.setX(qSqrt(scaling_point.x()*scaling_point.x() + scaling_point.y()*scaling_point.y())); + scaling_point.setY(scaling_point.x()); + reference_point.setX(qSqrt(reference_point.x()*reference_point.x() + reference_point.y()*reference_point.y())); + reference_point.setY(reference_point.x()); + } + + scaling_factor.setX(absmax(scaling_point.x(), minimum_length) / absmax(reference_point.x(), minimum_length)); + scaling_factor.setY(absmax(scaling_point.y(), minimum_length) / absmax(reference_point.y(), minimum_length)); if (using_scaling_center) { for (auto* object : editedObjects()) - object->scale(scaling_center, scaling_factor); + object->scale(scaling_center, scaling_factor.x(), scaling_factor.y()); } else { for (auto* object : editedObjects()) - object->scale(MapCoordF(object->getExtent().center()), scaling_factor); + object->scale(MapCoordF(object->getExtent().center()), scaling_factor.x(), scaling_factor.y()); } updatePreviewObjects(); @@ -142,6 +167,11 @@ bool ScaleTool::keyPressEvent(QKeyEvent* event) updateStatusText(); updateDirtyRect(); return false; // not consuming Ctrl + case Qt::Key_Shift: + uniform_scaling = false; + updateStatusText(); + updateDirtyRect(); + return false; } return false; @@ -157,6 +187,11 @@ bool ScaleTool::keyReleaseEvent(QKeyEvent* event) updateStatusText(); updateDirtyRect(); return false; // not consuming Ctrl + case Qt::Key_Shift: + uniform_scaling = true; + updateStatusText(); + updateDirtyRect(); + return false; } return false; diff --git a/src/tools/scale_tool.h b/src/tools/scale_tool.h index c4bc4e71f..bd2a00ed2 100644 --- a/src/tools/scale_tool.h +++ b/src/tools/scale_tool.h @@ -68,9 +68,11 @@ Q_OBJECT void objectSelectionChangedImpl() override; MapCoordF scaling_center; - double reference_length = 0; - double scaling_factor = 1; + MapCoordF scaling_start_point; + QPointF reference_point = QPointF(0,0); + QPointF scaling_factor = QPointF(1, 1); bool using_scaling_center = true; + bool uniform_scaling = true; };