64 , _path_size(path_size)
65 , _cross_start(cross_start)
66 , _reverse((to < from) ^ cross_start)
70 if (cross_start &&
_to <
to) {
90 if (cross_start &&
_to >
to) {
151 bool from_close =
_from.
t < min_dist;
152 bool to_close =
_to.
t > 1 - min_dist;
159 if (from_close || to_close) {
185 bool from_close =
_from.
t > 1 - min_dist;
186 bool to_close =
_to.
t < min_dist;
193 if (from_close || to_close) {
228 result._path_size = path_size;
233 result._from.normalizeBackward(path_size);
236 result._from.normalizeForward(path_size);
238 result._to.normalizeBackward(path_size);
244 result._cross_start =
false;
246 result._reverse = reversed;
261 , _exception_on_stitch(true)
263 for (
unsigned i = 0; i < 3; ++i) {
273 , _exception_on_stitch(true)
285 for (std::size_t i = 1; i < ch.
size(); ++i) {
298 , _exception_on_stitch(true)
301 Point p2 =
c.pointAt(M_PI);
312 , _exception_on_stitch(true)
325 if (
c &&
_data->curves.size() >= 2) {
328 Sequence::iterator last =
_data->curves.end() - 2;
329 if (last->isLineSegment() && last->finalPoint() ==
initialPoint()) {
331 _data->curves.erase(last);
340 _data->curves.pop_back().release();
341 _data->curves.clear();
355 if (
_data->fast_bounds) {
356 return _data->fast_bounds;
364 for (++iter; iter !=
end(); ++iter) {
381 for (++iter; iter !=
end(); ++iter) {
393 bool degenerate =
true;
396 if (!it->isDegenerate()) {
397 ret.
push(it->toSBasis(), i++);
409template <
typename iter>
410iter
inc(iter
const &x,
unsigned n) {
412 for (
unsigned i = 0; i < n; i++)
427 if (
_data->curves.size() > 1) {
474 std::vector<PathTime> res;
475 for (
unsigned i = 0; i <
size(); i++) {
476 std::vector<Coord> temp = (*this)[i].roots(v, d);
477 for (
double j : temp)
478 res.emplace_back(i, j);
488struct CurveIntersectionSweepSet
492 boost::intrusive::list_member_hook<> _hook;
498 CurveRecord(
Curve const *pc, std::size_t idx,
unsigned w)
506 typedef std::vector<CurveRecord>::const_iterator ItemIterator;
508 CurveIntersectionSweepSet(std::vector<PathIntersection> &
result,
514 std::size_t asz = a.size(), bsz = b.size();
515 _records.reserve(asz + bsz);
517 for (std::size_t i = 0; i < asz; ++i) {
518 _records.emplace_back(&a[i], i, 0);
520 for (std::size_t i = 0; i < bsz; ++i) {
521 _records.emplace_back(&b[i], i, 1);
524 OptRect abb = a.boundsFast() | b.boundsFast();
525 if (abb && abb->height() > abb->width()) {
530 std::vector<CurveRecord>
const &
items() {
return _records; }
531 Interval itemBounds(ItemIterator ii) {
532 return ii->bounds[_sweep_dir];
535 void addActiveItem(ItemIterator ii) {
536 unsigned w = ii->which;
537 unsigned ow = (
w+1) % 2;
539 _active[
w].push_back(
const_cast<CurveRecord&
>(*ii));
542 if (!ii->bounds.intersects(i.bounds))
continue;
543 std::vector<CurveIntersection> cx = ii->curve->intersect(*i.curve, _precision);
544 for (
auto & k : cx) {
545 PathTime tw(ii->index, k.first), tow(i.index, k.second);
553 void removeActiveItem(ItemIterator ii) {
554 ActiveCurveList &acl =
_active[ii->which];
555 acl.erase(acl.iterator_to(*ii));
559 typedef boost::intrusive::list
561 , boost::intrusive::member_hook
563 , boost::intrusive::list_member_hook<>
564 , &CurveRecord::_hook
568 std::vector<CurveRecord> _records;
569 std::vector<PathIntersection> &
_result;
577 std::vector<PathIntersection>
result;
579 CurveIntersectionSweepSet cisset(
result, *
this, other, precision);
584 std::size_t asz =
size(), bsz = other.
size();
586 i.first.normalizeForward(asz);
587 i.second.normalizeForward(bsz);
614 Point ip = i->initialPoint();
615 Point fp = i->finalPoint();
618 if (eqbox[
Y].lowerContains(p[
Y])) {
623 }
else if (ip[
Y] > fp[
Y]) {
632 wind += i->winding(p);
646 const Path &_path = *
this;
647 unsigned int sz = _path.
size();
650 if (from < 0 || to > sz) {
651 THROW_RANGEERROR(
"[from,to] interval out of bounds");
653 double sif, st = modf(from, &sif);
654 double eif, et = modf(to, &eif);
655 unsigned int si =
static_cast<unsigned int>(sif);
656 unsigned int ei =
static_cast<unsigned int>(eif);
666 std::vector<double> all_nearest = _path[si].
allNearestTimes(_point, st, et);
667 for (
double & i : all_nearest) {
672 std::vector<double> all_t;
673 std::vector<std::vector<double> > all_np;
675 std::vector<unsigned int> ni;
678 double mindistsq =
distanceSq(_point, _path[si].
pointAt(all_np.front().front()));
680 for (
unsigned int i = si + 1; i < ei; ++i) {
687 if (mindistsq > dsq) {
689 all_np.push_back(all_t);
693 }
else if (mindistsq == dsq) {
694 all_np.push_back(all_t);
700 if (mindistsq >= dsq) {
703 if (mindistsq > dsq) {
704 for (
double & i : all_t) {
708 }
else if (mindistsq == dsq) {
709 all_np.push_back(all_t);
713 std::vector<double> all_nearest;
714 for (
unsigned int i = 0; i < all_np.size(); ++i) {
715 for (
unsigned int j = 0; j < all_np[i].size(); ++j) {
716 all_nearest.push_back(ni[i] + all_np[i][j]);
719 all_nearest.erase(std::unique(all_nearest.begin(), all_nearest.end()), all_nearest.end());
726 std::vector<Coord> np;
728 np.push_back(it->nearestTime(p));
735 Coord mindist = std::numeric_limits<Coord>::max();
738 if (
_data->curves.size() == 1) {
750 if (
distance(p,
c.boundsFast()) >= mindist)
continue;
752 Coord t =
c.nearestTime(p);
769 std::vector<Point>
result;
771 for (
size_type i = 0; i < path_size; ++i) {
779 if (!(from >= 0 && to >= 0)) {
780 THROW_RANGEERROR(
"from and to must be >=0 in Path::appendPortionTo");
783 to =
size() + 0.999999;
788 double ff = modf(from, &fi), tf = modf(to, &ti);
794 if (fi == ti && from < to) {
795 ret.
append(fromi->portion(ff, tf));
801 ret.
append(fromi->portion(ff, 1.));
805 if (ender->initialPoint() == ender->finalPoint())
812 ret.
append(toi->portion(0., tf));
816 std::optional<Point>
const &p_from, std::optional<Point>
const &p_to)
const
849 i = (i + s + di) % s)
852 target.
append((*
this)[i].reverse());
854 target.
append((*
this)[i].duplicate());
868 typedef std::reverse_iterator<Sequence::const_iterator> RIter;
871 if (
empty())
return ret;
873 ret.
_data->curves.pop_back();
876 RIter rend(
_data->curves.begin());
880 if (
front().isLineSegment()) {
882 rend = RIter(
_data->curves.begin() + 1);
895 for (; iter != rend; ++iter) {
896 ret.
_data->curves.push_back(iter->reverse());
907 Sequence::iterator seq_pos(
seq_iter(pos));
909 source.push_back(
curve.duplicate());
916 Sequence::iterator seq_pos(
seq_iter(pos));
919 do_update(seq_pos, seq_pos + 1, stitched);
925 Sequence::iterator seq_first =
seq_iter(first);
926 Sequence::iterator seq_last =
seq_iter(last);
929 do_update(seq_first, seq_last, stitched);
936 THROW_CONTINUITYERROR();
951 Sequence::iterator seq_first_replaced(
seq_iter(first_replaced));
952 Sequence::iterator seq_last_replaced(
seq_iter(last_replaced));
954 source.push_back(
curve.duplicate());
956 do_update(seq_first_replaced, seq_last_replaced, source);
982 cleaned.reserve(
size());
985 if (!it->isDegenerate()) {
986 cleaned.push_back(it->duplicate());
1000 bool last_beyond_closing_segment = (last ==
_data->curves.end());
1004 if (source.empty()) {
1005 if (first == last)
return;
1008 if ((!
_closed && first ==
_data->curves.begin()) || (!
_closed && last ==
_data->curves.end() - 1) || last_beyond_closing_segment) {
1011 }
else if (first->initialPoint() != (last - 1)->
finalPoint()) {
1013 THROW_CONTINUITYERROR();
1015 source.push_back(
new StitchSegment(first->initialPoint(), (last - 1)->finalPoint()));
1019 if (first ==
_data->curves.begin() && last ==
_data->curves.end()) {
1024 _data->curves.transfer(
_data->curves.begin(), source.begin(), source.end(), source);
1031 }
else if (first->initialPoint() != source.front().initialPoint()) {
1033 THROW_CONTINUITYERROR();
1035 source.insert(source.begin(),
new StitchSegment(first->initialPoint(), source.front().initialPoint()));
1039 if ((!
_closed && last ==
_data->curves.end() - 1) || last_beyond_closing_segment) {
1042 }
else if (source.back().finalPoint() != (last - 1)->finalPoint()) {
1044 THROW_CONTINUITYERROR();
1046 source.push_back(
new StitchSegment(source.back().finalPoint(), (last - 1)->finalPoint()));
1051 if (last_beyond_closing_segment) {
1054 _data->curves.erase(first, last);
1055 _data->curves.transfer(first, source.begin(), source.end(), source);
1076 THROW_CONTINUITYERROR();
1078 if (
_closed &&
c->isLineSegment() &&
1092 Sequence::const_iterator i =
_data->curves.begin(), j =
_data->curves.begin();
1094 for (; j !=
_data->curves.end(); ++i, ++j) {
1095 if (i->finalPoint() != j->initialPoint()) {
1096 THROW_CONTINUITYERROR();
1099 if (
_data->curves.front().initialPoint() !=
_data->curves.back().finalPoint()) {
1100 THROW_CONTINUITYERROR();
1108 if (t < 0 || t > sz) {
1109 THROW_RANGEERROR(
"parameter t out of bounds");
1114 ret.
t = modf(t, &k);
1126 for (
unsigned i = 1; i <
paths.size(); i++) {
1134 if (a.
size() != b.
size())
return false;
1136 for (
unsigned i = 0; i < a.
size(); ++i) {
1137 if (!a[i].isNear(b[i], precision))
return false;
Path - a sequence of contiguous curves.
void setInitial(Point const &v) override
Change the starting point of the curve.
Coord length(Coord tolerance) const override
Compute the arc length of this curve.
Point finalPoint() const override
Retrieve the end of the curve.
Point initialPoint() const override
Retrieve the start of the curve.
void setFinal(Point const &v) override
Change the ending point of the curve.
Set of all points at a fixed distance from the center.
Convex hull based on the Andrew's monotone chain algorithm.
bool empty() const
Check for emptiness.
Point const & back() const
Get the penultimate point of the lower hull.
size_t size() const
Get the number of points in the hull.
Point const & front() const
Get the first, leftmost point in the hull.
Abstract continuous curve on a plane defined on [0,1].
virtual Rect boundsFast() const =0
Quickly compute the curve's approximate bounding box.
virtual Rect boundsExact() const =0
Compute the curve's exact bounding box.
virtual Coord valueAt(Coord t, Dim2 d) const
Evaluate one of the coordinates at the specified time value.
virtual void setInitial(Point const &v)=0
Change the starting point of the curve.
virtual Curve * portion(Coord a, Coord b) const =0
Create a curve that corresponds to a part of this curve.
virtual void setFinal(Point const &v)=0
Change the ending point of the curve.
virtual Point pointAt(Coord t) const
Evaluate the curve at a specified time value.
Set of points with a constant sum of distances from two foci.
Angle rotationAngle() const
Get the angle the X ray makes with the +X axis.
Point pointAt(Coord t) const
Evaluate a point on the ellipse.
Point rays() const
Get both rays as a point.
C right() const
Return rightmost coordinate of the rectangle (+X is to the right).
C left() const
Return leftmost coordinate of the rectangle (+X is to the right).
C height() const
Get the vertical extent of the rectangle.
void unionWith(CRect const &b)
Enlarge the rectangle to contain the argument.
CPoint corner(unsigned i) const
Return the n-th corner of the rectangle.
Range of real numbers that is never empty.
Axis-aligned rectangle that can be empty.
Contiguous subset of the path's parameter domain.
PathInternal::Sequence::size_type size_type
static PathInterval from_direction(PathTime const &from, PathTime const &to, bool reversed, size_type path_size)
Select one of two intervals with given endpoints by parameter direction.
bool reverse() const
True if the interval goes in the direction of decreasing time values.
PathTime inside(Coord min_dist=EPSILON) const
Get a time at least min_dist away in parameter space from the ends.
size_type curveCount() const
bool isDegenerate() const
Check whether the interval has only one point.
PathTime const & to() const
bool contains(PathTime const &pos) const
Test a path time for inclusion.
bool crossesStart() const
True if the interior of the interval contains the initial point of the path.
PathTime const & from() const
size_type pathSize() const
PathInterval()
Default interval.
virtual void feed(Curve const &c, bool moveto_initial=true)
Curve * reverse() const override
Create a reversed version of this curve.
Sequence of contiguous curves, aka spline.
bool closed() const
Check whether the path is closed.
Point finalPoint() const
Get the last point in the path.
bool operator==(Path const &other) const
Test paths for exact equality.
void close(bool closed=true)
Set whether the path is closed.
Interval timeRange() const
Get the allowed range of time values.
std::shared_ptr< PathData > _data
friend void swap(Path &a, Path &b) noexcept
Swap contents of two paths.
bool empty() const
Check whether path is empty.
OptRect boundsExact() const
Get a tight-fitting bounding box.
Piecewise< D2< SBasis > > toPwSb() const
bool _exception_on_stitch
Path(Point const &p=Point())
Construct an empty path starting at the specified point.
std::vector< PathTime > roots(Coord v, Dim2 d) const
Compute intersections with axis-aligned line.
const_iterator end() const
PathTime nearestTime(Point const &p, Coord *dist=NULL) const
Point pointAt(Coord t) const
Get the point at the specified time value.
OptRect boundsFast() const
Get the approximate bounding box.
size_type size_open() const
Size without the closing segment, even if the path is closed.
void clear()
Remove all curves from the path.
Path withoutDegenerateCurves() const
Return a copy of the path without degenerate curves, except possibly for a degenerate closing segment...
const_iterator end_open() const
size_type size_default() const
Natural size of the path.
ClosingSegment * _closing_seg
Curve const & back_open() const
std::vector< PathIntersection > intersect(Path const &other, Coord precision=EPSILON) const
Compute intersections with another path.
int winding(Point const &p) const
Determine the winding number at the specified point.
PathInternal::Sequence Sequence
void appendPortionTo(Path &p, Coord f, Coord t) const
Sequence::size_type size_type
bool _includesClosingSegment() const
void append(Curve *curve)
Add a new curve to the end of the path.
static Sequence::iterator seq_iter(iterator const &iter)
Point initialPoint() const
Get the first point in the path.
Curve const & front() const
Access the first curve in the path.
void do_append(Curve *curve)
Path reversed() const
Obtain a reversed version of the current path.
void setFinal(Point const &p)
void snapEnds(Coord precision=EPSILON)
Reduce the closing segment to a point if it's shorter than precision.
size_type size_closed() const
Size with the closing segment, if it makes a difference.
size_type size() const
Natural size of the path.
Coord valueAt(Coord t, Dim2 d) const
Get one coordinate (X or Y) at the specified time value.
const_iterator begin() const
void replace(iterator replaced, Curve const &curve)
Curve const & at(size_type i) const
Access a curve by index.
void checkContinuity() const
Verify the continuity invariant.
void do_update(Sequence::iterator first, Sequence::iterator last, Sequence &source)
std::vector< Coord > allNearestTimes(Point const &p, Coord from, Coord to) const
PathTime _factorTime(Coord t) const
Curve const & curveAt(Coord t, Coord *rest=NULL) const
Get the curve at the specified time value.
const_iterator end_closed() const
std::vector< Coord > nearestTimePerCurve(Point const &p) const
std::vector< Point > nodes() const
const_iterator end_default() const
void stitchTo(Point const &p)
Append a stitching segment ending at the specified point.
void insert(iterator pos, Curve const &curve)
void start(Point const &p)
Function defined as discrete pieces.
void push(const T &s, double to)
Convenience/implementation hiding function to add segment/cut pairs.
void concat(const Piecewise< T > &other)
Two-dimensional point that doubles as a vector.
Axis aligned, non-empty rectangle.
Serialize paths to SVG path data strings.
std::string str() const
Retrieve the generated path data string.
Generic sweepline algorithm.
void process()
Process entry and exit events.
Path and its polyline approximation.
Convex hull data structures.
std::vector< ItemIterator > _active
std::vector< Geom::PathVector > _result
BezierCurveN< 1 > LineSegment
Line segment.
constexpr Coord lerp(Coord t, Coord a, Coord b)
Numerically stable linear interpolation.
Dim2
2D axis enumeration (X or Y).
double Coord
Floating point type used to store coordinates.
vector< vector< Point > > paths
Various utility functions.
Bezier reverse(const Bezier &a)
Piecewise< D2< SBasis > > paths_to_pw(PathVector const &paths)
std::ostream & operator<<(std::ostream &os, const Bezier &b)
Coord distanceSq(Point const &p, Rect const &rect)
Angle distance(Angle const &a, Angle const &b)
iter inc(iter const &x, unsigned n)
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
PathVector - a sequence of subpaths.
Generalized time value in the path.
size_type curve_index
Index of the curve in the path.
Coord t
Time value in the curve.
void normalizeBackward(size_type path_size)
Convert times at or before 0 to 1 on the previous curve.
void normalizeForward(size_type path_size)
Convert times at or beyond 1 to 0 on the next curve.
Path sink which writes an SVG-compatible command string.
Class for implementing sweepline algorithms.