From 179fa413b047bede6e32109e2ce82437c5fb8d34 Mon Sep 17 00:00:00 2001 From: MenTaLguY Date: Mon, 16 Jan 2006 02:36:01 +0000 Subject: moving trunk for module inkscape (bzr r1) --- src/snap.cpp | 406 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 406 insertions(+) create mode 100644 src/snap.cpp (limited to 'src/snap.cpp') diff --git a/src/snap.cpp b/src/snap.cpp new file mode 100644 index 000000000..b14549db3 --- /dev/null +++ b/src/snap.cpp @@ -0,0 +1,406 @@ +#define __SP_DESKTOP_SNAP_C__ + +/** + * \file snap.cpp + * + * \brief Various snapping methods + * + * Authors: + * Lauris Kaplinski + * Frank Felfe + * Carl Hetherington + * + * Copyright (C) 1999-2002 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "sp-namedview.h" +#include "snap.h" +#include +#include +#include + + +/** + * \return true if one of the snappers will try to snap something. + */ +bool SnapManager::willSnapSomething() const +{ + SPNamedView::SnapperList s = namedview->getSnappers(); + SPNamedView::SnapperList::const_iterator i = s.begin(); + while (i != s.end() && (*i)->willSnapSomething() == false) { + i++; + } + + return (i != s.end()); +} + + +/* FIXME: lots of cut-and-paste here. This needs some +** functor voodoo to cut it all down a bit. +*/ + +Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t, + NR::Point const &p, + SPItem const *it) const + +{ + std::list lit; + lit.push_back(it); + return freeSnap(t, p, lit); +} + + +Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t, + NR::Point const &p, + std::list const &it) const +{ + Inkscape::SnappedPoint r(p, NR_HUGE); + + SPNamedView::SnapperList snappers = namedview->getSnappers(); + for (SPNamedView::SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { + Inkscape::SnappedPoint const s = (*i)->freeSnap(t, p, it); + if (s.getDistance() < r.getDistance()) { + r = s; + } + } + + return r; +} + + +Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t, + NR::Point const &p, + NR::Point const &c, + SPItem const *it) const +{ + std::list lit; + lit.push_back(it); + return constrainedSnap(t, p, c, lit); +} + + +Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t, + NR::Point const &p, + NR::Point const &c, + std::list const &it) const +{ + Inkscape::SnappedPoint r(p, NR_HUGE); + + SPNamedView::SnapperList snappers = namedview->getSnappers(); + for (SPNamedView::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 SnapManager::freeSnapTranslation(Inkscape::Snapper::PointType t, + std::vector const &p, + std::list const &it, + NR::Point const &tr) const +{ + if (willSnapSomething() == false) { + return std::make_pair(tr, false); + } + + NR::Point best_translation = tr; + NR::Coord best_distance = NR_HUGE; + + for (std::vector::const_iterator i = p.begin(); i != p.end(); i++) { + /* Translated version of this point */ + NR::Point const q = *i + tr; + /* Snap it */ + Inkscape::SnappedPoint s = freeSnap(t, q, it); + if (s.getDistance() < NR_HUGE) { + /* Resulting translation */ + NR::Point const r = s.getPoint() - *i; + NR::Coord const d = NR::L2(r); + if (d < best_distance) { + best_distance = d; + best_translation = r; + } + } + } + + return std::make_pair(best_translation, best_distance < NR_HUGE); +} + + + +std::pair SnapManager::constrainedSnapTranslation(Inkscape::Snapper::PointType t, + std::vector const &p, + NR::Point const &c, + std::list const &it, + NR::Point const &tr) const +{ + if (willSnapSomething() == false) { + return std::make_pair(tr, false); + } + + NR::Point best_translation = tr; + NR::Coord best_distance = NR_HUGE; + + for (std::vector::const_iterator i = p.begin(); i != p.end(); i++) { + /* Translated version of this point */ + NR::Point const q = *i + tr; + /* Snap it */ + Inkscape::SnappedPoint s = constrainedSnap(t, q, c, it); + if (s.getDistance() < NR_HUGE) { + /* Resulting translation */ + NR::Point const r = s.getPoint() - *i; + NR::Coord const d = NR::L2(r); + if (d < best_distance) { + best_distance = d; + best_translation = r; + } + } + } + + return std::make_pair(best_translation, best_distance < NR_HUGE); +} + + + + + + +/// Minimal distance to norm before point is considered for snap. +static const double MIN_DIST_NORM = 1.0; + +/** + * Try to snap \a req in one dimension. + * + * \param nv NamedView to use. + * \param req Point to snap; updated to the snapped point if a snap occurred. + * \param dim Dimension to snap in. + * \return Distance to the snap point along the \a dim axis, or \c NR_HUGE + * if no snap occurred. + */ +NR::Coord namedview_dim_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t, NR::Point &req, + NR::Dim2 const dim, SPItem const *it) +{ + return namedview_vector_snap(nv, t, req, component_vectors[dim], it); +} + +NR::Coord namedview_dim_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t, NR::Point &req, + NR::Dim2 const dim, std::list const &it) +{ + return namedview_vector_snap(nv, t, req, component_vectors[dim], it); +} + + +NR::Coord namedview_vector_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t, + NR::Point &req, NR::Point const &d, + SPItem const *it) +{ + std::list lit; + lit.push_back(it); + return namedview_vector_snap(nv, t, req, d, lit); +} + +/** + * Look for snap point along the line described by the point \a req + * and the direction vector \a d. + * Modifies req to the snap point, if one is found. + * \return The distance from \a req to the snap point along the vector \a d, + * or \c NR_HUGE if no snap point was found. + * + * \pre d ≁ (0, 0). + */ +NR::Coord namedview_vector_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t, + NR::Point &req, NR::Point const &d, + std::list const &it) +{ + g_assert(nv != NULL); + g_assert(SP_IS_NAMEDVIEW(nv)); + + SPNamedView::SnapperList snappers = nv->getSnappers(); + + NR::Coord best = NR_HUGE; + for (SPNamedView::SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { + Inkscape::SnappedPoint const s = (*i)->constrainedSnap(t, req, d, it); + if (s.getDistance() < best) { + req = s.getPoint(); + best = s.getDistance(); + } + } + + return best; +} + + +/* + * functions for lists of points + * + * All functions take a list of NR::Point and parameter indicating the proposed transformation. + * They return the updated transformation parameter. + */ + +/** + * Snap list of points in one dimension. + * \return Coordinate difference. + */ +std::pair namedview_dim_snap_list(SPNamedView const *nv, Inkscape::Snapper::PointType t, + const std::vector &p, + NR::Coord const dx, NR::Dim2 const dim, + std::list const &it + ) +{ + NR::Coord dist = NR_HUGE; + NR::Coord xdist = dx; + + SnapManager const m(nv); + + if (m.willSnapSomething()) { + for (std::vector::const_iterator i = p.begin(); i != p.end(); i++) { + NR::Point q = *i; + NR::Coord const pre = q[dim]; + q[dim] += dx; + NR::Coord const d = namedview_dim_snap(nv, t, q, dim, it); + if (d < dist) { + xdist = q[dim] - pre; + dist = d; + } + } + } + + return std::make_pair(xdist, dist < NR_HUGE); +} + +/** + * Snap list of points in two dimensions. + */ +std::pair namedview_vector_snap_list(SPNamedView const *nv, Inkscape::Snapper::PointType t, + const std::vector &p, NR::Point const &norm, + NR::scale const &s, std::list const &it) +{ + using NR::X; + using NR::Y; + + SnapManager const m(nv); + + if (m.willSnapSomething() == false) { + return std::make_pair(s[X], false); + } + + NR::Coord dist = NR_HUGE; + double ratio = fabs(s[X]); + for (std::vector::const_iterator i = p.begin(); i != p.end(); i++) { + NR::Point const &q = *i; + NR::Point check = ( q - norm ) * s + norm; + if (NR::LInfty( q - norm ) > MIN_DIST_NORM) { + NR::Coord d = namedview_vector_snap(nv, t, check, check - norm, it); + if (d < dist) { + dist = d; + NR::Dim2 const dominant = ( ( fabs( q[X] - norm[X] ) > + fabs( q[Y] - norm[Y] ) ) + ? X + : Y ); + ratio = ( ( check[dominant] - norm[dominant] ) + / ( q[dominant] - norm[dominant] ) ); + } + } + } + + return std::make_pair(ratio, dist < NR_HUGE); +} + + +/** + * Try to snap points in \a p after they have been scaled by \a sx with respect to + * the origin \a norm. The best snap is the one that changes the scale least. + * + * \return Pair containing snapped scale and a flag which is true if a snap was made. + */ +std::pair namedview_dim_snap_list_scale(SPNamedView const *nv, Inkscape::Snapper::PointType t, + const std::vector &p, NR::Point const &norm, + double const sx, NR::Dim2 dim, + std::list const &it) +{ + SnapManager const m(nv); + if (m.willSnapSomething() == false) { + return std::make_pair(sx, false); + } + + g_assert(dim < 2); + + NR::Coord dist = NR_HUGE; + double scale = sx; + + for (std::vector::const_iterator i = p.begin(); i != p.end(); i++) { + NR::Point q = *i; + NR::Point check = q; + + /* Scaled version of the point we are looking at */ + check[dim] = (sx * (q - norm) + norm)[dim]; + + if (fabs (q[dim] - norm[dim]) > MIN_DIST_NORM) { + /* Snap this point */ + const NR::Coord d = namedview_dim_snap (nv, t, check, dim, it); + /* Work out the resulting scale factor */ + double snapped_scale = (check[dim] - norm[dim]) / (q[dim] - norm[dim]); + + if (dist == NR_HUGE || fabs(snapped_scale - sx) < fabs(scale - sx)) { + /* This is either the first point, or the snapped scale + ** is the closest yet to the original. + */ + scale = snapped_scale; + dist = d; + } + } + } + + return std::make_pair(scale, dist < NR_HUGE); +} + +/** + * Try to snap points after they have been skewed. + */ +double namedview_dim_snap_list_skew(SPNamedView const *nv, Inkscape::Snapper::PointType t, + const std::vector &p, NR::Point const &norm, + double const sx, NR::Dim2 const dim) +{ + SnapManager const m(nv); + + if (m.willSnapSomething() == false) { + return sx; + } + + g_assert(dim < 2); + + gdouble dist = NR_HUGE; + gdouble skew = sx; + + for (std::vector::const_iterator i = p.begin(); i != p.end(); i++) { + NR::Point q = *i; + NR::Point check = q; + // apply shear + check[dim] += sx * (q[!dim] - norm[!dim]); + if (fabs (q[!dim] - norm[!dim]) > MIN_DIST_NORM) { + const gdouble d = namedview_dim_snap (nv, t, check, dim, NULL); + if (d < fabs (dist)) { + dist = d; + skew = (check[dim] - q[dim]) / (q[!dim] - norm[!dim]); + } + } + } + + return skew; +} + +/* + 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 : -- cgit v1.2.3