Geom::Rect Inkscape::snap_rectangular_box(SPDesktop const *desktop, SPItem *item,
                                        Geom::Point const &pt, Geom::Point const &center, int state)
{
    Geom::Point p[2];

    bool const shift = state & GDK_SHIFT_MASK;
    bool const control = state & GDK_CONTROL_MASK;

    SnapManager &m = desktop->namedview->snap_manager;
    m.setup(desktop, false, item);
    Inkscape::SnappedPoint snappoint;

    if (control) {

        /* Control is down: we are constrained to producing integer-ratio rectangles */

        /* Vector from the centre of the box to the point we are dragging to */
        Geom::Point delta = pt - center;

        /* Round it so that we have an integer-ratio (or golden ratio) box */
        if (fabs(delta[Geom::X]) > fabs(delta[Geom::Y]) && (delta[Geom::Y] != 0.0)) {
            double ratio = delta[Geom::X] / delta[Geom::Y];
            double ratioabs = fabs (ratio);
            double sign = (ratio < 0 ? -1 : 1);
            if (midpt_1_goldenratio < ratioabs && ratioabs < midpt_goldenratio_2) {
                delta[Geom::X] = sign * goldenratio * delta[Geom::Y];
            } else {
                delta[Geom::X] = floor(ratio + 0.5) * delta[Geom::Y];
            }
        } else if (delta[Geom::X] != 0.0) {
            double ratio = delta[Geom::Y] / delta[Geom::X];
            double ratioabs = fabs (ratio);
            double sign = (ratio < 0 ? -1 : 1);
            if (midpt_1_goldenratio < ratioabs && ratioabs < midpt_goldenratio_2) {
                delta[Geom::Y] = sign * goldenratio * delta[Geom::X];
            } else {
                delta[Geom::Y] = floor(delta[Geom::Y] / delta[Geom::X] + 0.5) * delta[Geom::X];
            }
        }

        /* p[1] is the dragged point with the integer-ratio constraint */
        p[1] = center + delta;

        if (shift) {

            /* Shift is down, so our origin is the centre point rather than the corner
            ** point; this means that corner-point movements are bound to each other.
            */

            /* p[0] is the opposite corner of our box */
            p[0] = center - delta;

            Inkscape::SnappedPoint s[2];

            /* Try to snap p[0] (the opposite corner) along the constraint vector */
            s[0] = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(p[0]),
                                     Inkscape::Snapper::ConstraintLine(p[0] - p[1]));

            /* Try to snap p[1] (the dragged corner) along the constraint vector */
            s[1] = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(p[1]),
                                     Inkscape::Snapper::ConstraintLine(p[1] - p[0]));

            /* Choose the best snap and update points accordingly */
            if (s[0].getSnapDistance() < s[1].getSnapDistance()) {
                if (s[0].getSnapped()) {
                    p[0] = s[0].getPoint();
                    p[1] = 2 * center - s[0].getPoint();
                    snappoint = s[0];
                }
            } else {
                if (s[1].getSnapped()) {
                    p[0] = 2 * center - s[1].getPoint();
                    p[1] = s[1].getPoint();
                    snappoint = s[1];
                }
            }
        } else {

            /* Our origin is the opposite corner.  Snap the drag point along the constraint vector */
            p[0] = center;
            snappoint = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(p[1]),
                                          Inkscape::Snapper::ConstraintLine(p[1] - p[0]));
            if (snappoint.getSnapped()) {
                p[1] = snappoint.getPoint();
            }
        }

    } else if (shift) {

        /* Shift is down, so our origin is the centre point rather than the corner point;
        ** this means that corner-point movements are bound to each other.
        */

        p[1] = pt;
        p[0] = 2 * center - p[1];

        Inkscape::SnappedPoint s[2];

        s[0] = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(p[0]));
        s[1] = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(p[1]));

        if (s[0].getSnapDistance() < s[1].getSnapDistance()) {
            if (s[0].getSnapped()) {
                p[0] = s[0].getPoint();
                p[1] = 2 * center - s[0].getPoint();
                snappoint = s[0];
            }
        } else {
            if (s[1].getSnapped()) {
                p[0] = 2 * center - s[1].getPoint();
                p[1] = s[1].getPoint();
                snappoint = s[1];
            }
        }

    } else {

        /* There's no constraint on the corner point, so just snap it to anything */
        p[0] = center;
        p[1] = pt;
        snappoint = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(pt));
        if (snappoint.getSnapped()) {
            p[1] = snappoint.getPoint();
        }
    }

    if (snappoint.getSnapped()) {
        desktop->snapindicator->set_new_snaptarget(snappoint);
    }

    p[0] = sp_desktop_dt2doc_xy_point(desktop, p[0]);
    p[1] = sp_desktop_dt2doc_xy_point(desktop, p[1]);
    
    return Geom::Rect(Geom::Point(MIN(p[0][Geom::X], p[1][Geom::X]), MIN(p[0][Geom::Y], p[1][Geom::Y])),
                    Geom::Point(MAX(p[0][Geom::X], p[1][Geom::X]), MAX(p[0][Geom::Y], p[1][Geom::Y])));
}
Пример #2
0
static void
sp_spiral_drag(SPSpiralContext *sc, Geom::Point p, guint state)
{
    SPDesktop *desktop = SP_EVENT_CONTEXT(sc)->desktop;

    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
    int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);

    if (!sc->item) {

        if (Inkscape::have_viable_layer(desktop, sc->_message_context) == false) {
            return;
        }

        /* Create object */
        Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_EVENT_CONTEXT_DOCUMENT(sc));
        Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");
        repr->setAttribute("sodipodi:type", "spiral");

        /* Set style */
        sp_desktop_apply_style_tool(desktop, repr, "/tools/shapes/spiral", false);

        sc->item = (SPItem *) desktop->currentLayer()->appendChildRepr(repr);
        Inkscape::GC::release(repr);
        sc->item->transform = sp_item_i2doc_affine(SP_ITEM(desktop->currentLayer())).inverse();
        sc->item->updateRepr();

        sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 5);
    }

    SnapManager &m = desktop->namedview->snap_manager;
    m.setup(desktop, true, sc->item);
    Geom::Point pt2g = to_2geom(p);
    m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, pt2g);

    Geom::Point const p0 = to_2geom(sp_desktop_dt2doc_xy_point(desktop, sc->center));
    Geom::Point const p1 = to_2geom(sp_desktop_dt2doc_xy_point(desktop, from_2geom(pt2g)));        
    
    SPSpiral *spiral = SP_SPIRAL(sc->item);

    Geom::Point const delta = p1 - p0;
    gdouble const rad = Geom::L2(delta);

    gdouble arg = Geom::atan2(delta) - 2.0*M_PI*spiral->revo;

    if (state & GDK_CONTROL_MASK) {
        arg = sp_round(arg, M_PI/snaps);
    }

    /* Fixme: these parameters should be got from dialog box */
    sp_spiral_position_set(spiral, p0[Geom::X], p0[Geom::Y],
                           /*expansion*/ sc->exp,
                           /*revolution*/ sc->revo,
                           rad, arg,
                           /*t0*/ sc->t0);

    /* status text */
    GString *rads = SP_PX_TO_METRIC_STRING(rad, desktop->namedview->getDefaultMetric());
    sc->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE,
                               _("<b>Spiral</b>: radius %s, angle %5g&#176;; with <b>Ctrl</b> to snap angle"),
                               rads->str, sp_round((arg + 2.0*M_PI*spiral->revo)*180/M_PI, 0.0001));
    g_string_free(rads, FALSE);
}