-
Notifications
You must be signed in to change notification settings - Fork 111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
4-segment Bezier circle inaccuracy #817
Comments
I fear that this is not going to be possible because of the way intersections work. They are enumerated by path time within path segments, so changing the number of segments could change the order of intersections which might break a lot of drawings. To reasonably tackle this we'd first have to resolve #666. |
I've just checked using \documentclass[border=5pt]{standalone}
\usepackage{l3draw}
\begin{document}
\ExplSyntaxOn
\draw_begin:
\draw_transform_scale:n { 10 }
\draw_path_circle:nn { 0 , 0 } { 1cm }
\draw_path_use:n { stroke , clip }
\draw_path_circle:nn { 1cm , (1/5)cm } { (1/5)cm }
\draw_path_use_clear:n { stroke }
\draw_color:n { cyan }
\draw_path_circle:nn { -1cm , 5cm } { 5cm }
\draw_path_use_clear:n { stroke }
\draw_end:
\ExplSyntaxOff
\end{document} (You'll need the fix in latex3/latex3#660 to make this work). So I think it is due to the use of a four-part approximation, not due to the inaccuracies in calculation. I might look to adjust From @muzimuzhi: |
It's just due to the fact that you can't draw a perfect circle with Bézier curves. Even with more segments it will not match. BTW, MetaPost also only uses four segments. |
Increasing the number of segments yields diminishing returns, though the 8-segment curve still looks like a perfect intersection at 64x magnification (worst case here is closer to r=1/9 and r=9), whereas the 4-segment curve is off by nearly a line width. The r=1/5 and r=5 combination places the angle subtended by the unit circle from the horizontal to the point of intersection very close to 22.5°, which is where the error is a maximum in the 4-segment approximation. |
Scaling up an approximation uniformly, is not possible regardless of segment number but of course it gets better. It is also known that the approximation error is worst at the plus/minus angle as you mentioned. Here is a precise calculation of those angles (see the plot) : http://spencermortensen.com/articles/bezier-circle/ However doubling the number of PDF objects for a minute improvement doesn't look like a feasible return to me. Also once you know this fact, only rotating the cyan circle would be a quite convincing intersection: \begin{tikzpicture}[scale=10]
\clip[postaction={draw, thick}] (0,0) circle [radius=1];
\draw (1,1/5) circle [radius={1/5}];
\draw[cyan, rotate around={22.5:(-1,5)}] (-1,5) circle [radius=5];
\end{tikzpicture} |
I've thought about this and I am not going to change the default circle. Too many drawings rely on this exact shape. However, I can offer adding this as a library solution, something like |
I agree that the circle command should stay with 4 segments (almost all softwares do this). If somebody needs a more precise circle it can use Remark : the TikZ code use |
I have made some python simulations to find the best replacement of Here is the fixed \documentclass[tikz,border=5pt]{standalone}
\usetikzlibrary{spy}
% replace 0.55228475 -> 0.55191502 for minimal radial deviation
% the OP example looks better with 0.5513 which is very far from optimal in general
\makeatletter
\def\bez@circle{0.55191502}
\def\pgfpathellipse#1#2#3{%
\pgfpointtransformed{#1}% store center in xc/yc
\pgf@xc=\pgf@x%
\pgf@yc=\pgf@y%
\pgfpointtransformed{#2}%
\pgf@xa=\pgf@x% store first axis in xa/ya
\pgf@ya=\pgf@y%
\advance\pgf@xa by-\pgf@pt@x%
\advance\pgf@ya by-\pgf@pt@y%
\pgfpointtransformed{#3}%
\pgf@xb=\pgf@x% store second axis in xb/yb
\pgf@yb=\pgf@y%
\advance\pgf@xb by-\pgf@pt@x%
\advance\pgf@yb by-\pgf@pt@y%
{%
\advance\pgf@xa by\pgf@xc%
\advance\pgf@ya by\pgf@yc%
\pgf@nlt@moveto{\pgf@xa}{\pgf@ya}%
}%
\pgf@x=\bez@circle\pgf@xb% first arc
\pgf@y=\bez@circle\pgf@yb%
\advance\pgf@x by\pgf@xa%
\advance\pgf@y by\pgf@ya%
\advance\pgf@x by\pgf@xc%
\advance\pgf@y by\pgf@yc%
\edef\pgf@temp{\pgf@xc\the\pgf@x\pgf@yc\the\pgf@y}%
\pgf@x=\bez@circle\pgf@xa%
\pgf@y=\bez@circle\pgf@ya%
\advance\pgf@x by\pgf@xb%
\advance\pgf@y by\pgf@yb%
{%
\advance\pgf@x by\pgf@xc%
\advance\pgf@y by\pgf@yc%
\advance\pgf@xb by\pgf@xc%
\advance\pgf@yb by\pgf@yc%
\pgf@temp%
\pgf@nlt@curveto{\pgf@xc}{\pgf@yc}{\pgf@x}{\pgf@y}{\pgf@xb}{\pgf@yb}%
}%
\pgf@xa=-\pgf@xa% flip first axis
\pgf@ya=-\pgf@ya%
\pgf@x=\bez@circle\pgf@xa% second arc
\pgf@y=\bez@circle\pgf@ya%
\advance\pgf@x by\pgf@xb%
\advance\pgf@y by\pgf@yb%
\advance\pgf@x by\pgf@xc%
\advance\pgf@y by\pgf@yc%
\edef\pgf@temp{\pgf@xc\the\pgf@x\pgf@yc\the\pgf@y}%
\pgf@x=\bez@circle\pgf@xb%
\pgf@y=\bez@circle\pgf@yb%
\advance\pgf@x by\pgf@xa%
\advance\pgf@y by\pgf@ya%
{%
\advance\pgf@x by\pgf@xc%
\advance\pgf@y by\pgf@yc%
\advance\pgf@xa by\pgf@xc%
\advance\pgf@ya by\pgf@yc%
\pgf@temp%
\pgf@nlt@curveto{\pgf@xc}{\pgf@yc}{\pgf@x}{\pgf@y}{\pgf@xa}{\pgf@ya}%
}%
\pgf@xb=-\pgf@xb% flip second axis
\pgf@yb=-\pgf@yb%
\pgf@x=\bez@circle\pgf@xb% third arc
\pgf@y=\bez@circle\pgf@yb%
\advance\pgf@x by\pgf@xa%
\advance\pgf@y by\pgf@ya%
\advance\pgf@x by\pgf@xc%
\advance\pgf@y by\pgf@yc%
\edef\pgf@temp{\pgf@xc\the\pgf@x\pgf@yc\the\pgf@y}%
\pgf@x=\bez@circle\pgf@xa%
\pgf@y=\bez@circle\pgf@ya%
\advance\pgf@x by\pgf@xb%
\advance\pgf@y by\pgf@yb%
{%
\advance\pgf@x by\pgf@xc%
\advance\pgf@y by\pgf@yc%
\advance\pgf@xb by\pgf@xc%
\advance\pgf@yb by\pgf@yc%
\pgf@temp%
\pgf@nlt@curveto{\pgf@xc}{\pgf@yc}{\pgf@x}{\pgf@y}{\pgf@xb}{\pgf@yb}%
}%
\pgf@xa=-\pgf@xa% flip first axis once more
\pgf@ya=-\pgf@ya%
\pgf@x=\bez@circle\pgf@xa% fourth arc
\pgf@y=\bez@circle\pgf@ya%
\advance\pgf@x by\pgf@xb%
\advance\pgf@y by\pgf@yb%
\advance\pgf@x by\pgf@xc%
\advance\pgf@y by\pgf@yc%
\edef\pgf@temp{\pgf@xc\the\pgf@x\pgf@yc\the\pgf@y}%
\pgf@x=\bez@circle\pgf@xb%
\pgf@y=\bez@circle\pgf@yb%
\advance\pgf@x by\pgf@xa%
\advance\pgf@y by\pgf@ya%
{%
\advance\pgf@x by\pgf@xc%
\advance\pgf@y by\pgf@yc%
\advance\pgf@xa by\pgf@xc%
\advance\pgf@ya by\pgf@yc%
\pgf@temp%
\pgf@nlt@curveto{\pgf@xc}{\pgf@yc}{\pgf@x}{\pgf@y}{\pgf@xa}{\pgf@ya}%
}%
\pgf@nlt@closepath%
\pgf@nlt@moveto{\pgf@xc}{\pgf@yc}%
}
\begin{document}
\begin{tikzpicture}[scale=10, spy using outlines={circle, size=35mm, connect spies}]
\draw (0,0) circle [radius=1];
\clip (0,0) circle [radius=1.1];
\draw (1,1/5) circle [radius={1/5}];
\draw[radius=5] [cyan] (-1,5) circle;
\coordinate(A) at (12/13,5/13);
\spy[magnification=5,green]
on (A)
in node [left] at (.5,.5);
\end{tikzpicture}
\end{document} It may be good to replace If we do this, we should probably replace |
PGF uses a 4-segment approximation to draw circles. It's possible for this to show inaccuracies visible in publication at typical page-size dimensions when small and large radius circles are mixed and expected to intersect precisely. Suggestion: upgrade the circle & ellipse code to use an 8-segment approximation.
Minimal working example (MWE)
These circles and arcs should intersect at a single point, but they are visibly off on the scale of a letter/A4 page.
Better results are achieved with an 8-segment approximation. The control point used is
4/3 tan (pi/16)
(= 0.265216...) instead of4/3 tan (pi/8)
(=0.552284... as used in\pgfpathellipse
)The text was updated successfully, but these errors were encountered: