snap.cpp revision f750db62ff32f0048fdc228874e70a1ad9af79ae
308N/A#define __SP_DESKTOP_SNAP_C__
308N/A
308N/A/**
308N/A * \file snap.cpp
308N/A *
308N/A * \brief Various snapping methods
308N/A *
308N/A * Authors:
308N/A * Lauris Kaplinski <lauris@kaplinski.com>
308N/A * Frank Felfe <innerspace@iname.com>
308N/A * Carl Hetherington <inkscape@carlh.net>
308N/A *
308N/A * Copyright (C) 1999-2002 Authors
308N/A *
308N/A * Released under GNU GPL, read the file 'COPYING' for more information
308N/A */
308N/A
308N/A#include "sp-namedview.h"
308N/A#include "snap.h"
308N/A#include <libnr/nr-point-fns.h>
1062N/A#include <libnr/nr-scale-ops.h>
308N/A#include <libnr/nr-values.h>
308N/A
308N/ASnapManager::SnapManager(SPNamedView const *v) : grid(v, 0), guide(v, 0), object(v, 0)
308N/A{
308N/A
308N/A}
1178N/A
308N/ASnapManager::SnapperList SnapManager::getSnappers() const
308N/A{
308N/A SnapManager::SnapperList s;
308N/A s.push_back(&grid);
308N/A s.push_back(&guide);
308N/A s.push_back(&object);
308N/A return s;
308N/A}
308N/A
308N/A/**
1190N/A * \return true if one of the snappers will try to snap something.
308N/A */
308N/Abool SnapManager::willSnapSomething() const
1238N/A{
308N/A SnapperList const s = getSnappers();
1190N/A SnapperList::const_iterator i = s.begin();
308N/A while (i != s.end() && (*i)->willSnapSomething() == false) {
308N/A i++;
308N/A }
1190N/A
308N/A return (i != s.end());
308N/A}
308N/A
308N/AInkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t,
1190N/A NR::Point const &p,
308N/A SPItem const *it) const
308N/A
308N/A{
308N/A std::list<SPItem const *> lit;
308N/A lit.push_back(it);
308N/A return freeSnap(t, p, lit);
308N/A}
308N/A
308N/A
308N/AInkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t,
308N/A NR::Point const &p,
1190N/A std::list<SPItem const *> const &it) const
308N/A{
1190N/A Inkscape::SnappedPoint r(p, NR_HUGE);
308N/A
308N/A SnapperList const snappers = getSnappers();
1062N/A for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
308N/A Inkscape::SnappedPoint const s = (*i)->freeSnap(t, p, it);
308N/A if (s.getDistance() < r.getDistance()) {
308N/A r = s;
1178N/A }
308N/A }
308N/A
308N/A return r;
308N/A}
308N/A
308N/A
308N/AInkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t,
308N/A NR::Point const &p,
308N/A Inkscape::Snapper::ConstraintLine const &c,
308N/A SPItem const *it) const
{
std::list<SPItem const *> lit;
lit.push_back(it);
return constrainedSnap(t, p, c, lit);
}
Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t,
NR::Point const &p,
Inkscape::Snapper::ConstraintLine const &c,
std::list<SPItem const *> const &it) const
{
Inkscape::SnappedPoint r(p, NR_HUGE);
SnapperList const snappers = getSnappers();
for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
Inkscape::SnappedPoint const s = (*i)->constrainedSnap(t, p, c, it);
if (s.getDistance() < r.getDistance()) {
r = s;
}
}
return r;
}
std::pair<NR::Point, bool> SnapManager::_snapTransformed(
Inkscape::Snapper::PointType type,
std::vector<NR::Point> const &points,
std::list<SPItem const *> const &ignore,
bool constrained,
Inkscape::Snapper::ConstraintLine const &constraint,
Transformation transformation_type,
NR::Point const &transformation,
NR::Point const &origin,
NR::Dim2 dim,
bool uniform) const
{
/* We have a list of points, which we are proposing to transform in some way. We need to see
** if any of these points, when transformed, snap to anything. If they do, we return the
** appropriate transformation with `true'; otherwise we return the original scale with `false'.
*/
/* Quick check to see if we have any snappers that are enabled */
if (willSnapSomething() == false) {
return std::make_pair(transformation, false);
}
/* The current best transformation */
NR::Point best_transformation = transformation;
/* The current best metric for the best transformation; lower is better, NR_HUGE
** means that we haven't snapped anything.
*/
double best_metric = NR_HUGE;
for (std::vector<NR::Point>::const_iterator i = points.begin(); i != points.end(); i++) {
/* Work out the transformed version of this point */
NR::Point transformed;
switch (transformation_type) {
case TRANSLATION:
transformed = *i + transformation;
break;
case SCALE:
transformed = ((*i - origin) * NR::scale(transformation[NR::X], transformation[NR::Y])) + origin;
break;
case STRETCH:
{
NR::scale s(1, 1);
if (uniform)
s[NR::X] = s[NR::Y] = transformation[dim];
else {
s[dim] = transformation[dim];
s[1 - dim] = 1;
}
transformed = ((*i - origin) * s) + origin;
break;
}
case SKEW:
transformed = *i;
transformed[dim] += transformation[dim] * ((*i)[1 - dim] - origin[1 - dim]);
break;
default:
g_assert_not_reached();
}
/* Snap it */
Inkscape::SnappedPoint const snapped = constrained ?
constrainedSnap(type, transformed, constraint, ignore) : freeSnap(type, transformed, ignore);
if (snapped.getDistance() < NR_HUGE) {
/* We snapped. Find the transformation that describes where the snapped point has
** ended up, and also the metric for this transformation.
*/
NR::Point result;
NR::Coord metric;
switch (transformation_type) {
case TRANSLATION:
result = snapped.getPoint() - *i;
metric = NR::L2(result);
break;
case SCALE:
{
NR::Point const a = (snapped.getPoint() - origin);
NR::Point const b = (*i - origin);
result = NR::Point(a[NR::X] / b[NR::X], a[NR::Y] / b[NR::Y]);
metric = std::abs(NR::L2(result) - NR::L2(transformation));
break;
}
case STRETCH:
{
for (int j = 0; j < 2; j++) {
if (uniform || j == dim) {
result[j] = (snapped.getPoint()[dim] - origin[dim]) / ((*i)[dim] - origin[dim]);
} else {
result[j] = 1;
}
}
metric = std::abs(result[dim] - transformation[dim]);
break;
}
case SKEW:
result[dim] = (snapped.getPoint()[dim] - (*i)[dim]) / ((*i)[1 - dim] - origin[1 - dim]);
metric = std::abs(result[dim] - transformation[dim]);
break;
default:
g_assert_not_reached();
}
/* Note it if it's the best so far */
if (metric < best_metric && metric != 0) {
best_transformation = result;
best_metric = metric;
}
}
}
return std::make_pair(best_transformation, best_metric < NR_HUGE);
}
std::pair<NR::Point, bool> SnapManager::freeSnapTranslation(Inkscape::Snapper::PointType t,
std::vector<NR::Point> const &p,
std::list<SPItem const *> const &it,
NR::Point const &tr) const
{
return _snapTransformed(
t, p, it, false, NR::Point(), TRANSLATION, tr, NR::Point(), NR::X, false
);
}
std::pair<NR::Point, bool> SnapManager::constrainedSnapTranslation(Inkscape::Snapper::PointType t,
std::vector<NR::Point> const &p,
std::list<SPItem const *> const &it,
Inkscape::Snapper::ConstraintLine const &c,
NR::Point const &tr) const
{
return _snapTransformed(
t, p, it, true, c, TRANSLATION, tr, NR::Point(), NR::X, false
);
}
std::pair<NR::scale, bool> SnapManager::freeSnapScale(Inkscape::Snapper::PointType t,
std::vector<NR::Point> const &p,
std::list<SPItem const *> const &it,
NR::scale const &s,
NR::Point const &o) const
{
return _snapTransformed(
t, p, it, false, NR::Point(), SCALE, NR::Point(s[NR::X], s[NR::Y]), o, NR::X, false
);
}
std::pair<NR::scale, bool> SnapManager::constrainedSnapScale(Inkscape::Snapper::PointType t,
std::vector<NR::Point> const &p,
std::list<SPItem const *> const &it,
Inkscape::Snapper::ConstraintLine const &c,
NR::scale const &s,
NR::Point const &o) const
{
return _snapTransformed(
t, p, it, true, c, SCALE, NR::Point(s[NR::X], s[NR::Y]), o, NR::X, false
);
}
std::pair<NR::Coord, bool> SnapManager::freeSnapStretch(Inkscape::Snapper::PointType t,
std::vector<NR::Point> const &p,
std::list<SPItem const *> const &it,
NR::Coord const &s,
NR::Point const &o,
NR::Dim2 d,
bool u) const
{
std::pair<NR::Point, bool> const r = _snapTransformed(
t, p, it, false, NR::Point(), STRETCH, NR::Point(s, s), o, d, u
);
return std::make_pair(r.first[d], r.second);
}
std::pair<NR::Coord, bool> SnapManager::freeSnapSkew(Inkscape::Snapper::PointType t,
std::vector<NR::Point> const &p,
std::list<SPItem const *> const &it,
NR::Coord const &s,
NR::Point const &o,
NR::Dim2 d) const
{
std::pair<NR::Point, bool> const r = _snapTransformed(
t, p, it, false, NR::Point(), SKEW, NR::Point(s, s), o, d, false
);
return std::make_pair(r.first[d], r.second);
}
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :