March 25, 2018

Tangency - An Experiment

How does Tangency work? I aim to address that question in this post.

Tangency was built using p5.js, with the end goal of creating a graphical plotting system and making it interactive using some differential calculus principles. There's a link to the demo at the bottom of this post.


The first step was to create a function plotting solution. The simplest way I could think of was to have a for loop go over the x-values ranging from 0 to the canvas width, and feed them into the mathematical function provided to get the y-values. Then, displaying the points using the individual coordinates as a series of connected vertices (effectively, a vertex). Here is the plotting function in all its glory:

Let me break it down.
plot is part of an object called "graph" (it contains other methods such as trace and tangent, which we'll get to later). The plot property of "graph" consists of an anonymous function which takes func as a parameter. func is a global function which returns the mathematical equation to be graphed (it takes x as a parameter) but again, we'll get to that later.
For consistency with the coordinate plane, we must translate the origin to the center from the left corner of the canvas. Next, we need to establish a scale for the y & x axis based on the canvas dimensions. scale is a global integer variable which does what you think it does, act as a scale for the graph. For instance, the visible domain of the function will be -scale ≤ x ≤ scale.
Subsequently, we have beginShape() which begins to record vertices for a custom shape. When used in conjunction with endShape(), it allows for the creation of more complex forms; forms such as the graphs of mathematical functions.
There are two possible values for x and y: the pixel value and the numerical value, the former is used for drawing the graph and the latter for graph-related calculations (such as the derivative at that value).
This leads us to the for loop, the main part of this function. We initialize the x value from the leftmost of the canvas (this only renders the function within the limits of the canvas). Inside, we declare two variables. xVal holds the numerical value of x and is used to calculate the y-value, which is immediately transformed to its respective pixel value since we have no use for the numerical y.
xCoords is a predefined global array which we use to hold the x-values so we don't have to loop over them again (explained later).
The next three lines are used to define the styling of the curve. Finally, we plot the curve to the canvas using the x-values from the for loop and the calculated y-values.

Differentiating, visually

Let's get right into it.

Getting the numerical x-value of the function outside our initial for loop seems tricky. Luckily, we pushed all our x-values into an array, xCoords. All we have to do is somehow link that array to the x coordinate of the cursor. To make things easy, we use mouseX, a built-in p5 variable which contains the horizontal position of the mouse. Also, xCoords can be thought of as a range of the visible x-values. Using this information, we map the mouseX coordinates to the visible x-value range (mouseX is initially from 0 to the canvas width). Now, our mouse's horizontal position corresponds to the pixel value of x on the canvas. yPos and xVal perform similarly as they do in the plotting function (with some scaling modifications). As an aside, very similar functionality is used in the trace function of Tangency; all we do is draw an ellipse at xPos and yPos.
Next comes the fun part.
We have to calculate the slope of the tangent line at the given mouse position, xPos. Well, we know that that the slope value is the derivative of the function computed at that x-value. The derivative of a function f(x) at x = a is the limit of the slope of the secant line from x = a to x = a + h as h approaches 0 (comprehensive definition here). This produces the equation in the following code (which defines the slope() function used above):

Instead of calculating the limit as h approaches 0, we can set h to a small number close to 0 (such as 1e-9 or 0.000000001). Essentially, this gives us a good approximation of the slope value which we store in m. We use the slope-intercept equation form to graph the tangent line: y = mx + b. All we're missing is the y-intercept and to find it, we just rearrange the equation to solve for b.
Finally, we get to the tangent line. Instead of plotting the entire line each frame, we can calculate two points which are the same distance apart from xPos on either side of it. diff holds that distance, 70 pixels in this case. We name these two points y1 & y2. To solve for these y points, we have to plug in the slope, xPos ± diff, and the y-intercept. And then, all that's left is to draw this line to the canvas and apply some styling to it.


Obviously, this is not the entire code for Tangency. It's merely the important parts which I felt needed explanation. The full source code can be found at this link. If anything is unclear or you require further explanation, email me and I'll be happy to help.

A fully-working demo can be found here.