/*
* Class for pure transformations, such as translating, scaling, stretching, skewing, and rotating
*
* Authors:
* Diederik van Lierop <mail@diedenrezi.nl>
*
* Copyright (C) 2015 Diederik van Lierop
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#include "pure-transform.h"
#include "snap.h"
namespace Inkscape
{
void PureTransform::snap(::SnapManager *sm, std::vector<Inkscape::SnapCandidatePoint> const &points, Geom::Point const &pointer) {
long source_num = 0;
for (std::vector<Inkscape::SnapCandidatePoint>::const_iterator i = points.begin(); i != points.end(); ++i) {
/* Work out the transformed version of this point */
Geom::Point transformed = getTransformedPoint(*i); // _transformPoint(*i, transformation_type, transformation, origin, dim, uniform);
// add the current transformed point to the box hulling all transformed points
} else {
}
transformed_points.push_back(Inkscape::SnapCandidatePoint(transformed, (*i).getSourceType(), source_num, Inkscape::SNAPTARGET_UNDEFINED, Geom::OptRect()));
source_num++;
}
/* The current best metric for the best transformation; lower is better, whereas Geom::infinity()
** means that we haven't snapped anything.
*/
// std::cout << std::endl;
bool first_free_snap = true;
for (std::vector<Inkscape::SnapCandidatePoint>::const_iterator i = points.begin(); i != points.end(); ++i) {
// If we have a collection of SnapCandidatePoints, with mixed constrained snapping and free snapping
// requirements (this can happen when scaling, see PureScale::snap()), then freeSnap might never see the
// SnapCandidatePoint with source_num == 0. The freeSnap() method in the object snapper depends on this,
// because only for source-num == 0 the target nodes will be collected. Therefore we enforce that the first
// SnapCandidatePoint that is to be freeSnapped always has source_num == 0;
// TODO: This is a bit ugly so fix this; do we need sourcenum for anything else? if we don't then get rid
// of it and explicitly communicate to the object snapper that this is a first point
if (first_free_snap) {
(*j).setSourceNum(0);
first_free_snap = false;
}
Inkscape::SnappedPoint snapped_point = snap(sm, *j, (*i).getPoint(), bbox); // Calls the snap() method of the derived classes
// std::cout << "dist = " << snapped_point.getSnapDistance() << std::endl;
/*Find the transformation that describes where the snapped point has
** ended up, and also the metric for this transformation.
*/
bool store_best_snap = false;
if (snapped_point.getSnapped()) {
// We snapped; keep track of the best snap
store_best_snap = true;
}
} else {
// So we didn't snap for this point
if (!best_snapped_point.getSnapped()) {
// ... and none of the points before snapped either
// We might still need to apply a constraint though, if we tried a constrained snap. And
// in case of a free snap we might have use for the transformed point, so let's return that
// point, whether it's constrained or not
// .. so we must keep track of the best non-snapped constrained point
store_best_snap = true;
}
}
}
if (store_best_snap) {
best_original_point = (*i);
}
++j;
}
/* The current best transformation */
//Geom::Point best_transformation = getResult(best_original_point, best_snapped_point);
// Using " < 1e6" instead of " < Geom::infinity()" for catching some rounding errors
// These rounding errors might be caused by NRRects, see bug #1584301
}
}
void PureTranslate::storeTransform(SnapCandidatePoint const original_point, SnappedPoint const snapped_point) {
/* Consider the case in which a box is almost aligned with a grid in both
* horizontal and vertical directions. The distance to the intersection of
* the grid lines will always be larger then the distance to a single grid
* line. If we prefer snapping to an intersection over to a single
* grid line, then we cannot use "metric = Geom::L2(result)". Therefore the
* snapped distance will be used as a metric. Please note that the snapped
* distance to an intersection is defined as the distance to the nearest line
* of the intersection, and not to the intersection itself!
*/
// Only for translations, the relevant metric will be the real snapped distance,
// so we don't have to do anything special here
}
SnappedPoint PureTranslate::snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point /*pt_orig*/, Geom::OptRect const &bbox_to_snap) const {
}
SnappedPoint PureTranslateConstrained::snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point pt_orig, Geom::OptRect const &bbox_to_snap) const {
// Calculate a constraint dedicated for this specific point
// When doing a constrained translation, all points will move in the same direction, i.e.
// either horizontally or vertically. The lines along which they move are therefore all
// parallel, but might not be co-linear. Therefore we will have to specify the point through
// which the constraint-line runs here, for each point individually.
}
}
void PureScale::storeTransform(SnapCandidatePoint const original_point, SnappedPoint snapped_point) {
// If this point *i is horizontally or vertically aligned with
// the origin of the scaling, then it will scale purely in X or Y
// We can therefore only calculate the scaling in this direction
// and the scaling factor for the other direction should remain
// untouched (unless scaling is uniform of course)
Geom::Point const b = original_point.getPoint() - _origin; // vector to original point (not the transformed point!)
if (fabs(fabs(a[index]/b[index]) - fabs(_scale[index])) > 1e-7) { // if SNAPPING DID occur in this direction
// _scale_snapped will be (1,1) if we haven't snapped, because the snapped point equals the original point
}
// we might have left result[1-index] = Geom::infinity() if scaling didn't occur in the other direction
}
}
if (_uniform) {
// Lock the scaling the be uniform, but keep the sign such that we don't change which quadrant we have dragged into
} else {
}
}
// Don't ever exit with one of scaling components uninitialized
}
}
// Compare the resulting scaling with the desired scaling
}
// When scaling, a point aligned either horizontally or vertically with the origin can only
// move in that specific direction; therefore it should only snap in that direction, so this
// then becomes a constrained snap; otherwise we can use a free snap;
SnappedPoint PureScale::snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point pt_orig, Geom::OptRect const &bbox_to_snap) const {
} else {
}
}
SnappedPoint PureScaleConstrained::snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point pt_orig, Geom::OptRect const &bbox_to_snap) const {
// When constrained scaling, only uniform scaling is supported.
// When uniformly scaling, each point will have its own unique constraint line,
// running from the scaling origin to the original untransformed point. We will
// calculate that line here as a dedicated constraint
}
if (_uniform)
else {
s[_direction] = _magnitude;
}
}
SnappedPoint PureStretchConstrained::snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point pt_orig, Geom::OptRect const &bbox_to_snap) const {
if (_uniform) {
// When uniformly stretching, each point will have its own unique constraint line,
// running from the scaling origin to the original untransformed point. We will
// calculate that line here
} else {
}
}
void PureStretchConstrained::storeTransform(SnapCandidatePoint const original_point, SnappedPoint snapped_point) {
Geom::Point const b = original_point.getPoint() - _origin; // vector to original point (not the transformed point!)
} else { // STRETCHING might occur for this point, but only when the stretching is uniform
}
}
// _stretch_snapped might have one or both components at infinity!
// Store the metric for this transformation as a virtual distance
}
// Apply the skew factor
transformed[_direction] = (p.getPoint())[_direction] + _skew * ((p.getPoint())[1 - _direction] - _origin[1 - _direction]);
// While skewing, mirroring and scaling (by integer multiples) in the opposite direction is also allowed.
// Apply that scale factor here
transformed[1-_direction] = (p.getPoint() - _origin)[1 - _direction] * _scale + _origin[1 - _direction];
return transformed;
}
SnappedPoint PureSkewConstrained::snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point pt_orig, Geom::OptRect const &bbox_to_snap) const {
// Snapping the nodes of the bounding box of a selection that is being transformed, will only work if
// the transformation of the bounding box is equal to the transformation of the individual nodes. This is
// NOT the case for example when rotating or skewing. The bounding box itself cannot possibly rotate or skew,
// so it's corners have a different transformation. The snappers cannot handle this, therefore snapping
// of bounding boxes is not allowed here.
}
void PureSkewConstrained::storeTransform(SnapCandidatePoint const original_point, SnappedPoint snapped_point) {
Geom::Point const b = original_point.getPoint() - _origin; // vector to original point (not the transformed point!)
_skew_snapped = (snapped_point.getPoint()[_direction] - (original_point.getPoint())[_direction]) / b[1 - _direction]; // skew factor
// Store the metric for this transformation as a virtual distance
}
}
SnappedPoint PureRotateConstrained::snap(::SnapManager *sm, SnapCandidatePoint const &p, Geom::Point pt_orig, Geom::OptRect const &bbox_to_snap) const {
// Snapping the nodes of the bounding box of a selection that is being transformed, will only work if
// the transformation of the bounding box is equal to the transformation of the individual nodes. This is
// NOT the case for example when rotating or skewing. The bounding box itself cannot possibly rotate or skew,
// so it's corners have a different transformation. The snappers cannot handle this, therefore snapping
// of bounding boxes is not allowed here.
// Calculate a constraint dedicated for this specific point
}
void PureRotateConstrained::storeTransform(SnapCandidatePoint const original_point, SnappedPoint snapped_point) {
Geom::Point const b = (original_point.getPoint() - _origin); // vector to original point (not the transformed point!)
// a is vector to snapped point; b is vector to original point; now lets calculate angle between a and b
if (Geom::L2(b) < 1e-9) { // points too close to the rotation center will not move. Don't try to snap these
// as they will always yield a perfect snap result if they're already snapped beforehand (e.g.
// when the transformation center has been snapped to a grid intersection in the selector tool)
// PS1: Apparently we don't have to do this for skewing, but why?
// PS2: We cannot easily filter these points upstream, e.g. in the grab() method (seltrans.cpp)
// because the rotation center will change when pressing shift, and grab() won't be recalled.
// Filtering could be done in handleRequest() (again in seltrans.cpp), by iterating through
// the snap candidates. But hey, we're iterating here anyway.
} else {
}
}
}