Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Sin wave with PostScript-style cubic Bézier curves

18 views
Skip to first unread message

jdawi...@gmail.com

unread,
May 20, 2017, 8:55:29 AM5/20/17
to
I was considering drawing a sin wave; wanted to do so efficiently; and though that others might wish to reuse the results.

Preliminaries:
• Control points are {x0,y0}, {x1,y1}, {x2,y2}, {x3,y3}.
• Have defined /HalfPi 1.57079632679489661923 def


Let’s try to have a single curve be 180°. Want to go through end-points, with correct tangents, and also want the tangent in the middle to be at a slope of 1:1 = 45°. That defines everything:
y0 = y1 = -1
y2 = y3 = +1
x0 = -HalfPi
x1 = HalfPi - 2
x2 = -x1
x3 = -x0

The largest y error is about one part in 1/1302, which happens at about x ≈ ±1.004 ≈ 32.48°.

Not bad. One part in 1302 is less than a pixel on a 1200 dpi printer if the sin wave is less than 2.17″ high (recall that, bottom-to-top, the whole wave is of height 2).

Optically, it is indistinguishable from a an actual sin wave, and given that setflat has a minimum value of 0.2 (PLRM3 p669), is plenty good enough. For PostScript, this is my recommendation.


We can do better if a single curve can be only 90°. With the ends in the correct place with the correct slopes, and having lost the symmetry of the previous arrangement, there are now two free parameters — which can be thought of as the speeds of departure from the ends.

x0 = y0 = 0
x1 = y1 = z1
x3 = HalfPi
x2 = x2 - z2
y2 = y3 = 1

Next, some Mathematica:

Quiet[Remove["Global`*"], {Remove::rmnsm}]; Print["Mathematica $Version = “", $Version, "”"]; Print["Execution time = ", DateString[DateList[], {"Hour", ":", "Minute", " on ", "DayNameShort", " ", "Day", " ", "MonthNameShort", " ", "Year"}]];

KnotsFromCoeffs[{c0_, c1_, c2_, c3_}] = {c0, c0 + c1/3, c0 + (2 c1 + c2)/3, c0 + c1 + c2 + c3};
CoeffsFromKnots[{z0_, z1_, z2_, z3_}] = {z0, -3 z0 + 3 z1, 3 z0 - 6 z1 + 3 z2, -z0 + 3 z1 - 3 z2 + z3};

{x0, x1, x2, x3} = {0, z1, Pi/2 - z2, Pi/2};
{y0, y1, y2, y3} = {0, z1, 1, 1};

zx = CoeffsFromKnots[{x0, x1, x2, x3}];
zy = CoeffsFromKnots[{y0, y1, y2, y3}];

Print[x = (zx[[1]] + t zx[[2]] + t t zx[[3]] + t t t zx[[4]]) // FullSimplify];
Print[y = (zy[[1]] + t zy[[2]] + t t zy[[3]] + t t t zy[[4]]) // FullSimplify];

errorTerm = ((Sin[x] - y)^2)*D[x, t]
Quiet[error = Hold[NIntegrate[errorTerm, {t, 0.0, 1.0}]]];
fm = FindMinimum[error, {z1, 0.512}, {z2, 0.568}, WorkingPrecision -> 24, AccuracyGoal -> 13]

1 / Sqrt[fm[[1]]/(Pi/2)]

{z1, z2} = {z1, z2} /. fm[[2]]; Plot[y - Sin[x], {t, 0, 1}]

That returns
z1 = 0.512065039021454886
z2 = 0.568537830614325103
The root-mean-square average error of the curve is 1/25326; the largest error is 1/15426.



But I think one part is 1302 is good enough for PostScript.

jdawi...@gmail.com

unread,
May 23, 2017, 4:57:04 PM5/23/17
to

jdawi...@gmail.com

unread,
May 28, 2017, 5:26:07 AM5/28/17
to
Just so they don’t get lost, the reader is pointed to conversation and files in
http://groups.google.com/forum/#!topic/comp.lang.postscript/MqpdDheJQBo
0 new messages