void EpsFileWriter::Bezier(SBezier *sb) { Vector c, n = Vector::From(0, 0, 1); double r; if(sb->deg == 1) { MaybeMoveTo(sb->ctrl[0], sb->ctrl[1]); fprintf(f, " %.3f %.3f lineto\r\n", MmToPts(sb->ctrl[1].x - ptMin.x), MmToPts(sb->ctrl[1].y - ptMin.y)); } else if(sb->IsCircle(n, &c, &r)) { Vector p0 = sb->ctrl[0], p1 = sb->ctrl[2]; double theta0 = atan2(p0.y - c.y, p0.x - c.x), theta1 = atan2(p1.y - c.y, p1.x - c.x), dtheta = WRAP_SYMMETRIC(theta1 - theta0, 2*PI); MaybeMoveTo(p0, p1); fprintf(f, " %.3f %.3f %.3f %.3f %.3f %s\r\n", MmToPts(c.x - ptMin.x), MmToPts(c.y - ptMin.y), MmToPts(r), theta0*180/PI, theta1*180/PI, dtheta < 0 ? "arcn" : "arc"); } else if(sb->deg == 3 && !sb->IsRational()) { MaybeMoveTo(sb->ctrl[0], sb->ctrl[3]); fprintf(f, " %.3f %.3f %.3f %.3f %.3f %.3f curveto\r\n", MmToPts(sb->ctrl[1].x - ptMin.x), MmToPts(sb->ctrl[1].y - ptMin.y), MmToPts(sb->ctrl[2].x - ptMin.x), MmToPts(sb->ctrl[2].y - ptMin.y), MmToPts(sb->ctrl[3].x - ptMin.x), MmToPts(sb->ctrl[3].y - ptMin.y)); } else { BezierAsNonrationalCubic(sb); } }
void VectorFileWriter::BezierAsNonrationalCubic(SBezier *sb, int depth) { Vector t0 = sb->TangentAt(0), t1 = sb->TangentAt(1); // The curve is correct, and the first derivatives are correct, at the // endpoints. SBezier bnr = SBezier::From( sb->Start(), sb->Start().Plus(t0.ScaledBy(1.0/3)), sb->Finish().Minus(t1.ScaledBy(1.0/3)), sb->Finish()); double tol = SS.ChordTolMm() / SS.exportScale; // Arbitrary choice, but make it a little finer than pwl tolerance since // it should be easier to achieve that with the smooth curves. tol /= 2; bool closeEnough = true; int i; for(i = 1; i <= 3; i++) { double t = i/4.0; Vector p0 = sb->PointAt(t), pn = bnr.PointAt(t); double d = (p0.Minus(pn)).Magnitude(); if(d > tol) { closeEnough = false; } } if(closeEnough || depth > 3) { Bezier(&bnr); } else { SBezier bef, aft; sb->SplitAt(0.5, &bef, &aft); BezierAsNonrationalCubic(&bef, depth+1); BezierAsNonrationalCubic(&aft, depth+1); } }
void PdfFileWriter::Bezier(SBezier *sb) { if(sb->deg == 1) { MaybeMoveTo(sb->ctrl[0], sb->ctrl[1]); fprintf(f, "%.3f %.3f l\r\n", MmToPts(sb->ctrl[1].x - ptMin.x), MmToPts(sb->ctrl[1].y - ptMin.y)); } else if(sb->deg == 3 && !sb->IsRational()) { MaybeMoveTo(sb->ctrl[0], sb->ctrl[3]); fprintf(f, "%.3f %.3f %.3f %.3f %.3f %.3f c\r\n", MmToPts(sb->ctrl[1].x - ptMin.x), MmToPts(sb->ctrl[1].y - ptMin.y), MmToPts(sb->ctrl[2].x - ptMin.x), MmToPts(sb->ctrl[2].y - ptMin.y), MmToPts(sb->ctrl[3].x - ptMin.x), MmToPts(sb->ctrl[3].y - ptMin.y)); } else { BezierAsNonrationalCubic(sb); } }
void SvgFileWriter::Bezier(SBezier *sb) { Vector c, n = Vector::From(0, 0, 1); double r; if(sb->deg == 1) { MaybeMoveTo(sb->ctrl[0], sb->ctrl[1]); fprintf(f, "L%.3f,%.3f ", (sb->ctrl[1].x - ptMin.x), (ptMax.y - sb->ctrl[1].y)); } else if(sb->IsCircle(n, &c, &r)) { Vector p0 = sb->ctrl[0], p1 = sb->ctrl[2]; double theta0 = atan2(p0.y - c.y, p0.x - c.x), theta1 = atan2(p1.y - c.y, p1.x - c.x), dtheta = WRAP_SYMMETRIC(theta1 - theta0, 2*PI); // The arc must be less than 180 degrees, or else it couldn't have // been represented as a single rational Bezier. So large-arc-flag // must be false. sweep-flag is determined by the sign of dtheta. // Note that clockwise and counter-clockwise are backwards in SVG's // mirrored csys. MaybeMoveTo(p0, p1); fprintf(f, "A%.3f,%.3f 0 0,%d %.3f,%.3f ", r, r, (dtheta < 0) ? 1 : 0, p1.x - ptMin.x, ptMax.y - p1.y); } else if(!sb->IsRational()) { if(sb->deg == 2) { MaybeMoveTo(sb->ctrl[0], sb->ctrl[2]); fprintf(f, "Q%.3f,%.3f %.3f,%.3f ", sb->ctrl[1].x - ptMin.x, ptMax.y - sb->ctrl[1].y, sb->ctrl[2].x - ptMin.x, ptMax.y - sb->ctrl[2].y); } else if(sb->deg == 3) { MaybeMoveTo(sb->ctrl[0], sb->ctrl[3]); fprintf(f, "C%.3f,%.3f %.3f,%.3f %.3f,%.3f ", sb->ctrl[1].x - ptMin.x, ptMax.y - sb->ctrl[1].y, sb->ctrl[2].x - ptMin.x, ptMax.y - sb->ctrl[2].y, sb->ctrl[3].x - ptMin.x, ptMax.y - sb->ctrl[3].y); } } else { BezierAsNonrationalCubic(sb); } }