• ArcPrecise, an accurate arc

    From jdaw1@jdawiseman@gmail.com to comp.lang.postscript on Tue Jul 19 12:47:25 2022
    From Newsgroup: comp.lang.postscript

    The PostScript command `arc` can draw circles, and part circles. Adobe Distiller draws angles ren90-# as a single B|-zier cubic.
    A B|-zier cubic has eight parameters, `curveto` receiving the two from the `currentpoint`, and six from the stack. An arc must go through the correct endpoints, using four parameters. At the endpoints the direction of travel must be tangent to the circle, so each end absorbs another parameter. And, by symmetry, at the two ends the speeds of departure must be the same. rf|N+A-aOnly one parameter remains to be chosen, being the speed of departure from the endpoints.
    For a 90-# part of a unit circle, Adobe uses a speed of 0.552. Mathematica shows that the worst error happens at t ree 0.18864 (and at one minus this), an angle ree-a17.39-# (and 90-# minus this) where the radius is too large by about 212 parts per million. At t-a=-a-+, angle-a=-a45-#, the radius is too small by reA151 parts per million. These values are confirmed by testing with `flattenpath` rCa `pathforall`.
    For a circle of radius 540pt-a=-a7-+rC| =-a190.5mm, plausible on A4 or 8-+rC||u11rC|, the error is as large as 0.1145pt.
    Typically, a ree0.04mm error doesnrCOt matter: the eye would not perceive it midst an empty page. But if a circle is being drawn with `arc`, and things placed at its edge (locations computed with `sin` and `cos`), as my software http://github.com/jdaw1/placemat/ does, then these things could be falsely apart by 0.11pt. That isnrCOt a disaster, but could be a multi-pixel visible imperfection. And an unnecessary imperfection.
    This is solved by new PostScript routines `ArcPrecise`, and |a la `arcn`, `ArcPreciseN`.
    http://www.jdawiseman.com/2022/ArcPrecise.ps http://www.jdawiseman.com/2022/ArcPrecise.pdf http://www.jdawiseman.com/2022/ArcPrecise_bitmap_17.png (Adobe error of +212-appm)
    http://www.jdawiseman.com/2022/ArcPrecise_bitmap_73.png (Adobe error of +212-appm)
    http://www.jdawiseman.com/2022/ArcPrecise_bitmap_45.png (Adobe error of reA151-appm)
    http://www.jdawiseman.com/2022/ArcPrecise_bitmap_06.png (worst ArcPrecise error, +0.37-appm)
    Output is shown in the PDF and the .png extracts from it. The grey line, width 0.36pt, is a precise circle, made of tiny `lineto`s only rac-# apart. Underneath is a red line, width 0.60pt, drawn with AdoberCOs `arc`, the red diagonals touching its worst radii. On top is a blue line, width 0.12pt, drawn with `ArcPrecise`, short blue lines touching its worst points. The widths are chosen such that, where all are neatly aligned, each stripe of colour has width 0.12pt. Throughout, the grey and the blue are neatly aligned; but the red drifts out by almost 0.12pt, then in, then back out.
    Assume curve of angle ++. A speed of Tan[++/4]-+4/3 has the radius precisely correct at the midpoint, t-a=-a-+, angle-a=-a++/2. The radius is never too small, and is maximal at t-a=-a-+-areA-araOreU3 ree-a0.2113. For ++ small and in radians, the maximal radius happens near angle (-+-areA-araOreU3)-+++ ree-a0.2113-a++, where the radius is too big by ree-a2rU+-|-|-+3rU+-|-+++rU| =-a(++^6)/55296.
    `ArcPrecise` chooses curves of no more than 30-#. For a 30-# curve the actual worst error is 0.372662 parts per million; this approximation says -CrU|/2579890176-areA-a1 ree-a0.372647-appm. So it is a good approximation to the error.
    For a 540pt radius, `ArcPrecise` has a peak error of 0.00020pt. If the smallest error we care about is 0.01pt, half a pixel at 3600d.p.i., that doesnrCOt happen with radius-aren-a9.46-ametres ree-a31-afeet. This is bigger than the PDF standardrCOs maximum page size of only 200rC|-a=-a16rao-afeet =-a5.08-ametres. Further, PostScriptrCOs arithmetic is single-precision, with a 23-bit mantissa, which is only slightly more accurate than 0.37-appm. So 30-# curves are sufficiently small.
    There is also a little neatness in the choice of curve-end angles. If every multiple of 90-# is a curve endpoint, then `pathbbox` returns the correct minimal box. So the angle choosing algorithm works as follows. It computes the next multiple of 90-#, If thatrCOs ren30-# away, done in one curve; else if ren60-# away, done in two equal-angle curves; otherwise split into three equal-angle curves. Then 30-# curves until the final multiple of 90-#; the final section, like the first, in at most three equal-angle curves each ren30-#. This means that: curves are all ren30-#; for a non-rotated frame `pathbbox` works optimally; and the number of curves is at most 1-a+-arie|angree-areA-aangreU|-a|+-a30-#rie.
    The build-in routines start with a line from a currentpoint, if there is one. This is annoying. Consider an annulus: thererCOs a line between the inner and outer circles, avoiding which requires a manual moveto. `ArcPrecise` and `ArcPreciseN` prevent this annoyance by starting, always, with a moveto, never a lineto.
    Comment welcomed.
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From jdaw1@jdawiseman@gmail.com to comp.lang.postscript on Wed Jul 20 11:25:21 2022
    From Newsgroup: comp.lang.postscript

    The build-in routines start with a line from a currentpoint, if there is one. This is annoying. Consider an annulus: thererCOs a line between the inner and outer circles, avoiding which requires a manual moveto. `ArcPrecise` and `ArcPreciseN` prevent this annoyance by starting, always, with a moveto, never a lineto.
    I think this is an error. What if the user wants a rounded rectangle. That isnrCOt possible if `ArcPrecise` starts with a `moveto`. Perhaps there could be an extra parameter, code, usually being one of {moveto}, {lineto}, {pop pop}. Advice welcomed.
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From David Newall@davidn@davidnewall.com to comp.lang.postscript on Thu Jul 21 18:29:21 2022
    From Newsgroup: comp.lang.postscript

    On 21/7/22 04:25, jdaw1 wrote:
    The build-in routines start with a line from a currentpoint, if there is one. This is annoying. Consider an annulus: thererCOs a line between the inner and outer circles, avoiding which requires a manual moveto. `ArcPrecise` and `ArcPreciseN` prevent this annoyance by starting, always, with a moveto, never a lineto.

    I think this is an error. What if the user wants a rounded rectangle. That isnrCOt possible if `ArcPrecise` starts with a `moveto`. Perhaps there could be an extra parameter, code, usually being one of {moveto}, {lineto}, {pop pop}. Advice welcomed.

    Use PS arc: you (often) have to add moveto. Use ArcPrecise: you
    (sometimes) have to add lineto. It seems unimportant.

    OTOH, if the goal is to make a better arc, start with lineto instead of
    moveto so programmers have no surprises when they use ArcPrecise.
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From John Reiser@vendor@BitWagon.com to comp.lang.postscript on Thu Jul 21 07:17:19 2022
    From Newsgroup: comp.lang.postscript

    On 7/20/22 11:25, jdaw1 wrote:
    The build-in routines start with a line from a currentpoint, if there is one. This is annoying. Consider an annulus: thererCOs a line between the inner and outer circles, avoiding which requires a manual moveto. `ArcPrecise` and `ArcPreciseN` prevent this annoyance by starting, always, with a moveto, never a lineto.

    I think this is an error. What if the user wants a rounded rectangle. That isnrCOt possible if `ArcPrecise` starts with a `moveto`. Perhaps there could be an extra parameter, code, usually being one of {moveto}, {lineto}, {pop pop}. Advice welcomed.

    Advice: 'ArcPrecise' should have the same API as 'arc'. Why? When I see
    an image that I want to improve by using ArcPrecise instead of arc, then
    I want the *option* to interpose a global dictionary which defines 'arc'
    as 'ArcPrecise', and have better arcs be the only change to the image.
    I might not have access to the innards of the rest of the Postscript code;
    it might not be possible to use a text editor to change all calls on "arc".
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From jdaw1@jdawiseman@gmail.com to comp.lang.postscript on Thu Jul 21 12:02:39 2022
    From Newsgroup: comp.lang.postscript

    so programmers have no surprises when they use ArcPrecise.
    Good desideratum.
    IrCOm coming to the view that ArcPrecise should have an extra parameter, which could be one of three values:
    rL- `/l` rf|N+A `arc`-style `lineto`;
    rL- `/m` rf|N+A `moveto`, especially useful if previous command was `closepath`;
    rL- `/n` rf|N+A nothing, presumably because the currentpoint is already the start of the curve.
    The compulsory extra parameter prevents surprises.
    Advice: 'ArcPrecise' should have the same API as 'arc'.
    Good desideratum. And
    `/arc {/l ArcPrecise} def`
    would work as you require.
    Would this extra final parameter, `/l` | `/m` | `/n`, satisfy everybody?
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From luser droog@luser.droog@gmail.com to comp.lang.postscript on Thu Jul 21 14:31:21 2022
    From Newsgroup: comp.lang.postscript

    On Thursday, July 21, 2022 at 9:17:26 AM UTC-5, John Reiser wrote:
    On 7/20/22 11:25, jdaw1 wrote:
    The build-in routines start with a line from a currentpoint, if there is one. This is annoying. Consider an annulus: thererCOs a line between the inner and outer circles, avoiding which requires a manual moveto. `ArcPrecise` and `ArcPreciseN` prevent this annoyance by starting, always, with a moveto, never a lineto.

    I think this is an error. What if the user wants a rounded rectangle. That isnrCOt possible if `ArcPrecise` starts with a `moveto`. Perhaps there could be an extra parameter, code, usually being one of {moveto}, {lineto}, {pop pop}. Advice welcomed.
    Advice: 'ArcPrecise' should have the same API as 'arc'. Why? When I see
    an image that I want to improve by using ArcPrecise instead of arc, then
    I want the *option* to interpose a global dictionary which defines 'arc'
    as 'ArcPrecise', and have better arcs be the only change to the image.
    I might not have access to the innards of the rest of the Postscript code; it might not be possible to use a text editor to change all calls on "arc".
    I lean towards endorsing this view, but I don't hold the opinion very strongly. Having a drop-in replacement is very useful. Having a considered and improved behavior based on the common use cases is very useful. I'm not sure how
    to weigh these benefits against each other.
    For the implementation side -- apart from the policy decision -- mimicking
    the existing API of `arc` can be pretty short and sweet:
    % x0 y0
    { lineto } stopped { moveto } if
    This leads to the question: how can you (most) easily get the starting point out of `arc` or `ArcPrecise` (were it to follow this convention) in order to call `moveto` first to eliminate the line segment (erhm, make its length zero)? If you call `arc` or `ArcPrecise` with an angular difference of zero, it ought to result in adding to the path just that initial point, right? Then you'd just need a crazy function to rewrite the path where the very last `lineto` is changed to a `moveto`. Too ugly for me to want to write it right now, but
    feels possible to write.
    The extra parameter idea is nice. Extra parameterization is the obvious
    way to select variant behaviors. Whether it "looks nice" is a subjective judgement.
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From jdaw1@jdawiseman@gmail.com to comp.lang.postscript on Thu Jul 21 15:00:18 2022
    From Newsgroup: comp.lang.postscript

    { lineto } stopped { moveto } if

    Nice. I was testing `currentpoint` with `stopped`: this is more concise. Thank you.
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From jdaw1@jdawiseman@gmail.com to comp.lang.postscript on Thu Jul 21 15:24:17 2022
    From Newsgroup: comp.lang.postscript

    An observation about the maths.
    A speed of Tan[++/4]-+4/3 has the radius precisely correct at the midpoint
    If ++ = 90-#, then the speed is Tan[22-+-#]-+4/3 =-a(reU2-areA-a1)-+4/3 ree-a0.55228474983, which is only an edge bigger than AdoberCOs fixed value of 0.552.
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From jdaw1@jdawiseman@gmail.com to comp.lang.postscript on Fri Jul 22 00:58:02 2022
    From Newsgroup: comp.lang.postscript

    Is it the best name? Is an error fo 0.37-appm rCypreciserCO, or merely rCyaccuraterCO? Should it be ArcAccurate?
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From Phil Wakely@pwakely99@googlemail.com to comp.lang.postscript on Fri Jul 22 01:42:17 2022
    From Newsgroup: comp.lang.postscript

    On Friday, 22 July 2022 at 08:58:03 UTC+1, jdaw1 wrote:
    Is it the best name? Is an error fo 0.37 ppm rCypreciserCO, or merely rCyaccuraterCO? Should it be ArcAccurate?
    Are you achieving a more accurate result by calculating with greater precision, or by using a better method? Increased precision usually increases accuracy (exception cases aside), but accuracy can often be improved without increasing precision using better approaches or calculation sequence adjustment (typically to maintain improved precision through the calculation especially with limited precision variables). Both "Accurate" and "Precise" would nominally require qualification or quantification (how precise or accurate); ArcImproved could also work.
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From Phil Wakely@pwakely99@googlemail.com to comp.lang.postscript on Fri Jul 22 01:52:32 2022
    From Newsgroup: comp.lang.postscript

    Really it should be ArcImprovedAccuracy; but I suspect that may be too long for most people's taste.
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From jdaw1@jdawiseman@gmail.com to comp.lang.postscript on Fri Jul 22 14:03:11 2022
    From Newsgroup: comp.lang.postscript

    Cross-link: also mentioned at https://github.com/jdaw1/placemat/issues/164
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From jdaw1@jdawiseman@gmail.com to comp.lang.postscript on Sun Jul 24 09:02:25 2022
    From Newsgroup: comp.lang.postscript

    Done. Added to my code https://github.com/jdaw1/placemat/blob/main/PostScript/placemat.ps
    with commit https://github.com/jdaw1/placemat/commit/77143c29d01f9352a53fa98361cf4589cace92e8
    For the latest version, look in that .ps, searching for rCLArcAccurateBothrCY or rCLB23RW2QpIjUrCY.
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From jdaw1@jdawiseman@gmail.com to comp.lang.postscript on Mon Jan 22 11:07:52 2024
    From Newsgroup: comp.lang.postscript

    Also see https://stackoverflow.com/questions/77855798/postscript-circles-how-accurate-how-improve/
    --- Synchronet 3.21d-Linux NewsLink 1.2