Follow-up Comment #5, bug #65910 (group groff): I'm not yet convinced that deep mathematical sophistication is required here. (If it is, I'll try to recover what meager allotment of that I may once have had.)
The GNU _pic_ function that breaks up a dashed ellipse in to a bunch of elliptical arcs is called `ellipse_arc()`. I crudely instrumented it as follows. diff --git a/src/preproc/pic/common.cpp b/src/preproc/pic/common.cpp index 6a4a93eb9..e72c67807 100644 --- a/src/preproc/pic/common.cpp +++ b/src/preproc/pic/common.cpp @@ -1,4 +1,3 @@ -// -*- C++ -*- /* Copyright (C) 1989-2020 Free Software Foundation, Inc. Written by James Clark (j...@jclark.com) @@ -172,8 +171,21 @@ void common_output::dashed_ellipse(const position ¢, const distance &dim, // and use it to get the exact value on the ellipse double psi = atan2(zdot.y / dim_y, zdot.x / dim_x); zdot = position(dim_x * cos(psi), dim_y * sin(psi)); - if ((i % 2 == 0) && (i > 1)) + if ((i % 2 == 0) && (i > 1)) { + fprintf(stderr, "GBR: ellipse_arc(" + "cent=(%g, %g); " + "zpre=(%5.2f,%5.2f); " + "zdot=(%5.2f,%5.2f); " + "dim/2=(%g, %g); " + "line type=%d)\n", + cent.x, cent.y, + zpre.x, zpre.y, + zdot.x, zdot.y, + (dim.x / 2.0), (dim.y / 2.0), + slt.type); + fflush(stderr); ellipse_arc(cent, zpre, zdot, dim / 2, slt); + } } } I tuned the floating point output formats deliberately. We don't want to be blitzed with numerals; we want to sanity check the values at something approximating a glance. I don't have mastery yet of what this function does but the rough concept seems to be pretty straightforward: a dashed ellipse is like a regular ellipse except you chop it up into N elliptical arcs, and you lift and lower the pen each time you draw one of those arcs, in alternation. That's what the expression "(i % 2 == 0)" does for us--we draw only the "even" arcs, not the odd ones. There's some business about deriving the placement of the arcs from the "affine circle". I once could rattle off with ease what an affine transform is. I seem to remember that it's a linear transform with the origin preserved.[1] (A linear transform is mapping between linear spaces. Linear spaces are mathematical structures where a dot product is defined. I think. It's been over a decade since I used linear algebra in anger, and that makes me a little sad. I *loved* linear algebra.) Anyway. This function's parameters are: 1. Coordinates of the ellipse's center. 2. Coordinates of the previous location of "dot" (a pen-up/pen-down location); 3. Coordinates of the current location of "dot"; 4. The halved dimensions of the ellipse, a.k.a. the semi-major and semi-minor axis lengths; and 5. The line type, which just a C++ enum for "invisible, solid, dotted, dashed". The line type here is always "solid" because I guess we can't count on the output device to draw a dashed arc, so we construct it ourselves--with this very function. Here is the output. $ ./build/pic -t EXPERIMENTS/dashed-ellipse-8x3.ptex >| EXPERIMENTS/dashed-ellipse-8x3.tex GBR: ellipse_arc(cent=(4, 0); zpre=( 3.89, 0.35); zdot=( 3.73, 0.54); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=( 3.53, 0.71); zdot=( 3.24, 0.88); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=( 3.09, 0.95); zdot=( 2.83, 1.06); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=( 2.62, 1.13); zdot=( 2.35, 1.21); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=( 2.13, 1.27); zdot=( 1.82, 1.34); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=( 1.63, 1.37); zdot=( 1.24, 1.43); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=( 1.13, 1.44); zdot=( 0.63, 1.48); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=( 0.63, 1.48); zdot=( 0.00, 1.50); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=( 0.00, 1.50); zdot=(-0.13, 1.50); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=(-0.63, 1.48); zdot=(-0.63, 1.48); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=(-1.24, 1.43); zdot=(-1.24, 1.43); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=(-1.39, 1.41); zdot=(-1.82, 1.34); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=(-1.89, 1.32); zdot=(-2.35, 1.21); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=(-2.38, 1.21); zdot=(-2.83, 1.06); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=(-2.86, 1.05); zdot=(-3.24, 0.88); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=(-3.32, 0.83); zdot=(-3.56, 0.68); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=(-3.73, 0.54); zdot=(-3.89, 0.35); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=(-3.99, 0.11); zdot=(-3.98,-0.13); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=(-3.88,-0.36); zdot=(-3.72,-0.55); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=(-3.52,-0.71); zdot=(-3.24,-0.88); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=(-3.08,-0.96); zdot=(-2.83,-1.06); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=(-2.61,-1.14); zdot=(-2.35,-1.21); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=(-2.12,-1.27); zdot=(-1.82,-1.34); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=(-1.62,-1.37); zdot=(-1.24,-1.43); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=(-1.12,-1.44); zdot=(-0.63,-1.48); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=(-0.62,-1.48); zdot=(-0.00,-1.50); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=(-0.00,-1.50); zdot=( 0.14,-1.50); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=( 0.63,-1.48); zdot=( 0.64,-1.48); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=( 1.24,-1.43); zdot=( 1.24,-1.43); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=( 1.40,-1.40); zdot=( 1.82,-1.34); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=( 1.90,-1.32); zdot=( 2.35,-1.21); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=( 2.39,-1.20); zdot=( 2.83,-1.06); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=( 2.87,-1.04); zdot=( 3.24,-0.88); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=( 3.33,-0.83); zdot=( 3.56,-0.68); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=( 3.74,-0.53); zdot=( 3.90,-0.34); dim/2=(4, 1.5); line type=1) GBR: ellipse_arc(cent=(4, 0); zpre=( 3.99,-0.10); zdot=( 3.98, 0.14); dim/2=(4, 1.5); line type=1) So what we should be seeing here is a fairly regular change in x and y coordinates.[2] In fact I think if you graphed the foregoing you would get a polygon with dashed sides. The arc lengths between each adjacent pair of vertices should be close to uniform, and that's what we're **NOT** seeing in the visible output. These numbers look okay to me but the human brain is a poor device for analyzing such things. Because we're drawing a curve, the changes in neither the x nor the y coordinates will be constant. But they will, or should, have a uniform property: the arc length. The nonuniformity of the arc length is what jumps out at us and prompted the filing of this bug. A dashed ellipse should have uniform dashes. We can compute the arc length the way they taught us in second-semester calculus: ds=dy/dx, except we don't care about signs so we'd take the absolute value. More prosaically, one can use the "distance formula" (square root of ((y2 - y1) over (x2 - x1))--again we won't care about signs. That's what I think my next step is: to compute and report the arc length at each stage. If it remains consistent within a certain tolerance, we'll know that bug lies somewhere other than `ellipse_arc()`, possibly in the logic that translates these arcs to _troff_ or TeX commands. It's really interesting to me that the same bug affects both output formats, though. That's why I started here. [1] Or maybe it's the other way around: an affine transform is the general case and a linear transform keeps the origin the same(?). I remember learning that the meaning of "linear" we were taught as non-math majors was actually "affine" to true mathematicians. This made me angry. [2] I say "regular". The magnitudes of the changes to x and y coordinates actually would be periodic, I think. I'd bet a shot of whiskey that they're sinusoidal. What they should **not** be is, technically, "bonkers". _______________________________________________________ Reply to this item at: <https://savannah.gnu.org/bugs/?65910> _______________________________________________ Message sent via Savannah https://savannah.gnu.org/
signature.asc
Description: PGP signature