14#define NULL_IDX UINT_MAX
17#include "../2geom/orphan-code/intersection-by-smashing.cpp"
81 std::vector<SmashIntersection>
result;
89 i.times[
X] += a_dom.
min();
91 i.times[
Y] += b_dom.
min();
107 struct NearPredicate {
109 NearPredicate(
double eps):tol(eps){}
110 NearPredicate(){tol =
EPSILON;}
111 bool operator()(T x, T y) {
return are_near(x, y, tol); } };
116 void process_splits(std::vector<double> &splits,
double f,
double t,
double tol=
EPSILON) {
119 std::sort(splits.begin(), splits.end());
120 std::vector<double>::iterator
end = std::unique(splits.begin(), splits.end(), NearPredicate<double>(tol));
121 splits.resize(
end - splits.begin());
124 while(!splits.empty() && splits.front() < f+tol) splits.erase(splits.begin());
125 splits.insert(splits.begin(), f);
127 while(!splits.empty() && splits.back() > t-tol) splits.erase(splits.end() - 1);
132 struct IntersectionMinTimeOrder {
134 IntersectionMinTimeOrder (
unsigned idx) : which(idx) {}
143 std::vector<std::pair<Interval, Rect> >
144 process_intersections(std::vector<SmashIntersection> &inters,
unsigned which,
unsigned tileidx) {
145 std::vector<std::pair<Interval, Rect> >
result;
146 std::pair<Interval, Rect> apair;
147 Interval dom ( tiles_data[tileidx].f, tiles_data[tileidx].t );
148 apair.first =
Interval( dom.min() );
149 apair.second = tiles_data[tileidx].fbox;
150 result.push_back( apair );
152 std::sort(inters.begin(), inters.end(), IntersectionMinTimeOrder(which) );
153 for (
auto & inter : inters){
154 if ( !inter.times[which].intersects( dom ) )
156 if (
result.back().first.intersects( inter.times[which] ) ){
157 result.back().first.unionWith( inter.times[which] );
158 result.back().second.unionWith( inter.bbox );
160 apair.first = inter.times[which];
161 apair.second = inter.bbox;
162 result.push_back( apair );
165 apair.first =
Interval( dom.max() );
166 apair.second = tiles_data[tileidx].tbox;
167 if (
result.size() > 1 &&
result.back().first.intersects( apair.first ) ){
168 result.back().first.unionWith( apair.first );
169 result.back().second.unionWith( apair.second );
171 result.push_back( apair );
198 Rect cur_box()
const {
return ((state>=0)^(reversed) ) ? tbox : fbox; }
199 Rect first_box()
const {
return ( reversed ) ? tbox : fbox; }
200 Rect last_box()
const {
return ( reversed ) ? fbox : tbox; }
205 assert( tile.path <
paths.size() );
206 assert( tile.curve <
paths[tile.path].size() );
216 SweepOrder(
Dim2 d) : dim(d) {}
217 bool operator()(
const Rect &a,
const Rect &b)
const {
218 return Point::LexLessRt(dim)(a.
min(), b.
min());
220 bool operator()(
const Tile &a,
const Tile &b)
const {
221 return Point::LexLessRt(dim)(a.cur_box().min(), b.cur_box().min());
228 std::vector<Tile>::iterator
const begin;
229 PtrSweepOrder(std::vector<Tile>::iterator
const beg,
Dim2 d) : dim(d), begin(beg){}
230 bool operator()(
const unsigned a,
const unsigned b)
const {
231 return Point::LexLessRt(dim)((begin+a)->cur_box().min(), (begin+b)->cur_box().min());
249 Ray(){tile = NULL_IDX; exit_side = 4;}
250 Ray(
unsigned tile_idx,
unsigned s,
double p,
double t){
256 Ray(
unsigned tile_idx,
bool outward){
259 centrifuge = outward;
260 exit_time = (centrifuge) ? 2 : -1 ;
262 void setExitInfo(
unsigned side,
double place,
double time){
269 class FatVertex :
public Rect{
271 std::vector<Ray> rays;
272 FatVertex(
const Rect &r,
unsigned const tile,
bool centrifuge) :
Rect(r){
273 rays.push_back(
Ray(tile, centrifuge) );
276 FatVertex() :
Rect(){}
277 void erase(
unsigned from,
unsigned to){
278 unsigned size = to-from;
279 from = from % rays.size();
282 if (to >= rays.size() ){
283 to = to % rays.size();
284 rays.erase( rays.begin()+from, rays.end() );
285 rays.erase( rays.begin(), rays.begin()+to );
287 rays.erase( rays.begin()+from, rays.begin()+to );
303 bool to_be_continued;
305 return tile==NULL_IDX;
312 to_be_continued =
false;
316 void printEvent(Event
const &e){
317 std::printf(
"Event: ");
318 std::printf(
"%s, ", e.opening?
"opening":
"closing");
319 std::printf(
"insert_at:%u, ", e.insert_at);
321 std::printf(
"tile:%u.\n", e.tile);
324 class Context :
public std::vector<std::pair<unsigned,bool> >{
327 FatVertex pending_vertex;
330 unsigned find(
unsigned const tile,
bool positive_only=
false){
331 for (
unsigned i=0; i<
size(); i++){
332 if ( (*
this)[i].first == tile ){
333 if ( (*
this)[i].second || !positive_only )
return i;
336 return (*this).size();
340 std::printf(
"context:[");
341 for (
unsigned i=0; i<context.size(); i++){
342 unsigned tile = context[i].first;
343 assert( tile<tiles_data.size() );
344 std::printf(
" %s%u%s", (tiles_data[ tile ].reversed)?
"-":
"+", tile, (context[i].second)?
"o":
"c");
346 assert( context[i].second || tiles_data[ tile ].state==1);
360 unsigned contextRanking(
Point const &pt){
364 unsigned rank = context.size();
365 std::vector<unsigned> unmatched_closed_tiles = std::vector<unsigned>();
369 for (
unsigned i=0; i<context.size(); i++){
371 unsigned tile_idx = context[i].first;
372 assert( tile_idx < tiles_data.size() );
375 Tile tile = tiles_data[tile_idx];
376 assert( tile.state >= 0 );
379 if ( tile.state == 0 ){
381 if (pt[1-dim] < tile.min()[1-dim] ) {
387 if (pt[1-dim] > tile.max()[1-dim] ){
395 std::vector<double> times =
roots(
c[dim]-pt[dim]);
396 if (times.size()==0){
397 assert( tile.first_box()[dim].contains(pt[dim]) );
398 if ( pt[1-dim] < tile.first_box()[1-dim].min() ){
406 if ( pt[1-dim] <
c[1-dim](times.front()) ){
419 if ( unmatched_closed_tiles.size()==0 || tile_idx != unmatched_closed_tiles.back() ){
420 unmatched_closed_tiles.push_back( tile_idx );
424 unmatched_closed_tiles.pop_back();
428 if ( !tile.bbox().contains( pt ) ){
440 std::vector<double> times =
roots(
c[1-dim]-pt[1-dim]);
441 if ( times.size()>0 ){
443 hit_place =
c[dim](times.front());
447 assert( tile.first_box()[1-dim].contains(pt[1-dim]) );
451 if ( pt[dim] > hit_place ){
462 assert( rank<=tiles_data.size() );
468 void purgeDeadTiles(){
471 for (
unsigned i=0; i<context.size(); i++){
472 assert( context[i].first<tiles_data.size() );
473 Tile tile = tiles_data[context[i].first];
474 if (tile.state==1 && Point::LexLessRt(dim)( tile.fbox.max(), context.last_pos ) && Point::LexLessRt(dim)( tile.tbox.max(), context.last_pos ) ){
477 for (j=i+1; j<context.size() && context[j].first != context[i].first; j++){}
478 assert ( j < context.size() );
479 if ( context[j].first == context[i].first){
480 assert ( context[j].second == !context[i].second );
481 context.erase(context.begin()+j);
482 context.erase(context.begin()+i);
491 void applyEvent(Event event){
502 assert ( context.begin() + event.insert_at <= context.end() );
508 tiles_data[
event.tile].open =
false;
509 tiles_data[
event.tile].state = 1;
511 unsigned idx =
event.insert_at;
512 context.insert(context.begin()+idx, std::pair<unsigned, bool>(event.tile,
false) );
514 unsigned idx =
event.insert_at;
515 tiles_data[
event.tile].open =
true;
516 tiles_data[
event.tile].state = 0;
517 context.insert(context.begin()+idx, std::pair<unsigned, bool>(event.tile,
true) );
520 context.last_pos = context.pending_vertex.min();
521 context.last_pos[1-dim] = context.pending_vertex.max()[1-dim];
536 std::vector<Tile> tiles_data;
537 std::vector<unsigned>
tiles;
538 std::vector<Rect> vtxboxes;
549 void createMonotonicTiles(){
550 for (
unsigned i=0; i<
paths.size(); i++){
551 for (
unsigned j=0; j<
paths[i].size(); j++){
554 std::vector<double> splits0 =
roots( deriv[
X] );
555 std::vector<double> splits90 =
roots( deriv[
Y] );
556 std::vector<double> splits45 =
roots( deriv[
X]- deriv[
Y] );
557 std::vector<double> splits135 =
roots( deriv[
X] + deriv[
Y] );
558 std::vector<double> splits;
559 splits.insert(splits.begin(), splits0.begin(), splits0.end() );
560 splits.insert(splits.begin(), splits90.begin(), splits90.end() );
561 splits.insert(splits.begin(), splits45.begin(), splits45.end() );
562 splits.insert(splits.begin(), splits135.begin(), splits135.end() );
563 process_splits(splits,0,1);
565 for(
unsigned k = 1; k < splits.size(); k++){
569 tile.f = splits[k-1];
574 tile.fbox =
Rect(fp, fp );
575 tile.tbox =
Rect(tp, tp );
578 tile.reversed = Point::LexLessRt(dim)(tp, fp);
580 tiles_data.push_back(tile);
584 std::sort(tiles_data.begin(), tiles_data.end(), SweepOrder(dim) );
587 void splitTile(
unsigned i,
double t,
double tolerance=0,
bool sort =
true){
588 assert( i<tiles_data.size() );
589 Tile newtile = tiles_data[i];
590 assert( newtile.f < t && t < newtile.t );
593 Point p =
paths[newtile.path][newtile.curve].pointAt(t);
594 newtile.fbox =
Rect(p, p);
595 newtile.fbox.expandBy( tolerance );
596 tiles_data[i].tbox = newtile.fbox;
598 tiles_data.insert(tiles_data.begin()+i+1, newtile);
600 std::sort(tiles_data.begin()+i+1, tiles_data.end(), SweepOrder(dim) );
602 void splitTile(
unsigned i,
SmashIntersection inter,
unsigned which,
bool sort =
true){
603 double t = inter.
times[which].middle();
604 assert( i<tiles_data.size() );
605 Tile newtile = tiles_data[i];
606 assert( newtile.f < t && t < newtile.t );
608 newtile.fbox = inter.
bbox;
609 tiles_data[i].tbox = newtile.fbox;
611 tiles_data.insert(tiles_data.begin()+i+1, newtile);
613 std::sort(tiles_data.begin()+i+1, tiles_data.end(), SweepOrder(dim) );
616 void splitTile(
unsigned i, std::vector<double>
const ×,
double tolerance=0,
bool sort =
true){
617 if ( times.size()<3 )
return;
618 assert( i<tiles_data.size() );
619 std::vector<Tile> pieces ( times.size()-2, tiles_data[i] );
620 Rect prevbox = tiles_data[i].fbox;
621 for (
unsigned k=0; k < times.size()-2; k++){
622 pieces[k].
f = times[k];
623 pieces[k].t = times[k+1];
624 pieces[k].fbox = prevbox;
626 prevbox = fatPoint(
paths[tiles_data[i].path][tiles_data[i].
curve].pointAt(times[k+1]), tolerance );
627 pieces[k].tbox = prevbox;
629 tiles_data.insert(tiles_data.begin()+i, pieces.begin(), pieces.end() );
630 unsigned newi = i + times.size()-2;
631 assert( newi<tiles_data.size() );
633 tiles_data[newi].f = tiles_data[newi-1].t;
634 tiles_data[newi].fbox = tiles_data[newi-1].tbox;
637 std::sort(tiles_data.begin()+i, tiles_data.end(), SweepOrder(dim) );
640 void splitTile(
unsigned i, std::vector<std::pair<Interval,Rect> >
const &cuts,
bool sort =
true){
641 assert ( cuts.size() >= 2 );
642 assert( i<tiles_data.size() );
643 std::vector<Tile> pieces ( cuts.size()-1, tiles_data[i] );
644 for (
unsigned k=1; k+1 < cuts.size(); k++){
645 pieces[k-1].t = cuts[k].first.middle();
646 pieces[k ].f = cuts[k].first.middle();
647 pieces[k-1].tbox = cuts[k].second;
648 pieces[k ].fbox = cuts[k].second;
650 pieces.front().fbox.unionWith( cuts[0].second );
651 pieces.back().tbox.unionWith( cuts.back().second );
653 tiles_data.insert(tiles_data.begin()+i, pieces.begin(), pieces.end()-1 );
654 unsigned newi = i + cuts.size()-2;
655 assert( newi < tiles_data.size() );
656 tiles_data[newi] = pieces.back();
659 std::sort(tiles_data.begin()+i, tiles_data.end(), SweepOrder(dim) );
665 void splitIntersectingTiles(){
667 std::sort(tiles_data.begin(), tiles_data.end(), SweepOrder(dim) );
671 for (
unsigned i=0; i+1<tiles_data.size(); i++){
673 std::vector<SmashIntersection> inters_on_i;
674 for (
unsigned j=i+1; j<tiles_data.size(); j++){
676 if ( Point::LexLessRt(dim)(tiles_data[i].
max(), tiles_data[j].
min()) )
break;
678 unsigned pi = tiles_data[i].path;
679 unsigned ci = tiles_data[i].curve;
680 unsigned pj = tiles_data[j].path;
681 unsigned cj = tiles_data[j].curve;
682 std::vector<SmashIntersection> intersections;
685 paths[pj][cj],
Interval(tiles_data[j].f, tiles_data[j].t), tol );
686 inters_on_i.insert( inters_on_i.end(), intersections.begin(), intersections.end() );
687 std::vector<std::pair<Interval, Rect> > cuts = process_intersections(intersections, 1, j);
691 splitTile(j, cuts,
false);
699 std::vector<std::pair<Interval, Rect> > cuts_on_i = process_intersections(inters_on_i, 0, i);
700 splitTile(i, cuts_on_i,
false);
701 i += cuts_on_i.size()-2;
704 std::sort(tiles_data.begin()+i+1, tiles_data.end(), SweepOrder(dim) );
707 std::sort(tiles_data.begin(), tiles_data.end(), SweepOrder(dim) );
711 std::sort(
tiles.begin(),
tiles.end(), PtrSweepOrder(tiles_data.begin(), dim) );
719 void fuseInsert(
const Rect &b, std::vector<Rect> &boxes,
Dim2 dim){
721 for (
unsigned i=0; i<boxes.size(); i++){
722 if ( Point::LexLessRt(dim)( b.
max(), boxes[i].min() ) )
break;
726 boxes.erase( boxes.begin()+i );
727 fuseInsert( bigb, boxes, dim);
731 std::vector<Rect>::iterator pos = std::lower_bound(boxes.begin(), boxes.end(), b, SweepOrder(dim) );
732 boxes.insert( pos, b );
736 bool isContained(
Rect b, std::vector<Rect>
const &boxes ){
737 for (
const auto & boxe : boxes){
738 if ( boxe.contains(b) )
return true;
745 std::vector<Rect> collectBoxes(){
746 std::vector<Rect> ret;
747 for (
auto & i : tiles_data){
748 fuseInsert(i.fbox, ret, dim);
749 fuseInsert(i.tbox, ret, dim);
756 void enlargeTilesEnds(
const std::vector<Rect> &boxes ){
757 for (
unsigned i=0; i<tiles_data.size(); i++){
758 std::vector<Rect>::const_iterator f_it;
759 f_it = std::lower_bound(boxes.begin(), boxes.end(), tiles_data[i].fbox, SweepOrder(dim) );
760 if ( f_it==boxes.end() ) f_it--;
761 while (!(*f_it).contains(tiles_data[i].fbox) && f_it != boxes.begin()){
764 assert( (*f_it).contains(tiles_data[i].fbox) );
765 tiles_data[i].fbox = *f_it;
767 std::vector<Rect>::const_iterator t_it;
768 t_it = std::lower_bound(boxes.begin(), boxes.end(), tiles_data[i].tbox, SweepOrder(dim) );
769 if ( t_it==boxes.end() ) t_it--;
770 while (!(*t_it).contains(tiles_data[i].tbox) && t_it != boxes.begin()){
773 assert( (*t_it).contains(tiles_data[i].tbox) );
774 tiles_data[i].tbox = *t_it;
777 tiles_data[i].reversed = Point::LexLessRt(dim)( tiles_data[i].tbox.min(), tiles_data[i].fbox.min());
780 tiles_data.erase(tiles_data.begin()+i);
788 bool splitTilesThroughFatPoints(std::vector<Rect> &boxes ){
790 std::sort(
tiles.begin(),
tiles.end(), PtrSweepOrder(tiles_data.begin(), dim) );
791 std::sort(boxes.begin(), boxes.end(), SweepOrder(dim) );
794 for (
unsigned i=0; i<tiles_data.size(); i++){
795 for (
auto & boxe : boxes){
796 if ( Point::LexLessRt(dim)( tiles_data[i].
max(), boxe.min()) )
break;
797 if ( Point::LexLessRt(dim)( boxe.max(), tiles_data[i].min()) )
continue;
798 if ( !boxe.intersects( tiles_data[i].bbox() ) )
continue;
799 if ( tiles_data[i].fbox.
intersects( boxe ) )
continue;
800 if ( tiles_data[i].tbox.
intersects( boxe ) )
continue;
806 for (
unsigned corner=0; corner<4; corner++){
807 unsigned D = corner % 2;
808 double val = boxe.corner(corner)[
D];
809 std::vector<double> times =
roots(
c[
D] - val );
810 if ( times.size()>0 ){
811 double t =
Geom::lerp(times.front(), tiles_data[i].f, tiles_data[i].t);
812 double hit_place =
c[1-
D](times.front());
817 splitTile( i, t, tol,
false);
864 case 0: ret[
X].setMax(( a.
max()[
X] + b.
min()[
X] )/ 2);
break;
865 case 1: ret[
X].setMin(( b.
max()[
X] + a.
min()[
X] )/ 2);
break;
866 case 2: ret[
Y].setMax(( a.
max()[
Y] + b.
min()[
Y] )/ 2);
break;
867 case 3: ret[
Y].setMin(( b.
max()[
Y] + a.
min()[
Y] )/ 2);
break;
877 for (
auto & vtxboxe : vtxboxes){
878 if ( Point::LexLessRt(dim)( sep->max(), vtxboxe.min() ) ){
882 OptRect sepi = separate(box, vtxboxe);
886 std::cout<<
"box="<<box<<
"\n";
887 std::cout<<
"vtxboxes[i]="<<vtxboxe<<
"\n";
892 if (!sep) THROW_EXCEPTION(
"Invalid intersection data.");
904 ExitPoint(
unsigned s,
double p,
unsigned r,
double t){
915 bool operator()(
Ray a,
Ray b)
const {
916 if ( a.exit_side < b.exit_side )
return true;
917 if ( a.exit_side > b.exit_side )
return false;
918 if ( a.exit_side <= 1) {
919 return ( a.exit_place < b.exit_place );
921 return ( a.exit_place > b.exit_place );
925 void printRay(
Ray const &r){
926 std::printf(
"Ray: tile=%u, centrifuge=%u, side=%u, place=%f\n",
927 r.tile, r.centrifuge, r.exit_side, r.exit_place);
929 void printVertex(FatVertex
const &v){
930 std::printf(
"Vertex: [%f,%f]x[%f,%f]\n", v[
X].
min(),v[
X].
max(),v[
Y].
min(),v[
Y].
max() );
931 for (
const auto & ray :
v.rays){
938 void sortRays( FatVertex &v ){
939 OptRect sep = isolateVertex(v);
941 for (
unsigned i=0; i <
v.rays.size(); i++){
942 v.rays[i].centrifuge = (tiles_data[
v.rays[i].tile ].fbox ==
v);
943 v.rays[i].exit_time =
v.rays[i].centrifuge ? 1 : 0 ;
946 for (
unsigned i=0; i <
v.rays.size(); i++){
948 assert(
v.rays[i].tile < tiles_data.size() );
949 D2<SBasis> c = tileToSB( tiles_data[
v.rays[i].tile ] );
951 for (
unsigned side=0; side<4; side++){
952 double level = sep->corner( side )[1-side%2];
954 std::vector<double> times =
roots(
c[1-side%2]-level);
955 if ( times.size() > 0 ) {
957 assert(
v.rays[i].tile < tiles_data.size() );
958 if (tiles_data[
v.rays[i].tile ].fbox == v){
960 if (
v.rays[i].exit_side > 3 ||
v.rays[i].exit_time > t ){
961 v.rays[i].setExitInfo( side,
c[side%2](t), t);
965 if (
v.rays[i].exit_side > 3 ||
v.rays[i].exit_time < t ){
966 v.rays[i].setExitInfo( side,
c[side%2](t), t);
975 std::sort(
v.rays.begin(),
v.rays.end(), ExitOrder() );
990 createMonotonicTiles();
993 splitIntersectingTiles();
997 vtxboxes = collectBoxes();
998 }
while ( splitTilesThroughFatPoints(vtxboxes) );
1000 enlargeTilesEnds(vtxboxes);
1003 tiles = std::vector<unsigned>(tiles_data.size(), 0);
1004 for (
unsigned i=0; i<tiles_data.size(); i++){
1010 if (tiles_data.size()>0){
1012 context.last_pos = tiles_data[
tiles.front()].min();
1013 context.last_pos[dim] -= 1;
1014 context.pending_vertex = FatVertex();
1015 context.pending_event = Event();
1027 Event getNextEvent(){
1032 Event old_event = context.pending_event, event;
1034 applyEvent(context.pending_event);
1037 if (context.pending_vertex.rays.size()== 0){
1042 std::vector<unsigned>::iterator low, high;
1044 for ( low =
tiles.begin(); low !=
tiles.end() &&
1045 ( tiles_data[*low].state==1 || Point::LexLessRt(dim)(tiles_data[*low].cur_box().min(), context.last_pos) ); low++){}
1047 if ( low ==
tiles.end() ){
1051 Rect pos = tiles_data[ *low ].cur_box();
1052 context.last_pos = pos.
min();
1053 context.last_pos[1-dim] = pos.
max()[1-dim];
1064 v.rays.push_back(
Ray(*high, tiles_data[*high].state!=0) );
1066 }
while( high !=
tiles.end() && tiles_data[ *high ].cur_box()==pos );
1074 for( i=0; i<
v.rays.size(); ++i){assert(
v.rays[i].tile<tiles_data.size() );}
1076 for( i=0; i<
v.rays.size() && tiles_data[
v.rays[i].tile ].state!=0; ++i){}
1079 if (i ==
v.rays.size() ){
1081 event.insert_at = contextRanking(pos.
min());
1087 std::vector<Ray> head;
1088 head.assign (
v.rays.begin(),
v.rays.begin()+i );
1089 v.rays.erase (
v.rays.begin(),
v.rays.begin()+i );
1090 v.rays.insert(
v.rays.end(), head.begin(), head.end());
1093 assert( tiles_data[
v.rays[0].tile ].state==0 );
1094 event.insert_at = context.find(
v.rays.front().tile )+1;
1098 context.pending_vertex =
v;
1101 event.tile = context.pending_vertex.rays.front().tile;
1102 event.insert_at = old_event.insert_at;
1112 event.tile = context.pending_vertex.rays.front().tile;
1114 event.opening = tiles_data[
event.tile ].state!=0;
1115 event.to_be_continued = context.pending_vertex.rays.size()>1;
1118 context.pending_vertex.rays.erase(context.pending_vertex.rays.begin());
1119 context.pending_event = event;
Defines the different types of exceptions that 2geom can throw.
Path - a sequence of contiguous curves.
Basic intersection routines.
std::vector< Tile > tiles
Abstract continuous curve on a plane defined on [0,1].
virtual D2< SBasis > toSBasis() const =0
Convert the curve to a symmetric power basis polynomial.
Adaptor that creates 2D functions from 1D ones.
D2< SBasis > toSBasis() const
constexpr C extent() const
bool intersects(GenericRect< C > const &r) const
Check whether the rectangles have any common points.
void unionWith(CRect const &b)
Enlarge the rectangle to contain the argument.
CPoint min() const
Get the corner of the rectangle with smallest coordinate values.
CPoint max() const
Get the corner of the rectangle with largest coordinate values.
Range of real numbers that is never empty.
Axis-aligned rectangle that can be empty.
Two-dimensional point that doubles as a vector.
Straight ray from a specific point to infinity.
Axis aligned, non-empty rectangle.
Generic sweepline algorithm.
constexpr Coord lerp(Coord t, Coord a, Coord b)
Numerically stable linear interpolation.
Dim2
2D axis enumeration (X or Y).
constexpr Coord infinity()
Get a value representing infinity.
constexpr Coord EPSILON
Default "acceptably small" value.
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.
bool contains(Path const &p, Point const &i, bool evenodd=true)
std::vector< double > roots(SBasis const &s)
std::vector< SmashIntersection > monotonic_smash_intersect(D2< SBasis > const &a, D2< SBasis > const &b, double tol)
Bezier portion(const Bezier &a, double from, double to)
Bezier derivative(Bezier const &a)
Piecewise< SBasis > min(SBasis const &f, SBasis const &g)
Return the more negative of the two functions pointwise.
SBasis toSBasis(SBasisN< 1 > f)
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
std::vector< Point > intersect(const xAx &C1, const xAx &C2)
std::unique_ptr< SPDocument > open(Extension *key, char const *filename, bool is_importing)
This is a generic function to use the open function of a module (including Autodetect)
PathVector - a sequence of subpaths.