/** * @file * LPE perspective path effect implementation. */ /* Authors: * Maximilian Albert * Johan Engelen * * Copyright (C) 2007-2012 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ #include #include "persp3d.h" //#include "transf_mat_3x4.h" #include "document-private.h" #include "live_effects/lpe-perspective_path.h" #include "live_effects/lpeobject.h" #include "knot-holder-entity.h" #include "knotholder.h" #include // TODO due to internal breakage in glibmm headers, this must be last: #include namespace Inkscape { namespace LivePathEffect { namespace PP { class KnotHolderEntityOffset : public LPEKnotHolderEntity { public: KnotHolderEntityOffset(LPEPerspectivePath *effect) : LPEKnotHolderEntity(effect) {}; virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); virtual Geom::Point knot_get() const; }; } // namespace PP static Glib::ustring perspectiveID = _("First perspective"); LPEPerspectivePath::LPEPerspectivePath(LivePathEffectObject *lpeobject) : Effect(lpeobject), // initialise your parameters here: scalex(_("Scale x"), _("Scale factor in x direction"), "scalex", &wr, this, 1.0), scaley(_("Scale y"), _("Scale factor in y direction"), "scaley", &wr, this, 1.0), offsetx(_("Offset x"), _("Offset in x direction"), "offsetx", &wr, this, 0.0), offsety(_("Offset y"), _("Offset in y direction"), "offsety", &wr, this, 0.0), uses_plane_xy(_("Uses XY plane?"), _("If true, put the path on the left side of an imaginary box, otherwise on the right side"), "uses_plane_xy", &wr, this, true) { // register all your parameters here, so Inkscape knows which parameters this effect has: registerParameter( &scalex); registerParameter( &scaley); registerParameter( &offsetx); registerParameter( &offsety); registerParameter( &uses_plane_xy); concatenate_before_pwd2 = true; // don't split the path into its subpaths _provides_knotholder_entities = true; apply_to_clippath_and_mask = true; } LPEPerspectivePath::~LPEPerspectivePath() { } void LPEPerspectivePath::doOnApply(SPLPEItem const* lpeitem) { Persp3D *persp = persp3d_document_first_persp(lpeitem->document); if(persp == 0 ){ char *msg = _("You need a BOX 3D object"); Gtk::MessageDialog dialog(msg, false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK, true); dialog.run(); SPLPEItem * item = const_cast(lpeitem); item->removeCurrentPathEffect(false); } } void LPEPerspectivePath::doBeforeEffect (SPLPEItem const* lpeitem) { original_bbox(lpeitem, true); SPLPEItem * item = const_cast(lpeitem); Persp3D *persp = persp3d_document_first_persp(lpeitem->document); if(persp == 0 ){ char *msg = _("You need a BOX 3D object"); Gtk::MessageDialog dialog(msg, false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK, true); dialog.run(); return; } Proj::TransfMat3x4 pmat = persp->perspective_impl->tmat; Geom::Affine doc2d = Geom::Scale(1, -1) * Geom::Translate(0, item->document->getHeight().value("px")); pmat = pmat * doc2d; pmat.copy_tmat(tmat); } void LPEPerspectivePath::refresh(Gtk::Entry* perspective) { perspectiveID = perspective->get_text(); Persp3D *first = 0; Persp3D *persp = 0; for (auto& child: lpeobj->document->getDefs()->children) { if (SP_IS_PERSP3D(&child) && first == 0) { first = SP_PERSP3D(&child); } if (SP_IS_PERSP3D(&child) && strcmp(child.getId(), const_cast(perspectiveID.c_str())) == 0) { persp = SP_PERSP3D(&child); break; } } if(first == 0 ){ char *msg = _("You need a BOX 3D object"); Gtk::MessageDialog dialog(msg, false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK, true); dialog.run(); return; } if(persp == 0){ persp = first; char *msg = _("First perspective selected"); Gtk::MessageDialog dialog(msg, false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK, true); dialog.run(); perspectiveID = _("First perspective"); }else{ char *msg = _("Perspective changed"); Gtk::MessageDialog dialog(msg, false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK, true); dialog.run(); } Proj::TransfMat3x4 pmat = persp->perspective_impl->tmat; pmat = pmat * SP_ACTIVE_DESKTOP->doc2dt(); pmat.copy_tmat(tmat); }; Geom::Piecewise > LPEPerspectivePath::doEffect_pwd2 (Geom::Piecewise > const & pwd2_in) { using namespace Geom; Piecewise > path_a_pw = pwd2_in; // FIXME: the minus sign is there because the SVG coordinate system goes down; // remove this once we have unified coordinate systems path_a_pw = path_a_pw + Geom::Point(offsetx, -offsety); D2 > B = make_cuts_independent(path_a_pw); Piecewise preimage[4]; //Geom::Point orig = Geom::Point(bounds_X.min(), bounds_Y.middle()); //orig = Geom::Point(orig[X], sp_document_height(this->lpeobj->document) - orig[Y]); //double offset = uses_plane_xy ? boundingbox_X.extent() : 0.0; orig = Point(uses_plane_xy ? boundingbox_X.max() : boundingbox_X.min(), boundingbox_Y.middle()); /** g_print ("Orig: (%8.2f, %8.2f)\n", orig[X], orig[Y]); g_print ("B[1] - orig[1]: %8.2f\n", (B[1] - orig[1])[0].valueAt(0)); g_print ("B[0] - orig[0]: %8.2f\n", (B[0] - orig[0])[0].valueAt(0)); **/ if (uses_plane_xy) { preimage[0] = (-B[0] + orig[0]) * scalex / 200.0; preimage[1] = ( B[1] - orig[1]) * scaley / 400.0; preimage[2] = B[0] - B[0]; // hack! } else { preimage[0] = B[0] - B[0]; // hack! preimage[1] = (B[1] - orig[1]) * scaley / 400.0; preimage[2] = (B[0] - orig[0]) * scalex / 200.0; } /* set perspective origin to first point of path */ tmat[0][3] = orig[0]; tmat[1][3] = orig[1]; /** g_print ("preimage[1]: %8.2f\n", preimage[1][0].valueAt(0)); g_print ("preimage[2]: %8.2f\n", preimage[2][0].valueAt(0)); **/ Piecewise res[3]; for (int j = 0; j < 3; ++j) { res[j] = preimage[0] * tmat[j][0] + preimage[1] * tmat[j][1] + preimage[2] * tmat[j][2] + tmat[j][3]; } D2 > result(divide(res[0],res[2], 3), divide(res[1],res[2], 3)); Piecewise > output = sectionize(result); return output; } Gtk::Widget * LPEPerspectivePath::newWidget() { // use manage here, because after deletion of Effect object, others might still be pointing to this widget. Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox(Effect::newWidget()) ); vbox->set_border_width(5); std::vector::iterator it = param_vector.begin(); while (it != param_vector.end()) { if ((*it)->widget_is_visible) { Parameter * param = *it; Gtk::Widget * widg = dynamic_cast(param->param_newWidget()); Glib::ustring * tip = param->param_getTooltip(); if (widg) { vbox->pack_start(*widg, true, true, 2); if (tip) { widg->set_tooltip_text(*tip); } else { widg->set_tooltip_text(""); widg->set_has_tooltip(false); } } } ++it; } Gtk::HBox * perspectiveId = Gtk::manage(new Gtk::HBox(true,0)); #if WITH_GTKMM_3_10 Gtk::Label* labelPerspective = Gtk::manage(new Gtk::Label("Perspective ID:", Gtk::ALIGN_START, Gtk::ALIGN_START)); #else Gtk::Label* labelPerspective = Gtk::manage(new Gtk::Label("Perspective ID:", 0., 0.)); #endif Gtk::Entry* perspective = Gtk::manage(new Gtk::Entry()); perspective->set_text(perspectiveID); perspective->set_tooltip_text("Set the perspective ID to apply"); perspectiveId->pack_start(*labelPerspective, true, true, 2); perspectiveId->pack_start(*perspective, true, true, 2); vbox->pack_start(*perspectiveId, true, true, 2); Gtk::Button* apply3D = Gtk::manage(new Gtk::Button()); #if WITH_GTKMM_3_10 Gtk::Label *apply3DLabel = Gtk::manage(new Gtk::Label(_("Refresh perspective"), Gtk::ALIGN_START, Gtk::ALIGN_CENTER)); #else Gtk::Label *apply3DLabel = Gtk::manage(new Gtk::Label(_("Refresh perspective"), 0.0, 0.5)); #endif apply3D->add(*apply3DLabel); apply3D->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this,&LPEPerspectivePath::refresh),perspective)); Gtk::Widget* apply3DWidget = dynamic_cast(apply3D); apply3DWidget->set_tooltip_text("Refresh perspective"); vbox->pack_start(*apply3DWidget, true, true,2); return dynamic_cast(vbox); } void LPEPerspectivePath::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) { KnotHolderEntity *e = new PP::KnotHolderEntityOffset(this); e->create( NULL, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _("Adjust the origin") ); knotholder->add(e); }; namespace PP { void KnotHolderEntityOffset::knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) { using namespace Geom; LPEPerspectivePath* lpe = dynamic_cast(_effect); Geom::Point const s = snap_knot_position(p, state); lpe->offsetx.param_set_value((s - origin)[Geom::X]); lpe->offsety.param_set_value(-(s - origin)[Geom::Y]); // additional minus sign is due to coordinate system flipping // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating. sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true); } Geom::Point KnotHolderEntityOffset::knot_get() const { LPEPerspectivePath const *lpe = dynamic_cast(_effect); return lpe->orig + Geom::Point(lpe->offsetx, -lpe->offsety); } } // namespace PP } //namespace LivePathEffect } /* namespace Inkscape */ /* 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:fileencoding=utf-8:textwidth=99 :