/** * @file * Roughen LPE implementation. Creates roughen paths. */ /* Authors: * Jabier Arraiza Cenoz * * Thanks to all people involved specialy to Josh Andler for the idea and to the * original extensions authors. * * Copyright (C) 2014 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ #include #include "desktop.h" #include "live_effects/lpe-roughen.h" #include "display/curve.h" #include "live_effects/parameter/parameter.h" #include "helper/geom.h" #include #include #include namespace Inkscape { namespace LivePathEffect { static const Util::EnumData DivisionMethodData[DM_END] = { { DM_SEGMENTS, N_("By number of segments"), "segments" }, { DM_SIZE, N_("By max. segment size"), "size" } }; static const Util::EnumDataConverter DMConverter(DivisionMethodData, DM_END); LPERoughen::LPERoughen(LivePathEffectObject *lpeobject) : Effect(lpeobject), // initialise your parameters here: unit(_("Unit"), _("Unit"), "unit", &wr, this), method(_("Method"), _("Division method"), "method", DMConverter, &wr, this, DM_SEGMENTS), maxSegmentSize(_("Max. segment size"), _("Max. segment size"), "maxSegmentSize", &wr, this, 10.), segments(_("Number of segments"), _("Number of segments"), "segments", &wr, this, 2), displaceX(_("Max. displacement in X"), _("Max. displacement in X"), "displaceX", &wr, this, 10.), displaceY(_("Max. displacement in Y"), _("Max. displacement in Y"), "displaceY", &wr, this, 10.), globalRandomize(_("Global randomize"), _("Global randomize"), "globalRandomize", &wr, this, 1.), shiftNodes(_("Shift nodes"), _("Shift nodes"), "shiftNodes", &wr, this, true), shiftNodeHandles(_("Shift node handles"), _("Shift node handles"), "shiftNodeHandles", &wr, this, true) { registerParameter(&unit); registerParameter(&method); registerParameter(&maxSegmentSize); registerParameter(&segments); registerParameter(&displaceX); registerParameter(&displaceY); registerParameter(&globalRandomize); registerParameter(&shiftNodes); registerParameter(&shiftNodeHandles); displaceX.param_set_range(0., Geom::infinity()); displaceY.param_set_range(0., Geom::infinity()); globalRandomize.param_set_range(0., Geom::infinity()); maxSegmentSize.param_set_range(0., Geom::infinity()); maxSegmentSize.param_set_increments(1, 1); maxSegmentSize.param_set_digits(1); segments.param_set_range(1, Geom::infinity()); segments.param_set_increments(1, 1); segments.param_set_digits(0); } LPERoughen::~LPERoughen() {} void LPERoughen::doBeforeEffect(SPLPEItem const *lpeitem) { displaceX.resetRandomizer(); displaceY.resetRandomizer(); globalRandomize.resetRandomizer(); srand(1); SPLPEItem * item = const_cast(lpeitem); item->apply_to_clippath(item); item->apply_to_mask(item); } Gtk::Widget *LPERoughen::newWidget() { Gtk::VBox *vbox = Gtk::manage(new Gtk::VBox(Effect::newWidget())); vbox->set_border_width(5); vbox->set_homogeneous(false); vbox->set_spacing(2); 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()); if (param->param_key == "unit") { Gtk::Label *unitLabel = Gtk::manage(new Gtk::Label( Glib::ustring(_("Roughen unit")), Gtk::ALIGN_START)); unitLabel->set_use_markup(true); vbox->pack_start(*unitLabel, false, false, 2); vbox->pack_start(*Gtk::manage(new Gtk::HSeparator()), Gtk::PACK_EXPAND_WIDGET); } if (param->param_key == "method") { Gtk::Label *methodLabel = Gtk::manage(new Gtk::Label( Glib::ustring(_("Add nodes Subdivide each segment")), Gtk::ALIGN_START)); methodLabel->set_use_markup(true); vbox->pack_start(*methodLabel, false, false, 2); vbox->pack_start(*Gtk::manage(new Gtk::HSeparator()), Gtk::PACK_EXPAND_WIDGET); } if (param->param_key == "displaceX") { Gtk::Label *displaceXLabel = Gtk::manage(new Gtk::Label( Glib::ustring(_("Jitter nodes Move nodes/handles")), Gtk::ALIGN_START)); displaceXLabel->set_use_markup(true); vbox->pack_start(*displaceXLabel, false, false, 2); vbox->pack_start(*Gtk::manage(new Gtk::HSeparator()), Gtk::PACK_EXPAND_WIDGET); } if (param->param_key == "globalRandomize") { Gtk::Label *displaceXLabel = Gtk::manage(new Gtk::Label( Glib::ustring(_("Extra roughen Add a extra layer of rough")), Gtk::ALIGN_START)); displaceXLabel->set_use_markup(true); vbox->pack_start(*displaceXLabel, false, false, 2); vbox->pack_start(*Gtk::manage(new Gtk::HSeparator()), Gtk::PACK_EXPAND_WIDGET); } 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; } return dynamic_cast(vbox); } double LPERoughen::sign(double randNumber) { if (rand() % 100 < 49) { randNumber *= -1.; } return randNumber; } Geom::Point LPERoughen::randomize() { Inkscape::Util::Unit const *svg_units = SP_ACTIVE_DESKTOP->namedview->svg_units; double displaceXParsed = Inkscape::Util::Quantity::convert( displaceX * globalRandomize, unit.get_abbreviation(), svg_units->abbr); double displaceYParsed = Inkscape::Util::Quantity::convert( displaceY * globalRandomize, unit.get_abbreviation(), svg_units->abbr); Geom::Point output = Geom::Point(sign(displaceXParsed), sign(displaceYParsed)); return output; } void LPERoughen::doEffect(SPCurve *curve) { Geom::PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(curve->get_pathvector()); curve->reset(); Inkscape::Util::Unit const *svg_units = SP_ACTIVE_DESKTOP->namedview->svg_units; for (Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) { if (path_it->empty()) continue; Geom::Path::const_iterator curve_it1 = path_it->begin(); Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); Geom::Path::const_iterator curve_endit = path_it->end_default(); SPCurve *nCurve = new SPCurve(); if (path_it->closed()) { const Geom::Curve &closingline = path_it->back_closed(); if (are_near(closingline.initialPoint(), closingline.finalPoint())) { curve_endit = path_it->end_open(); } } Geom::Point initialMove(0, 0); if (shiftNodes) { initialMove = randomize(); } Geom::Point initialPoint = curve_it1->initialPoint() + initialMove; nCurve->moveto(initialPoint); Geom::Point A0(0, 0); Geom::Point A1(0, 0); Geom::Point A2(0, 0); Geom::Point A3(0, 0); bool first = true; while (curve_it1 != curve_endit) { Geom::CubicBezier const *cubic = NULL; A0 = curve_it1->initialPoint(); A1 = curve_it1->initialPoint(); A2 = curve_it1->finalPoint(); A3 = curve_it1->finalPoint(); cubic = dynamic_cast(&*curve_it1); if (cubic) { A1 = (*cubic)[1]; if (shiftNodes && first) { A1 = (*cubic)[1] + initialMove; } A2 = (*cubic)[2]; nCurve->curveto(A1, A2, A3); } else { nCurve->lineto(A3); } double length = Inkscape::Util::Quantity::convert( curve_it1->length(0.001), svg_units->abbr, unit.get_abbreviation()); std::size_t splits = 0; if (method == DM_SEGMENTS) { splits = segments; } else { splits = ceil(length / maxSegmentSize); } for (unsigned int t = splits; t >= 1; t--) { if (t == 1 && splits != 1) { continue; } const SPCurve *tmp; if (splits == 1) { tmp = jitter(nCurve->last_segment()); } else { tmp = addNodesAndJitter(nCurve->last_segment(), 1. / t); } if (nCurve->get_segment_count() > 1) { nCurve->backspace(); nCurve->append_continuous(tmp, 0.001); } else { nCurve = tmp->copy(); } delete tmp; } ++curve_it1; if(curve_it2 != curve_endit) { ++curve_it2; } first = false; } if (path_it->closed()) { nCurve->closepath_current(); } curve->append(nCurve, false); nCurve->reset(); delete nCurve; } } SPCurve *LPERoughen::addNodesAndJitter(const Geom::Curve *A, double t) { SPCurve *out = new SPCurve(); Geom::CubicBezier const *cubic = dynamic_cast(&*A); Geom::Point A1(0, 0); Geom::Point A2(0, 0); Geom::Point A3(0, 0); Geom::Point B1(0, 0); Geom::Point B2(0, 0); Geom::Point B3(0, 0); if (shiftNodes) { A3 = randomize(); B3 = randomize(); } if (shiftNodeHandles) { A1 = randomize(); A2 = randomize(); B1 = randomize(); B2 = randomize(); } else { A2 = A3; B1 = A3; B2 = B3; } if (cubic) { std::pair div = cubic->subdivide(t); std::vector seg1 = div.first.points(), seg2 = div.second.points(); out->moveto(seg1[0]); out->curveto(seg1[1] + A1, seg1[2] + A2, seg1[3] + A3); out->curveto(seg2[1] + B1, seg2[2], seg2[3]); } else if (shiftNodeHandles) { out->moveto(A->initialPoint()); out->curveto(A->pointAt(t / 3) + A1, A->pointAt((t / 3) * 2) + A2, A->pointAt(t) + A3); out->curveto(A->pointAt(t + (t / 3)) + B1, A->pointAt(t + ((t / 3) * 2)), A->finalPoint()); } else { out->moveto(A->initialPoint()); out->lineto(A->pointAt(t) + A3); out->lineto(A->finalPoint()); } return out; } SPCurve *LPERoughen::jitter(const Geom::Curve *A) { SPCurve *out = new SPCurve(); Geom::CubicBezier const *cubic = dynamic_cast(&*A); Geom::Point A1(0, 0); Geom::Point A2(0, 0); Geom::Point A3(0, 0); if (shiftNodes) { A3 = randomize(); } if (shiftNodeHandles) { A1 = randomize(); A2 = randomize(); } else { A2 = A3; } if (cubic) { out->moveto((*cubic)[0]); out->curveto((*cubic)[1] + A1, (*cubic)[2] + A2, (*cubic)[3] + A3); } else if (shiftNodeHandles) { out->moveto(A->initialPoint()); out->curveto(A->pointAt(0.3333) + A1, A->pointAt(0.6666) + A2, A->finalPoint() + A3); } else { out->moveto(A->initialPoint()); out->lineto(A->finalPoint() + A3); } return out; } Geom::Point LPERoughen::tpoint(Geom::Point A, Geom::Point B, double t) { using Geom::X; using Geom::Y; return Geom::Point(A[X] + t * (B[X] - A[X]), A[Y] + t * (B[Y] - A[Y])); } }; //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 :