# Define a mathematical function from a graph

#1

Suppose I have an arbitrary function that I’d like to import into Synfig for multiple purposes. As a real-world example, I’d like to work with the refractive index of water, graphed below:

(Graph shamelessly ripped from this webpage.)

I would love to do all sorts of things with this graph. As a simple example, it would be nice to have a single parameter representing a value on the horizontal axis that varies linearly while tracing out the corresponding values according to that parameter. In other words, I’d like a parameter that I could set to 0.5, corresponding to about 300 micrometers, that outputs a value of about 2, the corresponding index of refraction. Unfortunately, if I were to trace this curve and link a circle (or whatever) to the spline, an amount of 0.5 won’t in general correspond to the value at 300 micrometers because the spline length does not increase uniformly with the horizontal axis.

Any ideas on how to do this? I have a method that I think will work marvelously, except that it’s a little tedious to set up. I’ll be happy to share it if no one has any ideas. I’ll withhold it for now to make sure replies are unbiased.

#2

If i understand you correctly, you want a node with two parameters: spline and x coordinate, that returns corresponding y coordinate (and perhaps fails gracefully when are either too many or none corresponding y). If that is so, this isn’t possible (as far as i know; maybe it’s possible to build extremely complex node system) with stock synfig. However, i once made a node to that effect (except that it took time for x coordinate, but that’s a minor detail and can be changed easily), so if you’re willing to build synfig yourself, you can get that node. It’s available at github.com/caryoscelus/synfig-playground and also in some git branch.

And of course, i should note that it works with bezier paths (called splines in synfig) and not arbitrary functions, but i suppose you can convert your graph to bezier curve (e.g. using inkscape tracing feature).

#3

Here’s my tutorial on how to do this in Synfig. I’m open to any and all constructive criticism or other feedback. If you like it enough, I’d be honored to see it published on the wiki.

Suppose you wish to work in Synfig with numbers on a graph. It could be a graph that already exists and you simply trace over it or perhaps you wish to make your own graph simply because you don’t want to muck about with waypoints and would rather work directly with splines to produce a graph of, say, displacement versus time. Synfig appears to have no way of doing this directly, but with a little effort, you can produce animations with arbitrary functions underlying the process.

For simplicity, I’l work with this graph.

It has just three vertices, making this process especially easy. I’m not including x- and y-axes, but they could easily be incorporated.

Before we begin, let me try to illustrate exactly what we’re trying to accomplish. It will also motivate the later solution. I’ll draw a straight horizontal line directly beneath the existing graph, then make two identical circles and link the origin of one to each spline. Finally, I’ll export and connect their “Amount” parameters under “Origin” so that they move together. The result looks like this:

In the above image, the “Amount” for the two circle origins was set to zero, putting them both at the left end of their respective splines. No problem so far. Bad things happen, however, when we change the “Amount” parameter:

In the above graph, I’ve set “Amount” to 0.7. The top circle on our graph is lagging behind the circle that moves uniformly from left to right. This is a nuisance in the graph I’ve drawn but can be devastating in a graph with more vertical parts. We’d instead like these two circles to move together; when I pick a value on the x-axis, I should be able to read off the corresponding value on the y-axis (not some nearby value).

Optional step 0: Duplicate your graph. If you’d like an untainted copy of your graph, now’s your chance to make one. If you just want to use your graph to output values, this may not be necessary.

Step 1: Scale it down. Select one of the copies of your graph and the scale tool. Make sure “Lock Aspect Ratio” is unchecked because we only wish to scale it vertically. Select all handles and scale it down until it is nearly flat.

Step 2: Make a circle and link it to your flattened spline. (It doesn’t have to be a circle, it could be almost anything.) Select the circle tool, draw a circle, then select both the circle and flattened graph layers. Click the circle’s origin handle, then right-click the spline and select “Link to spline”.

Step 3: Export the “Amount” parameter. Select only the circle’s layer and open up the “Origin” parameter. Right-click “Amount” and click “Export value”. Give it a descriptive name like “Graph position x”. Because the “Amount” parameter is based on spline length and the graph has been flattened, the parameter now varies almost linearly with the horizontal position. For full disclosure, there will still be a minor discrepancy between the amount and the horizontal position on the graph and there will be a loss of precision, but since Synfig uses double-precision values, these issues will be almost inconsequential.

Step 4: Insert a text box. Like the circle we drew earlier, this could actually be anything with a real parameter associated with it (so… anything). I’ll work with a text box so we can view the output. Convert the “Text” parameter to “Real string”, then convert the “Real” parameter within to “Vector Y”. Export your circle’s origin parameter then connect it to the “Vector” input to “Vector Y”. Optionally, you can create a second text box corresponding to the horizontal position, although I suggest working with the “Amount” parameter instead of connecting text to the circle’s origin. Export the “Real” parameter as the y value.

