Improve Bezier to piecewise linear conversion.

Instead of always using two points on every curve, with a hack for
some cubics edge case, use three points on the first iteration and
one point on every further iteration. This both faster and more
correct.
pull/4/head
EvilSpirit 2016-01-23 14:04:13 +06:00 committed by whitequark
parent 1e2a899ba2
commit 139dd80b48
2 changed files with 32 additions and 9 deletions

View File

@ -269,25 +269,47 @@ void SBezier::MakePwlInto(List<Vector> *l, double chordTol) {
// Never do fewer than one intermediate point; people seem to get // Never do fewer than one intermediate point; people seem to get
// unhappy when their circles turn into squares, but maybe less // unhappy when their circles turn into squares, but maybe less
// unhappy with octagons. // unhappy with octagons.
MakePwlWorker(l, 0.0, 0.5, chordTol); MakePwlInitialWorker(l, 0.0, 0.5, chordTol);
MakePwlWorker(l, 0.5, 1.0, chordTol); MakePwlInitialWorker(l, 0.5, 1.0, chordTol);
} }
} }
void SBezier::MakePwlWorker(List<Vector> *l, double ta, double tb, void SBezier::MakePwlWorker(List<Vector> *l, double ta, double tb, double chordTol)
double chordTol)
{ {
Vector pa = PointAt(ta); Vector pa = PointAt(ta);
Vector pb = PointAt(tb); Vector pb = PointAt(tb);
// Can't test in the middle, or certain cubics would break. Vector pm = PointAt((ta + tb) / 2.0);
double tm1 = (2*ta + tb) / 3; double d = pm.DistanceToLine(pa, pb.Minus(pa));
double tm2 = (ta + 2*tb) / 3;
double step = 1.0/SS.maxSegments;
if((tb - ta) < step || d < chordTol) {
// A previous call has already added the beginning of our interval.
l->Add(&pb);
} else {
double tm = (ta + tb) / 2;
MakePwlWorker(l, ta, tm, chordTol);
MakePwlWorker(l, tm, tb, chordTol);
}
}
void SBezier::MakePwlInitialWorker(List<Vector> *l, double ta, double tb, double chordTol)
{
Vector pa = PointAt(ta);
Vector pb = PointAt(tb);
double tm1 = ta + (tb - ta) * 0.25;
double tm2 = ta + (tb - ta) * 0.5;
double tm3 = ta + (tb - ta) * 0.75;
Vector pm1 = PointAt(tm1); Vector pm1 = PointAt(tm1);
Vector pm2 = PointAt(tm2); Vector pm2 = PointAt(tm2);
Vector pm3 = PointAt(tm3);
Vector dir = pb.Minus(pa);
double d = max(pm1.DistanceToLine(pa, pb.Minus(pa)), double d = max({
pm2.DistanceToLine(pa, pb.Minus(pa))); pm1.DistanceToLine(pa, dir),
pm2.DistanceToLine(pa, dir),
pm3.DistanceToLine(pa, dir)
});
double step = 1.0/SS.maxSegments; double step = 1.0/SS.maxSegments;
if((tb - ta) < step || d < chordTol) { if((tb - ta) < step || d < chordTol) {

View File

@ -93,6 +93,7 @@ public:
void MakePwlInto(SContour *sc, double chordTol=0); void MakePwlInto(SContour *sc, double chordTol=0);
void MakePwlInto(List<Vector> *l, double chordTol=0); void MakePwlInto(List<Vector> *l, double chordTol=0);
void MakePwlWorker(List<Vector> *l, double ta, double tb, double chordTol); void MakePwlWorker(List<Vector> *l, double ta, double tb, double chordTol);
void MakePwlInitialWorker(List<Vector> *l, double ta, double tb, double chordTol);
void AllIntersectionsWith(SBezier *sbb, SPointList *spl); void AllIntersectionsWith(SBezier *sbb, SPointList *spl);
void GetBoundingProjd(Vector u, Vector orig, double *umin, double *umax); void GetBoundingProjd(Vector u, Vector orig, double *umin, double *umax);