Element editor: changed the way scaling operations get rounded.

git-svn-id: svn+ssh://svn.tuxfamily.org/svnroot/qet/qet/trunk@2058 bfdf4180-ca20-0410-9c96-a3a8aa849046
This commit is contained in:
xavier
2013-03-06 18:51:29 +00:00
parent 62a59f5341
commit 5af8573960
7 changed files with 112 additions and 18 deletions

View File

@@ -59,6 +59,17 @@ void CustomElementPart::setDecorator(ElementPrimitiveDecorator *decorator) {
Q_UNUSED(decorator)
}
/**
This method is called by the decorator when it needs to determine the best
way to interactively scale a primitive. It is typically called when only a
single primitive is being scaled.
The default implementation systematically returns
QET::SnapScalingPointToGrid
*/
QET::ScalingMethod CustomElementPart::preferredScalingMethod() const {
return(QET::SnapScalingPointToGrid);
}
/**
This method is called by the decorator when it manages only a single
primitive and it received a mouse press event.

View File

@@ -20,6 +20,7 @@
#include <QtGui>
#include <QtXml>
#include <QImage>
#include "qet.h"
class CustomElement;
class ElementPrimitiveDecorator;
class ElementScene;
@@ -101,6 +102,7 @@ class CustomElementPart {
virtual QGraphicsItem *toItem();
virtual void setDecorator(ElementPrimitiveDecorator *);
virtual QET::ScalingMethod preferredScalingMethod() const;
virtual bool singleItemPressEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
virtual bool singleItemMoveEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
virtual bool singleItemReleaseEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);

View File

@@ -245,15 +245,30 @@ void ElementPrimitiveDecorator::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
QPointF scene_pos = event -> scenePos();
QPointF movement = scene_pos - latest_pos_;
// snap the movement to a non-visible grid when scaling selection
if (mustSnapToGrid(event)) {
// We now want some point belonging to the selection to be snapped to this rounded position
if (current_operation_square_ > QET::NoOperation) {
// This is a scaling operation.
// For convenience purposes, we may need to adjust mouse movements.
QET::ScalingMethod scaling_method = scalingMethod(event);
if (scaling_method > QET::FreeScaling) {
// real, non-rounded movement from the mouse press event
QPointF global_movement = scene_pos - first_pos_;
QPointF rounded_global_movement;
if (scaling_method == QET::SnapScalingPointToGrid) {
// real, rounded movement from the mouse press event
QPointF rounded_global_movement = snapConstPointToGrid(global_movement);
rounded_global_movement = snapConstPointToGrid(global_movement);
}
else {
QRectF new_bounding_rect = original_bounding_rect_;
applyMovementToRect(current_operation_square_, global_movement, new_bounding_rect);
const qreal scale_epsilon = 20.0; // rounds to 0.05
QPointF delta = deltaForRoundScaling(original_bounding_rect_, new_bounding_rect, scale_epsilon);
// real, rounded movement from the mouse press event
rounded_global_movement = global_movement + delta;
}
// rounded position of the current mouse move event
QPointF rounded_scene_pos = first_pos_ + rounded_global_movement;
@@ -262,12 +277,17 @@ void ElementPrimitiveDecorator::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
QPointF current_position = mapToScene(rects.at(current_operation_square_).center());
// determine the final, effective movement
movement = rounded_scene_pos - current_position;
} else if (current_operation_square_ == QET::MoveArea) {
// when moving the selection, consider the position of the first selected item
}
}
else if (current_operation_square_ == QET::MoveArea) {
// When moving the selection, consider the position of the first selected item
QPointF current_position = scene_pos - mouse_offset_;
QPointF rounded_current_position = snapConstPointToGrid(current_position);
movement = rounded_current_position - decorated_items_.at(0) -> toItem() -> scenePos();
} else {
}
else {
// Neither a movement nor a scaling operation -- perhaps the underlying item
// is interested in the mouse event for custom operations?
if (CustomElementPart *single_item = singleItem()) {
bool event_accepted = single_item -> singleItemMoveEvent(this, event);
if (event_accepted) {
@@ -276,7 +296,6 @@ void ElementPrimitiveDecorator::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
}
}
}
}
QRectF bounding_rect = modified_bounding_rect_;
applyMovementToRect(current_operation_square_, movement, modified_bounding_rect_);
@@ -627,6 +646,30 @@ int ElementPrimitiveDecorator::resizingSquareAtPos(const QPointF &position) {
return(current_square);
}
/**
Receive two rects, assuming they share a common corner and current is a \a
scaled version of \a original.
Calculate the scale ratios implied by this assumption, round them to the
nearest multiple of \a epsilon, then return the horizontal and vertical
offsets to be applied in order to pass from \a current to \a original scaled
by the rounded factors.
This method can be used to adjust a mouse movement so that it inputs a
round scaling operation.
*/
QPointF ElementPrimitiveDecorator::deltaForRoundScaling(const QRectF &original, const QRectF &current, qreal epsilon) {
qreal sx = current.width() / original.width();
qreal sy = current.height() / original.height();
qreal sx_rounded = QET::round(sx, epsilon);
qreal sy_rounded = QET::round(sy, epsilon);
QPointF delta(
original.width() * (sx_rounded - sx),
original.height() * (sy_rounded - sy)
);
return(delta);
}
/**
Round the coordinates of \a point so it is snapped to the grid defined by the
grid_step_x_ and grid_step_y_ attributes.
@@ -656,3 +699,19 @@ void ElementPrimitiveDecorator::snapPointToGrid(QPointF &point) const {
bool ElementPrimitiveDecorator::mustSnapToGrid(QGraphicsSceneMouseEvent *event) {
return(!(event -> modifiers() & Qt::ControlModifier));
}
/**
@param event Mouse event during the scale operations -- simply passed to mustSnapToGrid()
@return the scaling method to be used for the currently decorated items.
@see QET::ScalingMethod
@see mustSnapToGrid()
*/
QET::ScalingMethod ElementPrimitiveDecorator::scalingMethod(QGraphicsSceneMouseEvent *event) {
if (event && !mustSnapToGrid(event)) {
return(QET::FreeScaling);
}
if (CustomElementPart *single_item = singleItem()) {
return single_item -> preferredScalingMethod();
}
return QET::RoundScaleRatios;
}