At this point, we’re basically done. You now have control over the “Amount” parameter and in response, the y value you exported varies correspondingly. To illustrate that this works properly, I’ll create a circle that traces over our original graph.

Step 5: Make a new circle. Convert its “Origin” parameter to “Composite”. Convert both its “X-Axis” and “Y-Axis” parameters to “Add” and then connect the “Link” parameter under “X-Axis” to the “Amount” you exported earlier and connect the “Link” parameter under “Y-Axis” to the “y value” you exported earlier. At this point, the circle follows the motion of the other circle linked directly to the spline, but it follows a much smaller path. We’ll need to scale it up. Adjust the “Scalar” and “Addition” parameters for both your “X-Axis” and “Y-Axis” (I suggest working with “Scalar” first). This requires a lot of guessing and checking, but you should eventually get an acceptable result. Alternatively, you should be able to deduce what values are required based on the extrema and the affine transformation from one set of coordinates to another. I just haven’t bothered to do it.

Here’s a screenshot of the finished product:

Note that the circle on the flattened spline is almost directly above the circle following the original spline. There’s some discrepancy on the left end, but that’s because I accidentally scaled the spline horizontally a bit. It really does follow the curve quite nicely.

As a more impressive example, here’s what I’ve come up with for a real-world project, involving the index of refraction of water:

The middle circle follows the flattened graph down the middle (its layer is hidden). Note that the two circles are perfectly aligned vertically.

I’ve attached the file to this post so you can see how everything is linked up. One nifty feature about this file is that all of the vertices and tangents of the original graph are linked to the vertices and tangents of the flattened graph. This means you can alter the graph to your liking, provided it has no more than 32 vertices. (You can do more than 32 vertices, you’ll just have to link them up as I did.) I also computed the coefficients for the x- and y-coordinates of the dot that follows the original graph and these values should be within the precision of the canvas. I’ll be happy to share that procedure for anyone who’s interested.

#4

I’m still not sure what exactly you’re trying to achieve, but if it’s simply sharing x coordinate, then it can be achieved much simpler. See attached file. The only downside of this approach is that you can’t move circle on line directly, but it will move along when circle on curve moves.
test-59-line-and-curve.sif (17.4 KB)

#5

I’m trying to come up with a protocol entirely within Synfig in which the user inputs an x value and a corresponding y value is output. As a simple example, Synfig does not at this time support the inverse cosine function. The user could try to approximate it with a Taylor series or some kind of fancy interpolation or whatever. Alternatively, using my approach, they can just draw an inverse cosine graph and, with the appropriate linkages, use that to calculate the values. This generalizes to arbitrary functions, so the user has complete control over the output.

The fact that the circles align horizontally is just a consequence of the fact that this procedure works. In the file you attached, you cannot precisely control the horizontal position of the bottom circle with user input without using a sort of guess-and-check procedure.

#6

Ok, now i quite understand what you want and how your clever hack works and that it is in fact lacks the obvious disadvantage of my approach and solves the broader problem of extracting (approximate) Y for any valid X. It’s just that “parallel” movement isn’t best to illustrate that since it can be achieved with other simpler methods.

Anyway, i have only one thing to add. If you don’t care about graph appearing a bit weird (or you don’t need to display actual graph at all), you can avoid manual linking of two paths and use one path and stretch layer instead.
test-60-hack-and-stretch.sif (26.6 KB)

#7

This is exactly the kind of useful tip I was hoping to get from sharing my idea here.

For others who might be reading, if I understand this correctly, a stretch layer affects the positions and angles associated with vertices and tangents. I had previously thought that it was a sort of graphical post-processing step that reinterprets existing vertices and tangents without altering them. I’ve fiddled around with the file you attached and although I’m having a hard time describing exactly what the stretch layer does, I see that it works like a charm. I’ve attached a new version of the file to this post that has one curvy part and one straight part to the path and a “tracking” circle that follows a horizontal spline at the bottom, illustrating that the stretch layer works as needed.

This will indeed save a lot of time constructing the arbitrary functions the user desires.
test-60-hack-and-stretch.sif (37.1 KB)

#8

Basically what it does is apply transformation to coordinate system, which then gets applied both to render result (in a form of stretching the image: as you can notice, curve line doesn’t have constant width anymore) and editor (i.e. vertex handles are displayed and can be edited at correct positions). It does not really alter vertices or tangents, it simply lets you edit them at convenient position.

#9

I have an opposite problem. I need to transform, you understand to align, and to synchronize x-axis movement on track with x-axis displacement (flat). X-coordinate of element position on track is depend on x-coordinate of element displacement on x-axis. At the end i would like to synchonize x-axis coordinates of element movement on two different tracks or graphs. Any idea to solve in a simplest way?