Segment approach to y = sin(ex) with JSXGraph

Most graph plotters have trouble with the graph of f(x) = sin(ex). The curve gets more tightly bunched together as x increases, and no matter what algorithm the plotter is using, there's always untidy spots where bits of the curve are missing, causing noticeable gaps in the curve, or there's unwanted 'wiggles' where the upper (y = 1) and lower (y = −1) values are not reached.

The reason for this is no matter what algorithm the grapher is using, the javascript (or whatever) engine bumps up against the limitation that numbers use double-precision 64-bit format IEEE 754-2008 values, which have a limit of 16 digits in the representation. It means even if you throw tens of thousands of data points at the plot, there's always going to be inaccuracies due to that 16 digit limit.

See, for example:

I said "for me" above, because experience shows things change if you resize the graph, or zoom in or out, or change the stroke width of the curve, or restrict the domain in another way.

Another factor in the performance of online plotters is the browser itself, and the way it interacts with the particular screen the viewer is using. Even zooming in the browser can give different results. There are compromises going on in every step.

JSXGraph is not immune from these issues either, as you'll see in the demo below.

Proposal - base the plot on the function zeroes

Even when plotting at the sub-pixel level (that is, providing data points that are closer together than 1 px on the screen), it's still inevitable that some important points are going to "miss" due to rounding. We could just add more data points and hope we catch all the key points on the graph, but the more data points used when plotting a graph results in longer processing time and a laggy experience, and won't give the result we seek.

So this proposal works by using the observation that after a certain point (around x = 3.5) the plot looks like a series of segments. Our approach here is to plot the ordinary curve for around x < 3.5, then approximate the rest of the curve (for x > 3.5) with segments.

Method

  1. The zeroes for the function f(x) = sin(ex) occur when ex = nπ, that is, when x = loge(nπ), n = 1, 2, 3 ....
  2. We calculate the x-values for the maxima (1) and minima (−1) slope between each of the points x = loge(π), loge(2π), loge(3π)... and draw segments through those maxima and minima.
  3. Since the stroke width of the curve makes a difference to where we can start our segments (a thin curve means the segments need to start at a higher x-value), we determine the point where the curve resembles segments.
  4. Then we plot the normal curve up to the point determined in (3.), then plot the segments from that point on.

We have 2 extra tricks to ensure everything looks good and is efficient.

  1. For the portion of the curve before the segments, we don't use the default JSXGraph plot, but enhance it by ensuring the max (1) and min (−1) values are plotted, and that there are sufficient data points near those extremities to ensure a smooth curve.
  2. At some point, the graph becomes a solid block of colour, so instead of plotting thousands of very tightly packed segments, we construct a simple polygon.

So there are actually 4 portions to the graph:

  1. Default JSXGraph, up to the second zero
  2. "Enhanced" curve
  3. Segments
  4. Polygon

You can see these sections differentiated by colour if you check the "Distinguish curve portions" box. You can see the length of each array used to make up the various portions, and the total number of data points.

Instructions

You can:

  1. Change the multiple of the exponent of e, e.g. plot y = sin(e2.5x) using the first slider
  2. Change the stroke width of the curve using the second slider.
  3. Vary the visible domain using the third "Restrict domain" slider.
  4. See the different portions of the graph by selecting the checkbox "Distinguish curve portions" and you'll see the parts of the curve in different colours.
  5. See the default JSXGraph rendering of the graph by selecting "Default JSXGraph"
  6. Choose either the f(x) = sin(ex) or f(x) = cos(ex) form of the graph.

q (mult of x)

Stroke width

Restrict domain

Segments Default JSXGraph

sin cos

Limitations

Of course, this technique is specific to this kind of function. That is, it will work for small variations to the function, and for closely related ones, like f(x) = cos(ex) as in the demo, but even so, it may be possible to incorporate the approach into a generic graph plotter.