21#include <glibmm/i18n.h>
38namespace LivePathEffect {
72 std::vector<Geom::Interval> ret;
73 if (!domain.empty()) {
74 double min = domain.front().min();
75 double max = domain.back().max();
79 for (
auto i : domain){
80 std::optional<Geom::Interval> I1i =
intersect(i,I1);
81 if (I1i && !I1i->isSingular()) ret.push_back(*I1i);
82 std::optional<Geom::Interval> I2i =
intersect(i,I2);
83 if (I2i && !I2i->isSingular()) ret.push_back(*I2i);
94 double const ta,
double const width){
105 std::vector<double> times;
110 std::vector<double> times_i, temptimes;
112 times_i.insert(times_i.end(), temptimes.begin(), temptimes.end() );
114 times_i.insert(times_i.end(), temptimes.begin(), temptimes.end() );
116 times_i.insert(times_i.end(), temptimes.begin(), temptimes.end() );
118 times_i.insert(times_i.end(), temptimes.begin(), temptimes.end() );
119 for (
double & k : times_i){
122 times.insert(times.end(), times_i.begin(), times_i.end() );
124 std::sort( times.begin(), times.end() );
125 std::vector<double>::iterator new_end = std::unique( times.begin(), times.end() );
126 times.resize( new_end - times.begin() );
131 unsigned rk = upper_bound( times.begin(), times.end(), ta ) - times.begin();
132 if ( rk < times.size() )
134 else if ( patha.
closed() )
135 tmax = times[0]+period;
139 else if ( patha.
closed() )
140 tmin = times.back()-period;
158 for(
unsigned i=0; i<
paths.size(); i++){
160 for(
unsigned j=i; j<
paths.size(); j++){
162 std::vector<std::pair<double,double> > times;
163 if ( (i==j) && (ii==jj) ) {
180 for (
auto & time : times){
182 if ( !std::isnan(time.first) && !std::isnan(time.second) ){
184 if ( (i==j) && (fabs(time.first+ii - time.second-jj) <= zero) )
190 && (fabs(time.first) <= zero)
191 && (fabs(time.second - 1) <= zero) )
196 cp.
pt =
paths[i][ii].pointAt(time.first);
201 cp.
ti = time.first + ii;
202 cp.
tj = time.second + jj;
205 std::cerr<<
"ooops: find_(self)_intersections returned NaN:" << std::endl;
213 for(
unsigned i=0; i<
paths.size(); i++){
214 std::map < double, unsigned > cuts;
215 for(
unsigned k=0; k<
size(); k++){
217 if (cp.
i == i) cuts[cp.
ti] = k;
218 if (cp.
j == i) cuts[cp.
tj] = k;
221 for (
auto & cut : cuts){
222 if ( ((*
this)[cut.second].i == i) && ((*this)[cut.second].ti == cut.first) ){
223 (*this)[cut.second].
ni = count;
225 (*this)[cut.second].nj = count;
234 if ( (input.size() > 0) && (input.size()%9 == 0) ){
235 using namespace Geom;
236 for(
unsigned n=0; n<input.size(); ){
238 cp.
pt[
X] = input[n++];
239 cp.
pt[
Y] = input[n++];
246 cp.
sign = input[n++];
255 using namespace Geom;
256 std::vector<double>
result;
257 for(
unsigned n=0; n<
size(); n++){
261 result.push_back(
double(cp.
i));
262 result.push_back(
double(cp.
j));
276 for (
unsigned k=0; k<
size(); k++){
277 if ( ( ((*
this)[k].i==i) && ((*
this)[k].ni==ni) )
278 || ( ((*this)[k].j==i) && ((*
this)[k].nj==ni) ) )
283 g_warning(
"LPEKnotNS::CrossingPoints::get error. %uth crossing along string %u not found.",ni,i);
292 unsigned result = cpts.size();
293 for (
unsigned k=0; k<cpts.size(); k++){
294 double dist_k =
Geom::L2(p-cpts[k].pt);
295 if ( (dist < 0) || (dist > dist_k) ) {
308 bool topo_changed =
false;
309 for (
unsigned n=0; n <
size(); n++){
310 if ( (n < other.size())
311 && (other[n].i == (*this)[n].i)
312 && (other[n].j == (*
this)[n].j)
313 && (other[n].ni == (*
this)[n].ni)
314 && (other[n].nj == (*
this)[n].nj) )
316 (*this)[n].sign = other[n].sign;
325 for (
unsigned n=0; n <
size(); n++){
328 if (idx < other.size()) {
329 (*this)[n].sign = other[idx].sign;
331 (*this)[n].sign = default_value;
350 interruption_width(_(
"_Gap length:"), _(
"Size of hidden region of lower string"),
"interruption_width", &wr, this,
352 , prop_to_stroke_width(
353 _(
"_In units of stroke width"),
354 _(
"Gap width is given in multiples of stroke width. When unchecked, document units are used."),
355 "prop_to_stroke_width", &wr, this, true)
356 , both(_(
"_Gaps in both"), _(
"At path intersections, both parts will have a gap"),
"both", &wr, this, false)
357 , inverse_width(_(
"_Groups: Inverse"), _(
"Use other stroke width, useful in groups with different stroke widths"),
358 "inverse_width", &wr, this, false)
359 , add_stroke_width(
"St_roke width",
"Add the stroke width to the gap size",
"add_stroke_width", &wr, this,
360 "inkscape_1.0_and_up", true)
361 , add_other_stroke_width(
"_Crossing path stroke width",
"Add crossed stroke width to the gap size",
362 "add_other_stroke_width", &wr, this,
"inkscape_1.0_and_up", true)
363 , switcher_size(_(
"S_witcher size:"), _(
"Orientation indicator/switcher size"),
"switcher_size", &wr, this, 15)
364 , crossing_points_vector(_(
"Crossing Signs"), _(
"Crossing signs"),
"crossing_points_vector", &wr, this)
368 , selectedCrossing(0)
404 using namespace Geom;
411 for (
const auto & comp : original_pathv){
417 gint precision = prefs->
getInt(
"/options/svgoutput/numericprecision");
418 prefs->
setInt(
"/options/svgoutput/numericprecision", 4);
423 prefs->
setInt(
"/options/svgoutput/numericprecision", precision);
424 if (i0 ==
gpaths.
size() ) {THROW_EXCEPTION(
"lpe-knot error: group member not recognized");}
426 std::vector<Interval> dom;
429 if ((crossing_point.i == i0) || (crossing_point.j == i0)) {
430 unsigned i = crossing_point.i;
431 unsigned j = crossing_point.j;
432 double ti = crossing_point.ti;
433 double tj = crossing_point.tj;
437 t = modf(ti, &curveidx);
440 std::vector<Point> flag_i =
gpaths[i][curveidx].pointAndDerivatives(t,1);
442 t = modf(tj, &curveidx);
445 std::vector<Point> flag_j =
gpaths[j][curveidx].pointAndDerivatives(t,1);
448 int geom_sign = (
cross(flag_i[1], flag_j[1]) < 0 ? 1 : -1);
449 bool i0_is_under =
false;
451 if ( crossing_point.sign * geom_sign > 0 ){
452 i0_is_under = ( i == i0 );
454 else if (crossing_point.sign * geom_sign < 0) {
459 i0_is_under = crossing_point.sign != 0 &&
both ? true : i0_is_under;
460 if (i0_is_under && j == i0) {
463 if (!(i == j && !
both && crossing_point.sign * geom_sign > 0)) {
466 std::swap(flag_i, flag_j);
486 if (hidden.
max() > period ) hidden -= period;
493 if (crossing_point.i == i0 && crossing_point.j == i0 && crossing_point.sign != 0 &&
497 if (hidden.
max() > period)
499 if (hidden.
min() < 0) {
517 unsigned beg_comp = 0, end_comp = dom.size();
519 if ( dom.size() == 1){
533 for (
unsigned comp = beg_comp; comp < end_comp; comp++){
546 auto lpeitem_mutable =
const_cast<SPLPEItem *
>(lpeitem);
548 if (
auto group = cast<SPGroup>(lpeitem_mutable)) {
549 std::vector<SPItem*> item_list = group->item_list();
550 for (
auto subitem : item_list) {
551 if (is<SPLPEItem>(subitem)) {
555 }
else if (
auto shape = cast<SPShape>(lpeitem)) {
559 for (
const auto & subpath : subpaths){
560 paths.push_back(subpath);
572 using namespace Geom;
575 if (is<SPPath>(lpeitem)) {
576 supplied_path = cast<SPPath>(lpeitem)->curve()->get_pathvector();
637 using namespace Geom;
643 svgd =
"m -7.07,7.07 c 3.9,3.91 10.24,3.91 14.14,0 3.91,-3.9 3.91,-10.24 0,-14.14 -3.9,-3.91 -10.24,-3.91 -14.14,0 l 2.83,-4.24 0.7,2.12";
646 svgd =
"m 7.07,7.07 c -3.9,3.91 -10.24,3.91 -14.14,0 -3.91,-3.9 -3.91,-10.24 0,-14.14 3.9,-3.91 10.24,-3.91 14.14,0 l -2.83,-4.24 -0.7,2.12";
649 svgd =
"M 10,0 C 10,5.52 5.52,10 0,10 -5.52,10 -10,5.52 -10,0 c 0,-5.52 4.48,-10 10,-10 5.52,0 10,4.48 10,10 z";
653 hp_vec.push_back(pathv);
660 _(
"Drag to select a crossing, click to flip it, Shift + click to change all crossings, Ctrl + click to "
661 "reset and change all crossings"));
678KnotHolderEntityCrossingSwitcher::knot_get()
const
680 LPEKnot
const *lpe =
dynamic_cast<LPEKnot const*
>(_effect);
685KnotHolderEntityCrossingSwitcher::knot_click(guint state)
687 LPEKnot* lpe =
dynamic_cast<LPEKnot *
>(_effect);
689 if (s < lpe->crossing_points.size()){
690 if (state & GDK_SHIFT_MASK){
691 for (
auto &crossing_point : lpe->crossing_points) {
692 crossing_point.sign = ((crossing_point.sign + 2) % 3) - 1;
695 else if (state & GDK_CONTROL_MASK) {
696 int sign = lpe->crossing_points[s].sign;
697 for (
auto &crossing_point : lpe->crossing_points) {
698 crossing_point.sign = ((
sign + 2) % 3) - 1;
701 int sign = lpe->crossing_points[s].sign;
702 lpe->crossing_points[s].sign = ((
sign+2)%3)-1;
705 lpe->crossing_points_vector.param_set_and_write_new_value(lpe->crossing_points.to_vector());
706 lpe->makeUndoDone(_(
"Change knot crossing"));
Basic intersection routines.
3x3 matrix representing an affine transformation.
Affine inverse() const
Compute the inverse matrix.
Abstract continuous curve on a plane defined on [0,1].
virtual Point initialPoint() const =0
Retrieve the start of the curve.
virtual Point finalPoint() const =0
Retrieve the end of the curve.
Adaptor that creates 2D functions from 1D ones.
Range of real numbers that is never empty.
size_type size() const
Get the number of paths in the vector.
void push_back(Path const &path)
Append a path at the end.
Path & at(size_type index)
void clear()
Remove all paths from the vector.
Sequence of contiguous curves, aka spline.
bool closed() const
Check whether the path is closed.
void setStitching(bool x)
Enable or disable the throwing of exceptions when stitching discontinuities.
Curve const & back_closed() const
size_type size_open() const
Size without the closing segment, even if the path is closed.
size_type size_default() const
Natural size of the path.
Sequence::size_type size_type
void append(Curve *curve)
Add a new curve to the end of the path.
Two-dimensional point that doubles as a vector.
constexpr Point cw() const
Return a point like this point but rotated +90 degrees.
void param_setValue(std::vector< StorageType > const &new_vector)
std::vector< StorageType > const & data() const
void registerParameter(Parameter *param)
bool _provides_knotholder_entities
void original_bbox(SPLPEItem const *lpeitem, bool absolute=false, bool clip_mask=false, Geom::Affine base_transform=Geom::identity())
const Glib::ustring get_value() const
void inherit_signs(CrossingPoints const &from_other, int default_value=1)
CrossingPoint get(unsigned const i, unsigned const ni)
std::vector< double > to_vector()
Geom::PathVector supplied_path
HiddenParam add_other_stroke_width
friend class KnotHolderEntityCrossingSwitcher
unsigned selectedCrossing
ScalarParam interruption_width
LPEKnotNS::CrossingPoints crossing_points
LPEKnot(LivePathEffectObject *lpeobject)
void doBeforeEffect(SPLPEItem const *lpeitem) override
Is performed each time before the effect is updated.
BoolParam prop_to_stroke_width
void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) override
void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector< Geom::PathVector > &hp_vec) override
Add possible canvas indicators (i.e., helperpaths other than the original path) to hp_vec This functi...
HiddenParam add_stroke_width
ArrayParam< double > crossing_points_vector
std::vector< double > gstroke_widths
ScalarParam switcher_size
Geom::PathVector doEffect_path(Geom::PathVector const &input_path) override
Preference storage class.
static Preferences * get()
Access the singleton Preferences object.
int getInt(Glib::ustring const &pref_path, int def=0)
Retrieve an integer.
void setInt(Glib::ustring const &pref_path, int value)
Set an integer value.
KnotHolderEntity definition.
virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, unsigned state)=0
void create(SPDesktop *desktop, SPItem *item, KnotHolder *parent, Inkscape::CanvasItemCtrlType type=Inkscape::CANVAS_ITEM_CTRL_TYPE_DEFAULT, Glib::ustring const &name="unknown", char const *tip="", uint32_t color=0xffffff00)
virtual void knot_click(unsigned)
void add(KnotHolderEntity *e)
Wrapper around a Geom::PathVector object.
Base class for visual SVG elements.
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
T< SPAttr::STROKE_WIDTH, SPILength > stroke_width
stroke-width
constexpr Coord infinity()
Get a value representing infinity.
constexpr Coord EPSILON
Default "acceptably small" value.
Geom::PathVector pathv_to_linear_and_cubic_beziers(Geom::PathVector const &pathv)
Specific geometry functions for Inkscape, not provided my lib2geom.
LPE knot effect implementation, see lpe-knot.cpp.
vector< vector< Point > > paths
Various utility functions.
MultiDegree< n > max(MultiDegree< n > const &p, MultiDegree< n > const &q)
Returns the maximal degree appearing in the two arguments for each variables.
Affine from_basis(const Point &x_basis, const Point &y_basis, const Point &offset=Point(0, 0))
Creates a Affine given an axis and origin point.
static float sign(double number)
Returns +1 for positive numbers, -1 for negative numbers, and 0 otherwise.
std::vector< double > roots(SBasis const &s)
void find_intersections(std::vector< std::pair< double, double > > &xs, D2< Bezier > const &A, D2< Bezier > const &B, double precision=EPSILON)
Bezier portion(const Bezier &a, double from, double to)
Piecewise< SBasis > cross(Piecewise< D2< SBasis > > const &a, Piecewise< D2< SBasis > > const &b)
SBasis L2(D2< SBasis > const &a, unsigned k)
Piecewise< SBasis > min(SBasis const &f, SBasis const &g)
Return the more negative of the two functions pointwise.
SBasis toSBasis(SBasisN< 1 > f)
Point unit_vector(Point const &a)
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
std::vector< Point > intersect(const xAx &C1, const xAx &C2)
void find_self_intersections(std::vector< std::pair< double, double > > &xs, D2< SBasis > const &A, double precision=EPSILON)
static unsigned idx_of_nearest(CrossingPoints const &cpts, Geom::Point const &p)
static void collectPathsAndWidths(SPLPEItem const *lpeitem, Geom::PathVector &paths, std::vector< double > &stroke_widths)
static std::vector< Geom::Interval > complementOf(Geom::Interval I, std::vector< Geom::Interval > domain)
static Geom::Interval findShadowedTime(Geom::Path const &patha, std::vector< Geom::Point > const &pt_and_dir, double const ta, double const width)
static Geom::Path::size_type size_nondegenerate(Geom::Path const &path)
Helper class to stream background task notifications as a series of messages.
@ CANVAS_ITEM_CTRL_TYPE_LPE
Singleton class to access the preferences file in a convenient way.
void sp_lpe_item_update_patheffect(SPLPEItem *lpeitem, bool wholetree, bool write, bool with_satellites)
Calls any registered handlers for the update_patheffect action.
SPStyle - a style object for SPItem objects.
Geom::PathVector sp_svg_read_pathv(char const *str)
static void sp_svg_write_path(Inkscape::SVG::PathString &str, Geom::Path const &p, bool normalize=false)