// SPDX-License-Identifier: GPL-2.0-or-later /** \file * LPE implementation, see lpe-ruler.cpp. */ /* * Authors: * Maximilian Albert * * Copyright (C) Maximilian Albert 2008 * * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #include "live_effects/lpe-ruler.h" // TODO due to internal breakage in glibmm headers, this must be last: #include namespace Inkscape { namespace LivePathEffect { static const Util::EnumData MarkDirData[] = { {MARKDIR_LEFT , N_("Left"), "left"}, {MARKDIR_RIGHT , N_("Right"), "right"}, {MARKDIR_BOTH , N_("Both"), "both"}, }; static const Util::EnumDataConverter MarkDirTypeConverter(MarkDirData, sizeof(MarkDirData)/sizeof(*MarkDirData)); static const Util::EnumData BorderMarkData[] = { {BORDERMARK_NONE , NC_("Border mark", "None"), "none"}, {BORDERMARK_START , N_("Start"), "start"}, {BORDERMARK_END , N_("End"), "end"}, {BORDERMARK_BOTH , N_("Both"), "both"}, }; static const Util::EnumDataConverter BorderMarkTypeConverter(BorderMarkData, sizeof(BorderMarkData)/sizeof(*BorderMarkData)); LPERuler::LPERuler(LivePathEffectObject *lpeobject) : Effect(lpeobject), mark_distance(_("_Mark distance:"), _("Distance between successive ruler marks"), "mark_distance", &wr, this, 20.0), unit(_("Unit:"), _("Unit"), "unit", &wr, this), mark_length(_("Ma_jor length:"), _("Length of major ruler marks"), "mark_length", &wr, this, 14.0), minor_mark_length(_("Mino_r length:"), _("Length of minor ruler marks"), "minor_mark_length", &wr, this, 7.0), major_mark_steps(_("Major steps_:"), _("Draw a major mark every ... steps"), "major_mark_steps", &wr, this, 5), shift(_("Shift marks _by:"), _("Shift marks by this many steps"), "shift", &wr, this, 0), mark_dir(_("Mark direction:"), _("Direction of marks (when viewing along the path from start to end)"), "mark_dir", MarkDirTypeConverter, &wr, this, MARKDIR_LEFT), offset(_("_Offset:"), _("Offset of first mark"), "offset", &wr, this, 0.0), border_marks(_("Border marks:"), _("Choose whether to draw marks at the beginning and end of the path"), "border_marks", BorderMarkTypeConverter, &wr, this, BORDERMARK_BOTH) { registerParameter(&unit); registerParameter(&mark_distance); registerParameter(&mark_length); registerParameter(&minor_mark_length); registerParameter(&major_mark_steps); registerParameter(&shift); registerParameter(&offset); registerParameter(&mark_dir); registerParameter(&border_marks); major_mark_steps.param_make_integer(); major_mark_steps.param_set_range(1, 1000); shift.param_make_integer(); mark_length.param_set_increments(1.0, 10.0); minor_mark_length.param_set_increments(1.0, 10.0); offset.param_set_increments(1.0, 10.0); } LPERuler::~LPERuler() = default; Geom::Point LPERuler::n_major; Geom::Point LPERuler::n_minor; Geom::Piecewise > LPERuler::ruler_mark(Geom::Point const &A, Geom::Point const &n, MarkType const &marktype) { using namespace Geom; double real_mark_length = mark_length; SPDocument *document = getSPDoc(); if (document) { real_mark_length = Inkscape::Util::Quantity::convert(real_mark_length, unit.get_abbreviation(), document->getDisplayUnit()->abbr.c_str()); } double real_minor_mark_length = minor_mark_length; if (document) { real_minor_mark_length = Inkscape::Util::Quantity::convert(real_minor_mark_length, unit.get_abbreviation(), document->getDisplayUnit()->abbr.c_str()); } n_major = real_mark_length * n; n_minor = real_minor_mark_length * n; if (mark_dir == MARKDIR_BOTH) { n_major = n_major * 0.5; n_minor = n_minor * 0.5; } Point C, D; switch (marktype) { case MARK_MAJOR: C = A; D = A + n_major; if (mark_dir == MARKDIR_BOTH) C -= n_major; break; case MARK_MINOR: C = A; D = A + n_minor; if (mark_dir == MARKDIR_BOTH) C -= n_minor; break; default: // do nothing break; } Piecewise > seg(D2(SBasis(C[X], D[X]), SBasis(C[Y], D[Y]))); return seg; } Geom::Piecewise > LPERuler::doEffect_pwd2 (Geom::Piecewise > const & pwd2_in) { using namespace Geom; const int mminterval = static_cast(major_mark_steps); const int i_shift = static_cast(shift) % mminterval; int sign = (mark_dir == MARKDIR_RIGHT ? 1 : -1 ); Piecewise >output(pwd2_in); Piecewise >speed = derivative(pwd2_in); Piecewise arclength = arcLengthSb(pwd2_in); double totlength = arclength.lastValue(); //find at which times to draw a mark: std::vector s_cuts; double real_mark_distance = mark_distance; SPDocument *document = getSPDoc(); if (document) { real_mark_distance = Inkscape::Util::Quantity::convert(real_mark_distance, unit.get_abbreviation(), document->getDisplayUnit()->abbr.c_str()); } double real_offset = offset; if (document) { real_offset = Inkscape::Util::Quantity::convert(real_offset, unit.get_abbreviation(), document->getDisplayUnit()->abbr.c_str()); } for (double s = real_offset; s > roots = multi_roots(arclength, s_cuts); std::vector t_cuts; for (auto & root : roots){ //FIXME: 2geom multi_roots solver seem to sometimes "repeat" solutions. //Here, we are supposed to have one and only one solution for each s. if(root.size()>0) t_cuts.push_back(root[0]); } //draw the marks for (size_t i = 0; i < t_cuts.size(); i++) { Point A = pwd2_in(t_cuts[i]); Point n = rot90(unit_vector(speed(t_cuts[i])))*sign; if (static_cast(i % mminterval) == i_shift) { output.concat (ruler_mark(A, n, MARK_MAJOR)); } else { output.concat (ruler_mark(A, n, MARK_MINOR)); } } //eventually draw a mark at start if ((border_marks == BORDERMARK_START || border_marks == BORDERMARK_BOTH) && (offset != 0.0 || i_shift != 0)){ Point A = pwd2_in.firstValue(); Point n = rot90(unit_vector(speed.firstValue()))*sign; output.concat (ruler_mark(A, n, MARK_MAJOR)); } //eventually draw a mark at end if (border_marks == BORDERMARK_END || border_marks == BORDERMARK_BOTH){ Point A = pwd2_in.lastValue(); Point n = rot90(unit_vector(speed.lastValue()))*sign; //speed.lastValue() is sometimes wrong when the path is closed: a tiny line seg might added at the end to fix rounding errors... //TODO: Find a better fix!! (How do we know if the path was closed?) if ( A == pwd2_in.firstValue() && speed.segs.size() > 1 && speed.segs.back()[X].size() <= 1 && speed.segs.back()[Y].size() <= 1 && speed.segs.back()[X].tailError(0) <= 1e-10 && speed.segs.back()[Y].tailError(0) <= 1e-10 ){ n = rot90(unit_vector(speed.segs[speed.segs.size()-2].at1()))*sign; } output.concat (ruler_mark(A, n, MARK_MAJOR)); } return output; } } //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 :