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.
This commit is contained in:
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);