I'm working on a project where I need to plot graphs of mathematical functions in JavaScript, specifically functions that exhibit asymptotic behavior or approach infinity at certain points, such as tan(x), ln(x), and log(x). I'm struggling with how to accurately represent these functions, especially near the points where they approach infinity (for example, near vertical asymptotes for tan(x)).
This is my Code to draw functions:
export function drawFunction(latexStr) {
let isTan;
// Draw function
console.log(canvas.height);
console.log(canvas.width);
let prevY; // Stores the previous Y value
let startY = true; // To know when a new line is needed
ctx.beginPath();
ctx.strokeStyle = "blue";
ctx.lineWidth = 2;
// Draw the function in small steps
for (let x = -canvas.width / 2; x <= canvas.width / 2; x += stepSize) {
let y = parser(latexStr, x)[0]; // f(x)
isTan = parser(latexStr, x)[1];
console.log(isTan);
// Skip drawing the graph if it's infinite or too large for the canvas
if (Math.abs(y) > canvas.height / scaleFactor && !isTan) {
startY = true;
y = (y < 0 ? -1 : 1) * (canvas.height / scaleFactor - 1);
}
const adjustedX = canvas.width / 2 + x * scaleFactor; // Calculation of the small X line
const adjustedY = canvas.height / 2 - y * scaleFactor; // Calculation of the small Y line
/* if (Math.round(x * 100) / 100 === stepSize)
ctx.moveTo(adjustedX, adjustedY); */
// Draw or move to position
if (startY) {
ctx.moveTo(adjustedX, adjustedY);
startY = false;
} else {
if (prevY !== undefined && Math.abs(y - prevY) > 1 * scaleFactor) {
if (isTan) {
ctx.stroke(); // Draw the current line
ctx.beginPath(); // Start a new line
ctx.moveTo(adjustedX, y < 0 ? canvas.height : 0); // Move to the edge of the canvas
} else {
ctx.moveTo(adjustedX, adjustedY); // Move to the new starting position
}
} else {
ctx.lineTo(adjustedX, adjustedY);
}
}
prevY = y;
//console.log(Math.round(x * 100) / 100);
}
ctx.stroke(); // Draw the function
}
Drawing a normal function like "y = x+2" is no problem, but issues arise with a function like "y = tan(x)" as seen in the image below:
The values are correct, so it seems not to be an issue with the calculations but rather with the way the drawing is handled. I've searched everywhere but couldn't find a solution. Does anyone have suggestions on how I could improve the drawing algorithm to better handle infinity points for functions like tan(x)?
Thank you in advance for your help!
Your problem is not quite clear, and you have not provided full code ...
From your image I'm going to assume that what you are not happy with is some lines not making it all the way up to the edge of the canvas, like image below:
Here is some simple code reproducing your problem:
var canvas = document.getElementById('plotCanvas');
var ctx = canvas.getContext('2d');
var startX = -20;
var endX = 20;
var scaleFactor = 25;
ctx.beginPath();
for (var x = startX + 0.01; x <= endX; x += 0.01) {
xP = (x - startX) * (canvas.width / (endX - startX));
yP = canvas.height / 2 - Math.tan(x) * scaleFactor
if (xP > 0 && yP > 0 && xP < canvas.width && yP < canvas.height) {
ctx.lineTo(xP, yP);
} else {
ctx.moveTo(xP, yP)
}
}
ctx.strokeStyle = 'blue';
ctx.stroke();
canvas {
border: 1px solid black;
}
<canvas id="plotCanvas" width="400" height="300"></canvas>
We have a hard condition to only draw inside the canvas:
if (xP > 0 && yP > 0 && xP < canvas.width && yP < canvas.height) {
that is good to optimize what we draw and not waste time drawing things that are not visible, but...
The condition for the boundaries needs to be a bit more flexible, an easi fix is to let it draw a few lines outside the canvas boundaries, instead of stopping at 0 like I had, I'm letting it go to:
-canvas.width
that is all
var canvas = document.getElementById('plotCanvas');
var ctx = canvas.getContext('2d');
var startX = -20;
var endX = 20;
var scaleFactor = 25;
ctx.beginPath();
for (var x = startX + 0.01; x <= endX; x += 0.01) {
xP = (x - startX) * (canvas.width / (endX - startX));
yP = canvas.height / 2 - Math.tan(x) * scaleFactor
if (xP > -canvas.width && yP > -canvas.height && xP < canvas.width && yP < canvas.height) {
ctx.lineTo(xP, yP);
} else {
ctx.moveTo(xP, yP)
}
}
ctx.strokeStyle = 'blue';
ctx.stroke();
canvas {
border: 1px solid black;
}
<canvas id="plotCanvas" width="400" height="300"></canvas>