Home > Software design >  How to draw smooth contour/level curves of multivariable functions
How to draw smooth contour/level curves of multivariable functions

Time:11-15

G'day programmers and math enthusiasts.

Recently I have been exploring how CAS graphing calculators function; in particular, how they are able to draw level curves and hence contours for multivariable functions.

Just a couple of notes before I ask my question:

  • I am using Python's Pygame library purely for the window and graphics. Of course there are better options out there but I really wanted to keep my code as primitive as I am comfortable with, in an effort to learn the most.
  • Yes, yes. I know about matplotlib! God have I seen 100 different suggestions for using other supporting libraries. And while they are definitely stunning and robust tools, I am really trying to build up my knowledge from the foundations here so that one day I may even be able to grow and support libraries such as them.

My ultimate goal is to get plots looking as smooth as this: Mathematica Contour Plot Circle E.g.

What I currently do is:

  • Evaluate the function over a grid of 500x500 points equal to 0, with some error tolerance (mine is 0.01). This gives me a rough approximation of the level curve at f(x,y)=0.
  • Then I use a dodgy distance function to find each point's closest neighbour, and draw an anti-aliased line between the two.

The results of both of these steps can be seen here:

First Evaluating Valid Grid Points

Then Drawing Lines to Closest Points

For obvious reasons I've got gaps in the graph where the next closest point is always keeping the graph discontinuous. Alas! I thought of another janky work around. How about on top of finding the closest point, it actually looks for the next closest point that hasn't already been visited? This idea came close, but still doesn't really seem to be even close to efficient. Here are my results after implementing that:

Slightly Smarter Point Connecting

My question is, how is this sort of thing typically implemented in graphing calculators? Have I been going about this all wrong? Any ideas or suggestions would be greatly appreciated :)

(I haven't included any code, mainly because it's not super clear, and also not particularly relevant to the problem).

Also if anyone has some hardcore math answers to suggest, don't be afraid to suggest them, I've got a healthy background in coding and mathematics (especially numerical and computational methods) so here's me hoping I should be able to cope with them.

CodePudding user response:

so you are evaluating the equation for every x and y point on your plane. then you check if the result is < 0.01 and if so, you are drawing the point.

a better way to check if the point should be drawn is to check if one of the following is true:
(a) if the point is zero
(b) if the point is positive and has at least one negative neighbor
(c) if the point is negative and has at least one positive neighbor

there are 3 problems with this:

  • it doesn't support any kind of antialisasing so the result will not look as smooth as you would want
  • you can't make thicker lines (more then 1 pixel)
  • if the 0-point line is only touching (it's positive on both sides and not positive on one, negative on the other)

this second solution may fix those problems but it was made by me and not tested so it may or may not work:

you assign the value to a corner and then calculate the distance to the zero line for each point from it's corners. this is the algorithm for finding the distance:

def distance(tl, tr, bl, br):  # the 4 corners
   avg = abs((tl   tr   bl   br) / 4)  # getting the absolute average
   m = min(map(abs, (tl   tr   bl   br)))  # absolute minimum of points
   if min == 0:  # special case
        return float('inf')
   return avg / m  # distance to 0 point assuming the trend will continue

this returns the estimated distance to the 0 line you can now draw the pixel e.g. if you want a 5-pixel line, then if the result is <4 you draw the pixel full color, elif the pixel is <5 you draw the pixel with an opacity of distance - 4 (*255 if you are using pygames alpha option)

this solution assumes that the function is somewhat linear.

just try it, in the worst case it doesn't work...

CodePudding user response:

http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.30.1319&rep=rep1&type=pdf

This 21 Page doc has everything I need to draw the implicit curves accurately and smoothly. It even covers optimisation methods and supports bifurcation points for implicit functions. Would highly recommend to anyone with questions similar to my own above.

Thanks to everyone who had recommendations and clarifying questions, they all helped lead me to this resource.

  • Related