Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
piecewise.h
Go to the documentation of this file.
1/*
4 * Copyright 2007 Michael Sloan <mgsloan@gmail.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it either under the terms of the GNU Lesser General Public
8 * License version 2.1 as published by the Free Software Foundation
9 * (the "LGPL") or, at your option, under the terms of the Mozilla
10 * Public License Version 1.1 (the "MPL"). If you do not alter this
11 * notice, a recipient may use your version of this file under either
12 * the MPL or the LGPL.
13 *
14 * You should have received a copy of the LGPL along with this library
15 * in the file COPYING-LGPL-2.1; if not, output to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 * You should have received a copy of the MPL along with this library
18 * in the file COPYING-MPL-1.1
19 *
20 * The contents of this file are subject to the Mozilla Public License
21 * Version 1.1 (the "License"); you may not use this file except in
22 * compliance with the License. You may obtain a copy of the License at
23 * http://www.mozilla.org/MPL/
24 *
25 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27 * the specific language governing rights and limitations.
28 *
29 */
30
31#ifndef LIB2GEOM_SEEN_PIECEWISE_H
32#define LIB2GEOM_SEEN_PIECEWISE_H
33
34#include <vector>
35#include <map>
36#include <utility>
37#include <boost/concept_check.hpp>
38#include <2geom/concepts.h>
39#include <2geom/math-utils.h>
40#include <2geom/sbasis.h>
41
42
43namespace Geom {
70template <typename T>
71class Piecewise {
73
74 public:
75 std::vector<double> cuts;
76 std::vector<T> segs;
77 //segs[i] stretches from cuts[i] to cuts[i+1].
78
80
81 explicit Piecewise(const T &s) {
82 push_cut(0.);
83 push_seg(s);
84 push_cut(1.);
85 }
86
87 unsigned input_dim(){return 1;}
88
89 typedef typename T::output_type output_type;
90
91 explicit Piecewise(const output_type & v) {
92 push_cut(0.);
93 push_seg(T(v));
94 push_cut(1.);
95 }
96
97 inline void reserve(unsigned i) { segs.reserve(i); cuts.reserve(i + 1); }
98
99 inline T const& operator[](unsigned i) const { return segs[i]; }
100 inline T& operator[](unsigned i) { return segs[i]; }
101 inline output_type operator()(double t) const { return valueAt(t); }
102 inline output_type valueAt(double t) const {
103 unsigned n = segN(t);
104 return segs[n](segT(t, n));
105 }
106 inline output_type firstValue() const {
107 return valueAt(cuts.front());
108 }
109 inline output_type lastValue() const {
110 return valueAt(cuts.back());
111 }
112
116 std::vector<output_type> valueAndDerivatives(double t, unsigned n_derivs) const {
117 unsigned n = segN(t);
118 std::vector<output_type> ret, val = segs[n].valueAndDerivatives(segT(t, n), n_derivs);
119 double mult = 1;
120 for(unsigned i = 0; i < val.size(); i++) {
121 ret.push_back(val[i] * mult);
122 mult /= cuts[n+1] - cuts[n];
123 }
124 return ret;
125 }
126
127 //TODO: maybe it is not a good idea to have this?
130
131 inline unsigned size() const { return segs.size(); }
132 inline bool empty() const { return segs.empty(); }
133 inline void clear() {
134 segs.clear();
135 cuts.clear();
136 }
137
141 inline void push(const T &s, double to) {
142 assert(cuts.size() - segs.size() == 1);
143 push_seg(s);
144 push_cut(to);
145 }
146 inline void push(T &&s, double to) {
147 assert(cuts.size() - segs.size() == 1);
148 push_seg(s);
149 push_cut(to);
150 }
151 //Convenience/implementation hiding function to add cuts.
152 inline void push_cut(double c) {
153 ASSERT_INVARIANTS(cuts.empty() || c > cuts.back());
154 cuts.push_back(c);
155 }
156 //Convenience/implementation hiding function to add segments.
157 inline void push_seg(const T &s) { segs.push_back(s); }
158 inline void push_seg(T &&s) { segs.emplace_back(s); }
159
163 inline unsigned segN(double t, int low = 0, int high = -1) const {
164 high = (high == -1) ? size() : high;
165 if(t < cuts[0]) return 0;
166 if(t >= cuts[size()]) return size() - 1;
167 while(low < high) {
168 int mid = (high + low) / 2; //Lets not plan on having huge (> INT_MAX / 2) cut sequences
169 double mv = cuts[mid];
170 if(mv < t) {
171 if(t < cuts[mid + 1]) return mid; else low = mid + 1;
172 } else if(t < mv) {
173 if(cuts[mid - 1] < t) return mid - 1; else high = mid - 1;
174 } else {
175 return mid;
176 }
177 }
178 return low;
179 }
180
185 inline double segT(double t, int i = -1) const {
186 if(i == -1) i = segN(t);
187 assert(i >= 0);
188 return (t - cuts[i]) / (cuts[i+1] - cuts[i]);
189 }
190
191 inline double mapToDomain(double t, unsigned i) const {
192 return (1-t)*cuts[i] + t*cuts[i+1]; //same as: t * (cuts[i+1] - cuts[i]) + cuts[i]
193 }
194
195 //Offsets the piecewise domain
196 inline void offsetDomain(double o) {
197 assert(std::isfinite(o));
198 if(o != 0)
199 for(unsigned i = 0; i <= size(); i++)
200 cuts[i] += o;
201 }
202
203 //Scales the domain of the function by a value. 0 will result in an empty Piecewise.
204 inline void scaleDomain(double s) {
205 assert(s > 0);
206 if(s == 0) {
207 cuts.clear(); segs.clear();
208 return;
209 }
210 for(unsigned i = 0; i <= size(); i++)
211 cuts[i] *= s;
212 }
213
214 //Retrieves the domain in interval form
215 inline Interval domain() const { return Interval(cuts.front(), cuts.back()); }
216
217 //Transforms the domain into another interval
218 inline void setDomain(Interval dom) {
219 if(empty()) return;
220 /* dom can not be empty
221 if(dom.empty()) {
222 cuts.clear(); segs.clear();
223 return;
224 }*/
225 double cf = cuts.front();
226 double o = dom.min() - cf, s = dom.extent() / (cuts.back() - cf);
227 for(unsigned i = 0; i <= size(); i++)
228 cuts[i] = (cuts[i] - cf) * s + o;
229 //fix floating point precision errors.
230 cuts[0] = dom.min();
231 cuts[size()] = dom.max();
232 }
233
234 //Concatenates this Piecewise function with another, offsetting time of the other to match the end.
235 inline void concat(const Piecewise<T> &other) {
236 if(other.empty()) return;
237
238 if(empty()) {
239 cuts = other.cuts; segs = other.segs;
240 return;
241 }
242
243 segs.insert(segs.end(), other.segs.begin(), other.segs.end());
244 double t = cuts.back() - other.cuts.front();
245 cuts.reserve(cuts.size() + other.size());
246 for(unsigned i = 0; i < other.size(); i++)
247 push_cut(other.cuts[i + 1] + t);
248 }
249
250 //Like concat, but ensures continuity.
251 inline void continuousConcat(const Piecewise<T> &other) {
252 boost::function_requires<AddableConcept<typename T::output_type> >();
253 if(other.empty()) return;
254
255 if(empty()) { segs = other.segs; cuts = other.cuts; return; }
256
257 typename T::output_type y = segs.back().at1() - other.segs.front().at0();
258 double t = cuts.back() - other.cuts.front();
259 reserve(size() + other.size());
260 for(unsigned i = 0; i < other.size(); i++)
261 push(other[i] + y, other.cuts[i + 1] + t);
262 }
263
264 //returns true if the Piecewise<T> meets some basic invariants.
265 inline bool invariants() const {
266 // segs between cuts
267 if(!(segs.size() + 1 == cuts.size() || (segs.empty() && cuts.empty())))
268 return false;
269 // cuts in order
270 for(unsigned i = 0; i < segs.size(); i++)
271 if(cuts[i] >= cuts[i+1])
272 return false;
273 return true;
274 }
275
276};
277
283template<typename T>
285 boost::function_requires<FragmentConcept<T> >();
286
287 if(f.empty()) return typename FragmentConcept<T>::BoundsType();
289 for(unsigned i = 1; i < f.size(); i++)
290 ret.unionWith(bounds_fast(f[i]));
291 return ret;
292}
293
299template<typename T>
301 boost::function_requires<FragmentConcept<T> >();
302
303 if(f.empty()) return typename FragmentConcept<T>::BoundsType();
305 for(unsigned i = 1; i < f.size(); i++)
306 ret.unionWith(bounds_exact(f[i]));
307 return ret;
308}
309
315template<typename T>
317 boost::function_requires<FragmentConcept<T> >();
318
319 if(f.empty() || !_m) return typename FragmentConcept<T>::BoundsType();
320 Interval const &m = *_m;
321 if(m.isSingular()) return typename FragmentConcept<T>::BoundsType(f(m.min()));
322
323 unsigned fi = f.segN(m.min()), ti = f.segN(m.max());
324 double ft = f.segT(m.min(), fi), tt = f.segT(m.max(), ti);
325
326 if(fi == ti) return bounds_local(f[fi], Interval(ft, tt));
327
328 typename FragmentConcept<T>::BoundsType ret(bounds_local(f[fi], Interval(ft, 1.)));
329 for(unsigned i = fi + 1; i < ti; i++)
330 ret.unionWith(bounds_exact(f[i]));
331 if(tt != 0.) ret.unionWith(bounds_local(f[ti], Interval(0., tt)));
332
333 return ret;
334}
335
340template<typename T>
341T elem_portion(const Piecewise<T> &a, unsigned i, double from, double to) {
342 assert(i < a.size());
343 double rwidth = 1 / (a.cuts[i+1] - a.cuts[i]);
344 return portion( a[i], (from - a.cuts[i]) * rwidth, (to - a.cuts[i]) * rwidth );
345}
346
358template<typename T>
359Piecewise<T> partition(const Piecewise<T> &pw, std::vector<double> const &c) {
360 assert(pw.invariants());
361 if(c.empty()) return Piecewise<T>(pw);
362
364 ret.reserve(c.size() + pw.cuts.size() - 1);
365
366 if(pw.empty()) {
367 ret.cuts = c;
368 for(unsigned i = 0; i < c.size() - 1; i++)
369 ret.push_seg(T());
370 return ret;
371 }
372
373 unsigned si = 0, ci = 0; //Segment index, Cut index
374
375 //if the cuts have something earlier than the Piecewise<T>, add portions of the first segment
376 while(ci < c.size() && c[ci] < pw.cuts.front()) {
377 bool isLast = (ci == c.size()-1 || c[ci + 1] >= pw.cuts.front());
378 ret.push_cut(c[ci]);
379 ret.push_seg( elem_portion(pw, 0, c[ci], isLast ? pw.cuts.front() : c[ci + 1]) );
380 ci++;
381 }
382
383 ret.push_cut(pw.cuts[0]);
384 double prev = pw.cuts[0]; //previous cut
385 //Loop which handles cuts within the Piecewise<T> domain
386 //Should have the cuts = segs + 1 invariant
387 while(si < pw.size() && ci <= c.size()) {
388 if(ci == c.size() && prev <= pw.cuts[si]) { //cuts exhausted, straight copy the rest
389 ret.segs.insert(ret.segs.end(), pw.segs.begin() + si, pw.segs.end());
390 ret.cuts.insert(ret.cuts.end(), pw.cuts.begin() + si + 1, pw.cuts.end());
391 return ret;
392 }else if(ci == c.size() || c[ci] >= pw.cuts[si + 1]) { //no more cuts within this segment, finalize
393 if(prev > pw.cuts[si]) { //segment already has cuts, so portion is required
394 ret.push_seg(portion(pw[si], pw.segT(prev, si), 1.0));
395 } else { //plain copy is fine
396 ret.push_seg(pw[si]);
397 }
398 ret.push_cut(pw.cuts[si + 1]);
399 prev = pw.cuts[si + 1];
400 si++;
401 } else if(c[ci] == pw.cuts[si]){ //coincident
402 //Already finalized the seg with the code immediately above
403 ci++;
404 } else { //plain old subdivision
405 ret.push(elem_portion(pw, si, prev, c[ci]), c[ci]);
406 prev = c[ci];
407 ci++;
408 }
409 }
410
411 //input cuts extend further than this Piecewise<T>, extend the last segment.
412 while(ci < c.size()) {
413 if(c[ci] > prev) {
414 ret.push(elem_portion(pw, pw.size() - 1, prev, c[ci]), c[ci]);
415 prev = c[ci];
416 }
417 ci++;
418 }
419 return ret;
420}
421
426template<typename T>
427Piecewise<T> portion(const Piecewise<T> &pw, double from, double to) {
428 if(pw.empty() || from == to) return Piecewise<T>();
429
430 Piecewise<T> ret;
431
432 double temp = from;
433 from = std::min(from, to);
434 to = std::max(temp, to);
435
436 unsigned i = pw.segN(from);
437 ret.push_cut(from);
438 if(i == pw.size() - 1 || to <= pw.cuts[i + 1]) { //to/from inhabit the same segment
439 ret.push(elem_portion(pw, i, from, to), to);
440 return ret;
441 }
442 ret.push_seg(portion( pw[i], pw.segT(from, i), 1.0 ));
443 i++;
444 unsigned fi = pw.segN(to, i);
445 ret.reserve(fi - i + 1);
446 if (to == pw.cuts[fi]) fi-=1;
447
448 ret.segs.insert(ret.segs.end(), pw.segs.begin() + i, pw.segs.begin() + fi); //copy segs
449 ret.cuts.insert(ret.cuts.end(), pw.cuts.begin() + i, pw.cuts.begin() + fi + 1); //and their cuts
450
451 ret.push_seg( portion(pw[fi], 0.0, pw.segT(to, fi)));
452 if(to != ret.cuts.back()) ret.push_cut(to);
453 ret.invariants();
454 return ret;
455}
456
457//TODO: seems like these should be mutating
463template<typename T>
465 if(f.empty()) return f;
466 Piecewise<T> ret;
467 ret.reserve(f.size());
468 ret.push_cut(f.cuts[0]);
469 for(unsigned i=0; i<f.size(); i++){
470 if (f.cuts[i+1]-f.cuts[i] >= tol || i==f.size()-1) {
471 ret.push(f[i], f.cuts[i+1]);
472 }
473 }
474 return ret;
475}
476
477//TODO: seems like these should be mutating
483template<typename T>
485 if(f.empty()) return f;
486 Piecewise<T> ret;
487 ret.reserve(f.size());
488 ret.push_cut(f.cuts[0]);
489 double last = f.cuts[0]; // last cut included
490 for(unsigned i=0; i<f.size(); i++){
491 if (f.cuts[i+1]-f.cuts[i] >= tol) {
492 ret.push(elem_portion(f, i, last, f.cuts[i+1]), f.cuts[i+1]);
493 last = f.cuts[i+1];
494 }
495 }
496 return ret;
497}
498
504template<typename T>
505std::vector<double> roots(const Piecewise<T> &pw) {
506 std::vector<double> ret;
507 for(unsigned i = 0; i < pw.size(); i++) {
508 std::vector<double> sr = roots(pw[i]);
509 for (double & j : sr) ret.push_back(j * (pw.cuts[i + 1] - pw.cuts[i]) + pw.cuts[i]);
510
511 }
512 return ret;
513}
514
515//IMPL: OffsetableConcept
521template<typename T>
522Piecewise<T> operator+(Piecewise<T> const &a, typename T::output_type b) {
523 boost::function_requires<OffsetableConcept<T> >();
524//TODO:empty
525 Piecewise<T> ret;
526 ret.segs.reserve(a.size());
527 ret.cuts = a.cuts;
528 for(unsigned i = 0; i < a.size();i++)
529 ret.push_seg(a[i] + b);
530 return ret;
531}
532template<typename T>
533Piecewise<T> operator-(Piecewise<T> const &a, typename T::output_type b) {
534 boost::function_requires<OffsetableConcept<T> >();
535//TODO: empty
536 Piecewise<T> ret;
537 ret.segs.reserve(a.size());
538 ret.cuts = a.cuts;
539 for(unsigned i = 0; i < a.size();i++)
540 ret.push_seg(a[i] - b);
541 return ret;
542}
543template<typename T>
544Piecewise<T>& operator+=(Piecewise<T>& a, typename T::output_type b) {
545 boost::function_requires<OffsetableConcept<T> >();
546
547 if(a.empty()) { a.push_cut(0.); a.push(T(b), 1.); return a; }
548
549 for(unsigned i = 0; i < a.size();i++)
550 a[i] += b;
551 return a;
552}
553template<typename T>
554Piecewise<T>& operator-=(Piecewise<T>& a, typename T::output_type b) {
555 boost::function_requires<OffsetableConcept<T> >();
556
557 if(a.empty()) { a.push_cut(0.); a.push(T(-b), 1.); return a; }
558
559 for(unsigned i = 0;i < a.size();i++)
560 a[i] -= b;
561 return a;
562}
563
564//IMPL: ScalableConcept
570template<typename T>
572 boost::function_requires<ScalableConcept<T> >();
573
574 Piecewise<T> ret;
575 ret.segs.reserve(a.size());
576 ret.cuts = a.cuts;
577 for(unsigned i = 0; i < a.size();i++)
578 ret.push_seg(- a[i]);
579 return ret;
580}
586template<typename T>
588 boost::function_requires<ScalableConcept<T> >();
589
590 if(a.empty()) return Piecewise<T>();
591
592 Piecewise<T> ret;
593 ret.segs.reserve(a.size());
594 ret.cuts = a.cuts;
595 for(unsigned i = 0; i < a.size();i++)
596 ret.push_seg(a[i] * b);
597 return ret;
598}
604template<typename T>
606 boost::function_requires<ScalableConcept<T> >();
607
608 if(a.empty()) return Piecewise<T>();
609
610 Piecewise<T> ret;
611 ret.segs.reserve(a.size());
612 ret.cuts = a.cuts;
613 for(unsigned i = 0; i < a.size();i++)
614 ret.push_seg(a[i] * b);
615 return ret;
616}
622template<typename T>
624 boost::function_requires<ScalableConcept<T> >();
625
626 //FIXME: b == 0?
627 if(a.empty()) return Piecewise<T>();
628
629 Piecewise<T> ret;
630 ret.segs.reserve(a.size());
631 ret.cuts = a.cuts;
632 for(unsigned i = 0; i < a.size();i++)
633 ret.push_seg(a[i] / b);
634 return ret;
635}
636template<typename T>
638 boost::function_requires<ScalableConcept<T> >();
639
640 for(unsigned i = 0; i < a.size();i++)
641 a[i] *= b;
642 return a;
643}
644template<typename T>
646 boost::function_requires<ScalableConcept<T> >();
647
648 //FIXME: b == 0?
649
650 for(unsigned i = 0; i < a.size();i++)
651 a[i] /= b;
652 return a;
653}
654
655//IMPL: AddableConcept
661template<typename T>
663 boost::function_requires<AddableConcept<T> >();
664
665 Piecewise<T> pa = partition(a, b.cuts), pb = partition(b, a.cuts);
666 Piecewise<T> ret;
667 assert(pa.size() == pb.size());
668 ret.segs.reserve(pa.size());
669 ret.cuts = pa.cuts;
670 for (unsigned i = 0; i < pa.size(); i++)
671 ret.push_seg(pa[i] + pb[i]);
672 return ret;
673}
679template<typename T>
681 boost::function_requires<AddableConcept<T> >();
682
683 Piecewise<T> pa = partition(a, b.cuts), pb = partition(b, a.cuts);
685 assert(pa.size() == pb.size());
686 ret.segs.reserve(pa.size());
687 ret.cuts = pa.cuts;
688 for (unsigned i = 0; i < pa.size(); i++)
689 ret.push_seg(pa[i] - pb[i]);
690 return ret;
691}
692template<typename T>
694 a = a+b;
695 return a;
696}
697template<typename T>
699 a = a-b;
700 return a;
701}
702
708template<typename T1,typename T2>
710 //function_requires<MultiplicableConcept<T1> >();
711 //function_requires<MultiplicableConcept<T2> >();
712
713 Piecewise<T1> pa = partition(a, b.cuts);
714 Piecewise<T2> pb = partition(b, a.cuts);
716 assert(pa.size() == pb.size());
717 ret.segs.reserve(pa.size());
718 ret.cuts = pa.cuts;
719 for (unsigned i = 0; i < pa.size(); i++)
720 ret.push_seg(pa[i] * pb[i]);
721 return ret;
722}
723
729template<typename T>
731 a = a * b;
732 return a;
733}
734
735Piecewise<SBasis> divide(Piecewise<SBasis> const &a, Piecewise<SBasis> const &b, unsigned k);
736//TODO: replace divide(a,b,k) by divide(a,b,tol,k)?
737//TODO: atm, relative error is ~(tol/a)%. Find a way to make it independent of a.
738//Nota: the result is 'truncated' where b is smaller than 'zero': ~ a/max(b,zero).
740divide(Piecewise<SBasis> const &a, Piecewise<SBasis> const &b, double tol, unsigned k, double zero=1.e-3);
742divide(SBasis const &a, Piecewise<SBasis> const &b, double tol, unsigned k, double zero=1.e-3);
744divide(Piecewise<SBasis> const &a, SBasis const &b, double tol, unsigned k, double zero=1.e-3);
746divide(SBasis const &a, SBasis const &b, double tol, unsigned k, double zero=1.e-3);
747
748//Composition: functions called compose_* are pieces of compose that are factored out in pw.cpp.
749std::map<double,unsigned> compose_pullback(std::vector<double> const &cuts, SBasis const &g);
750int compose_findSegIdx(std::map<double,unsigned>::iterator const &cut,
751 std::map<double,unsigned>::iterator const &next,
752 std::vector<double> const &levels,
753 SBasis const &g);
754
760template<typename T>
764 if (f.empty()) return result;
765 if (g.isZero()) return Piecewise<T>(f(0));
766 if (f.size()==1){
767 double t0 = f.cuts[0], width = f.cuts[1] - t0;
768 return (Piecewise<T>) compose(f.segs[0],compose(Linear(-t0 / width, (1-t0) / width), g));
769 }
770
771 //first check bounds...
772 Interval bs = *bounds_fast(g);
773 if (f.cuts.front() > bs.max() || bs.min() > f.cuts.back()){
774 int idx = (bs.max() < f.cuts[1]) ? 0 : f.cuts.size()-2;
775 double t0 = f.cuts[idx], width = f.cuts[idx+1] - t0;
776 return (Piecewise<T>) compose(f.segs[idx],compose(Linear(-t0 / width, (1-t0) / width), g));
777 }
778
779 std::vector<double> levels;//we can forget first and last cuts...
780 levels.insert(levels.begin(),f.cuts.begin()+1,f.cuts.end()-1);
781 //TODO: use a std::vector<pairs<double,unsigned> > instead of a map<double,unsigned>.
782 std::map<double,unsigned> cuts_pb = compose_pullback(levels,g);
783
784 //-- Compose each piece of g with the relevant seg of f.
785 result.cuts.push_back(0.);
786 std::map<double,unsigned>::iterator cut=cuts_pb.begin();
787 std::map<double,unsigned>::iterator next=cut; next++;
788 while(next!=cuts_pb.end()){
789 //assert(std::abs(int((*cut).second-(*next).second))<1);
790 //TODO: find a way to recover from this error? the root finder missed some root;
791 // the levels/variations of f might be too close/fast...
792 int idx = compose_findSegIdx(cut,next,levels,g);
793 double t0=(*cut).first;
794 double t1=(*next).first;
795
796 if (!are_near(t0,t1,EPSILON*EPSILON)) { // prevent adding cuts that are extremely close together and that may cause trouble with rounding e.g. when reversing the path
797 SBasis sub_g=compose(g, Linear(t0,t1));
798 sub_g=compose(Linear(-f.cuts[idx]/(f.cuts[idx+1]-f.cuts[idx]),
799 (1-f.cuts[idx])/(f.cuts[idx+1]-f.cuts[idx])),sub_g);
800 result.push(compose(f[idx],sub_g),t1);
801 }
802
803 cut++;
804 next++;
805 }
806 return(result);
807}
808
814template<typename T>
818 for(unsigned i = 0; i < g.segs.size(); i++){
819 Piecewise<T> fgi=compose(f, g.segs[i]);
820 fgi.setDomain(Interval(g.cuts[i], g.cuts[i+1]));
821 result.concat(fgi);
822 }
823 return result;
824}
825
826/*
827Piecewise<D2<SBasis> > compose(D2<SBasis2d> const &sb2d, Piecewise<D2<SBasis> > const &pwd2sb){
829 Piecewise<D2<SBasis> > result;
830 result.push_cut(0.);
831 for(unsigned i = 0; i < pwd2sb.size(); i++){
832 result.push(compose_each(sb2d,pwd2sb[i]),i+1);
833 }
834 return result;
835}*/
836
843Piecewise<SBasis> pw_compose_inverse(SBasis const &f, SBasis const &g, unsigned order, double zero);
844
845
846
847template <typename T>
849template <typename T>
851
857template<typename T>
860 result.segs.resize(a.segs.size());
861 result.cuts = a.cuts;
862 typename T::output_type c = a.segs[0].at0();
863 for(unsigned i = 0; i < a.segs.size(); i++){
864 result.segs[i] = integral(a.segs[i])*(a.cuts[i+1]-a.cuts[i]);
865 result.segs[i]+= c-result.segs[i].at0();
866 c = result.segs[i].at1();
867 }
868 return result;
869}
870
876template<typename T>
879 result.segs.resize(a.segs.size());
880 result.cuts = a.cuts;
881 for(unsigned i = 0; i < a.segs.size(); i++){
882 result.segs[i] = derivative(a.segs[i])/(a.cuts[i+1]-a.cuts[i]);
883 }
884 return result;
885}
886
887std::vector<double> roots(Piecewise<SBasis> const &f);
888
889std::vector<std::vector<double> >multi_roots(Piecewise<SBasis> const &f, std::vector<double> const &values);
890
891//TODO: implement level_sets directly for pwsb instead of sb (and derive it fo sb).
892//It should be faster than the reverse as the algorithm may jump over full cut intervals.
893std::vector<Interval> level_set(Piecewise<SBasis> const &f, Interval const &level, double tol=1e-5);
894std::vector<Interval> level_set(Piecewise<SBasis> const &f, double v, double vtol, double tol=1e-5);
895//std::vector<Interval> level_sets(Piecewise<SBasis> const &f, std::vector<Interval> const &levels, double tol=1e-5);
896//std::vector<Interval> level_sets(Piecewise<SBasis> const &f, std::vector<double> &v, double vtol, double tol=1e-5);
897
898
904template<typename T>
907 ret.reserve(f.size());
908 double start = f.cuts[0];
909 double end = f.cuts.back();
910 for (unsigned i = 0; i < f.cuts.size(); i++) {
911 double x = f.cuts[f.cuts.size() - 1 - i];
912 ret.push_cut(end - (x - start));
913 }
914 for (unsigned i = 0; i < f.segs.size(); i++)
915 ret.push_seg(reverse(f[f.segs.size() - i - 1]));
916 return ret;
917}
918
924template<typename T>
926 // Make sure both paths have the same number of segments and cuts at the same locations
927 b.setDomain(a.domain());
928 Piecewise<T> pA = partition(a, b.cuts);
929 Piecewise<T> pB = partition(b, a.cuts);
930
931 return (pA*(1-t) + pB*t);
932}
933
934}
935#endif //LIB2GEOM_SEEN_PIECEWISE_H
936/*
937 Local Variables:
938 mode:c++
939 c-file-style:"stroustrup"
940 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
941 indent-tabs-mode:nil
942 fill-column:99
943 End:
944*/
945// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Coord at0() const
Definition bezier.h:272
constexpr C extent() const
constexpr bool isSingular() const
constexpr C min() const
constexpr C max() const
Range of real numbers that is never empty.
Definition interval.h:59
Function that interpolates linearly between two values.
Definition linear.h:55
Range of real numbers that can be empty.
Definition interval.h:199
Function defined as discrete pieces.
Definition piecewise.h:71
Piecewise< T > operator+(Piecewise< T > const &a, typename T::output_type b)
...
Definition piecewise.h:522
Piecewise< T2 > operator*(Piecewise< T1 > const &a, Piecewise< T2 > const &b)
...
Definition piecewise.h:709
bool empty() const
Definition piecewise.h:132
unsigned segN(double t, int low=0, int high=-1) const
Returns the segment index which corresponds to a 'global' piecewise time.
Definition piecewise.h:163
Piecewise< T > operator*(Piecewise< T > const &a, double b)
...
Definition piecewise.h:587
Piecewise< T > portion(const Piecewise< T > &pw, double from, double to)
Returns a Piecewise<T> with a defined domain of [min(from, to), max(from, to)].
Definition piecewise.h:427
Interval domain() const
Definition piecewise.h:215
output_type valueAt(double t) const
Definition piecewise.h:102
Piecewise< T > remove_short_cuts(Piecewise< T > const &f, double tol)
...
Definition piecewise.h:464
Piecewise< T > operator-(Piecewise< T > const &a, Piecewise< T > const &b)
...
Definition piecewise.h:680
unsigned size() const
Definition piecewise.h:131
void offsetDomain(double o)
Definition piecewise.h:196
void push(const T &s, double to)
Convenience/implementation hiding function to add segment/cut pairs.
Definition piecewise.h:141
FragmentConcept< T >::BoundsType bounds_fast(const Piecewise< T > &f)
...
Definition piecewise.h:284
Piecewise< T > partition(const Piecewise< T > &pw, std::vector< double > const &c)
Piecewise<T> partition(const Piecewise<T> &pw, std::vector<double> const &c); Further subdivides the ...
Definition piecewise.h:359
Piecewise< T > integral(Piecewise< T > const &a)
...
Definition piecewise.h:858
Piecewise(const output_type &v)
Definition piecewise.h:91
Piecewise< T > operator*(Piecewise< T > const &a, T b)
...
Definition piecewise.h:605
void push_seg(T &&s)
Definition piecewise.h:158
Piecewise< T > operator()(SBasis f)
Definition piecewise.h:848
Piecewise< T > & operator*=(Piecewise< T > &a, Piecewise< T > const &b)
...
Definition piecewise.h:730
unsigned input_dim()
Definition piecewise.h:87
BOOST_CLASS_REQUIRE(T, Geom, FragmentConcept)
T elem_portion(const Piecewise< T > &a, unsigned i, double from, double to)
Returns a portion of a piece of a Piecewise<T>, given the piece's index and a to/from time.
Definition piecewise.h:341
void scaleDomain(double s)
Definition piecewise.h:204
Piecewise< T > compose(Piecewise< T > const &f, Piecewise< SBasis > const &g)
...
Definition piecewise.h:815
void push_seg(const T &s)
Definition piecewise.h:157
void push_cut(double c)
Definition piecewise.h:152
T & operator[](unsigned i)
Definition piecewise.h:100
void push(T &&s, double to)
Definition piecewise.h:146
void continuousConcat(const Piecewise< T > &other)
Definition piecewise.h:251
bool invariants() const
Definition piecewise.h:265
output_type lastValue() const
Definition piecewise.h:109
std::vector< double > cuts
Definition piecewise.h:75
Piecewise< T > remove_short_cuts_extending(Piecewise< T > const &f, double tol)
...
Definition piecewise.h:484
Piecewise< T > compose(Piecewise< T > const &f, SBasis const &g)
...
Definition piecewise.h:761
Piecewise< T > operator()(Piecewise< SBasis >f)
Definition piecewise.h:850
Piecewise< T > derivative(Piecewise< T > const &a)
...
Definition piecewise.h:877
std::vector< double > roots(const Piecewise< T > &pw)
...
Definition piecewise.h:505
Piecewise(const T &s)
Definition piecewise.h:81
std::vector< output_type > valueAndDerivatives(double t, unsigned n_derivs) const
The size of the returned vector equals n_derivs+1.
Definition piecewise.h:116
output_type firstValue() const
Definition piecewise.h:106
Piecewise< T > operator+(Piecewise< T > const &a, Piecewise< T > const &b)
...
Definition piecewise.h:662
double segT(double t, int i=-1) const
Returns the time within a segment, given the 'global' piecewise time.
Definition piecewise.h:185
void concat(const Piecewise< T > &other)
Definition piecewise.h:235
Piecewise< T > reverse(Piecewise< T > const &f)
...
Definition piecewise.h:905
T const & operator[](unsigned i) const
Definition piecewise.h:99
FragmentConcept< T >::BoundsType bounds_local(const Piecewise< T > &f, const OptInterval &_m)
...
Definition piecewise.h:316
std::vector< T > segs
Definition piecewise.h:76
FragmentConcept< T >::BoundsType bounds_exact(const Piecewise< T > &f)
...
Definition piecewise.h:300
Piecewise< T > operator/(Piecewise< T > const &a, double b)
...
Definition piecewise.h:623
void setDomain(Interval dom)
Definition piecewise.h:218
output_type operator()(double t) const
Definition piecewise.h:101
double mapToDomain(double t, unsigned i) const
Definition piecewise.h:191
Piecewise< T > lerp(double t, Piecewise< T > const &a, Piecewise< T > b)
Interpolates between a and b.
Definition piecewise.h:925
void reserve(unsigned i)
Definition piecewise.h:97
T::output_type output_type
Definition piecewise.h:89
Piecewise< T > operator-(Piecewise< T > const &a)
...
Definition piecewise.h:571
Polynomial in symmetric power basis.
Definition sbasis.h:70
bool isZero(double eps=EPSILON) const
Definition sbasis.h:195
Template concepts used by 2Geom.
Css & result
double c[8][4]
const unsigned order
constexpr Coord EPSILON
Default "acceptably small" value.
Definition coord.h:84
Low level math functions and compatibility wrappers.
Geom::Point start
Geom::Point end
Various utility functions.
Definition affine.h:22
OptInterval bounds_exact(Bezier const &b)
Definition bezier.cpp:310
Bezier reverse(const Bezier &a)
Definition bezier.h:342
SBasisN< n > divide(SBasisN< n > const &a, SBasisN< n > const &b, int k)
OptInterval bounds_local(Bezier const &b, OptInterval const &i)
Definition bezier.cpp:320
int compose_findSegIdx(std::map< double, unsigned >::iterator const &cut, std::map< double, unsigned >::iterator const &next, std::vector< double > const &levels, SBasis const &g)
D2< T > operator+=(D2< T > &a, D2< T > const &b)
Definition d2.h:219
D2< T > operator-(D2< T > const &a, D2< T > const &b)
Definition d2.h:209
D2< T > operator*=(D2< T > &a, Point const &b)
Definition d2.h:268
D2< T > operator-=(D2< T > &a, D2< T > const &b)
Definition d2.h:228
D2< T > compose(D2< T > const &a, T const &b)
Definition d2.h:405
std::vector< double > roots(SBasis const &s)
Bezier portion(const Bezier &a, double from, double to)
Definition bezier.cpp:250
Bezier integral(Bezier const &a)
Definition bezier.cpp:294
Bezier derivative(Bezier const &a)
Definition bezier.cpp:282
Piecewise< SBasis > pw_compose_inverse(SBasis const &f, SBasis const &g, unsigned order, double zero)
std::vector< std::vector< double > > multi_roots(SBasis const &f, std::vector< double > const &levels, double htol=1e-7, double vtol=1e-7, double a=0, double b=1)
std::map< double, unsigned > compose_pullback(std::vector< double > const &cuts, SBasis const &g)
OptInterval bounds_fast(Bezier const &b)
Definition bezier.cpp:305
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
std::vector< Interval > level_set(D2< SBasis > const &f, Rect region)
D2< T > operator/=(D2< T > &a, Point const &b)
Definition d2.h:277
unsigned long bs
Definition quantize.cpp:38
Polynomial in symmetric power basis (S-basis)
ResultTraits< OutputType >::bounds_type BoundsType
Definition concepts.h:63
double width