[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[bug #65910] [pic] some dashed ellipse sizes produce irregular dashes
From: |
G. Branden Robinson |
Subject: |
[bug #65910] [pic] some dashed ellipse sizes produce irregular dashes |
Date: |
Wed, 10 Jul 2024 12:23:12 -0400 (EDT) |
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 (jjc@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
Message not available
Message not available
Message not available
Message not available