View File

@@ -1,6 +1,7 @@
#ifndef ELEMENTPRIMITIVEDECORATOR_H
#define ELEMENTPRIMITIVEDECORATOR_H
#include <QGraphicsObject>
#include "qet.h"
class ElementEditionCommand;
class ElementScene;
class CustomElementPart;
@@ -49,9 +50,11 @@ class ElementPrimitiveDecorator : public QGraphicsObject {
void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
void keyPressEvent(QKeyEvent *);
void keyReleaseEvent(QKeyEvent *);
QPointF deltaForRoundScaling(const QRectF &, const QRectF &, qreal);
QPointF snapConstPointToGrid(const QPointF &) const;
void snapPointToGrid(QPointF &) const;
bool mustSnapToGrid(QGraphicsSceneMouseEvent *);
QET::ScalingMethod scalingMethod(QGraphicsSceneMouseEvent *);
private:
void init();

View File

@@ -190,6 +190,17 @@ void PartPolygon::handleUserTransformation(const QRectF &initial_selection_rect,
setPolygon(mapFromScene(QPolygonF(mapped_points.toVector())));
}
/**
@reimp CustomElementPart::preferredScalingMethod
This method is called by the decorator when it needs to determine the best
way to interactively scale a primitive. It is typically called when only a
single primitive is being scaled.
This reimplementation systematically returns QET::RoundScaleRatios.
*/
QET::ScalingMethod PartPolygon::preferredScalingMethod() const {
return(QET::RoundScaleRatios);
}
/**
@return le rectangle delimitant cette partie.
*/

View File

@@ -59,6 +59,7 @@ class PartPolygon : public QGraphicsPolygonItem, public CustomElementGraphicPart
virtual QRectF sceneGeometricRect() const;
virtual void startUserTransformation(const QRectF &);
virtual void handleUserTransformation(const QRectF &, const QRectF &);
virtual QET::ScalingMethod preferredScalingMethod() const;
protected:
QVariant itemChange(GraphicsItemChange, const QVariant &);

View File

@@ -59,6 +59,13 @@ namespace QET {
ResizeFromBottomRightCorner = 7
};
/// Supported types of interactive scaling, typically for a single element primitive
enum ScalingMethod {
FreeScaling, ///< do not interfer with the default scaling process
SnapScalingPointToGrid, ///< snap the point used to define the new bounding rectangle to the grid
RoundScaleRatios ///< adjust the scaling movement so that the induced scaling ratios are rounded
};
/// Known kinds of conductor segments
enum ConductorSegmentType {
Horizontal = 1, ///< Horizontal segment