Plotting an Equation Using JavaScript Canvas: y = log(29.565ln(x) - 4.4898)

59 views Asked by At

I'm attempting to plot the equation y = log(29.565ln(x) - 4.4898) on a JavaScript canvas. The challenge lies in determining the control points for a cubic Bezier curve that accurately represents this equation between specified starting and ending points. My starting x is 1.64003, starting y is -4.07205084, ending x is 4, and ending y is 1.562245182.

I've explored various approaches, including using cubic Bezier curves and attempting interpolation with Bernstein polynomials. However, these methods haven't produced accurate critical points or control points for the curve.

Additionally, I've considered the idea of finding the point of maximum curvature to split the curve into two quadratic Beziers, but I'm unable to devise a program that accurately determines the maxCurv point.

Here are two snippets showcasing my attempts:

I'm seeking guidance or alternative approaches to accurately plot this equation on a JavaScript canvas, ensuring that the plotted curve aligns closely with the function's behavior between the specified starting and ending points. Any insights, methods, or strategies to achieve this would be greatly appreciated. Thank you!

1

There are 1 answers

0
Mike 'Pomax' Kamermans On

Just plot your function by drawing a path on the canvas. No need to approximate anything:

// Get the graphics context and flip the coordinate
// system so that (0,0) is in the lower-left:
const ctx = graph.getContext(`2d`);
graph.width = graph.height = 400;
ctx.translate(0,graph.height);
ctx.scale(1,-1);

// JS's Math.log is the natural logaritm
const { log: ln, min, max } = Math;

// then again, we can make any log function
// using any other log, so log10 is:
const log = v => ln(v)/ln(10);

// Let's write a simple plot2D function so
// that we can easily plot anything we want:
function plot2D(f, start=0, end=1) {
  // we'll generate values along the entire canvas' width:
  const interval = end - start;
  const step = interval / graph.width;
  const points = [...new Array(graph.width)].map((_,x) => f(start + x*step));

  // To plot this in a way to "looks good" we'll want
  // to scale everything so that it fills the canvas:
  const minValue = min(...points);
  const maxValue = max(...points);
  const range = maxValue - minValue;
  const scaleY = graph.height / range;
  const yValues = points.map((value) => (value - minValue) * scaleY);
 
  // And then we just draw a path using moveTo and lineTo:
  ctx.beginPath();
  ctx.moveTo(0, yValues[0]);
  for(let x=1; x<yValues.length; x++) ctx.lineTo(x, yValues[x]);
  ctx.stroke();
}

// Then, your function:
const ourFunction = (x) => log(29.565 * ln(x) - 4.4898);

// And then we plot that:
plot2D(ourFunction, 1.64003, 4);
canvas {
  border: 1px solid black;
  background: #EEE;
}
<canvas id="graph" width"500px" height="500px"></canvas>

(I'll leave padding the canvas, adding and labeling the axes, and putting the right unit values along those axes for you to do)

Do note, though, that your claim that "My starting x is 1.64003, starting y is -4.07205084, ending x is 4, and ending y is 1.562245182" is incorrect: assuming that log is the base-10 logarithm function and ln is the base-e logarithm, those are not the y values you'll get for those x values. You'll start at y=1.0058852487205416 and end at y=1.5617255645804091.