Drawing directions fields

后端 未结 3 1223
予麋鹿
予麋鹿 2021-02-04 06:23

Is there a way to draw direction fields in python?

My attempt is to modify http://www.compdigitec.com/labs/files/slopefields.py giving

#!/usr/bin/python
         


        
相关标签:
3条回答
  • 2021-02-04 06:24

    You can use this matplotlib code as a base. Modify it for your needs. I have updated the code to show same length arrows. The important option is to set the angles option of the quiver function, so that the arrows are correctly printed from (x,y) to (x+u,y+v) (instead of the default, which just takes into account of (u,v) when computing the angles).

    It is also possible to change the axis form "boxes" to "arrows". Let me know if you need that change and I could add it.

    import matplotlib.pyplot as plt
    from scipy.integrate import odeint
    import numpy as np
    
    fig = plt.figure()
    
    def vf(x, t):
        dx = np.zeros(2)
        dx[0] = 1.0
        dx[1] = x[0] ** 2 - x[0] - 2.0
        return dx
    
    
    # Solution curves
    t0 = 0.0
    tEnd = 10.0
    
    # Vector field
    X, Y = np.meshgrid(np.linspace(-5, 5, 20), np.linspace(-10, 10, 20))
    U = 1.0
    V = X ** 2 - X - 2
    # Normalize arrows
    N = np.sqrt(U ** 2 + V ** 2)
    U = U / N
    V = V / N
    plt.quiver(X, Y, U, V, angles="xy")
    
    t = np.linspace(t0, tEnd, 100)
    for y0 in np.linspace(-5.0, 0.0, 10):
        y_initial = [y0, -10.0]
        y = odeint(vf, y_initial, t)
        plt.plot(y[:, 0], y[:, 1], "-")
    
    plt.xlim([-5, 5])
    plt.ylim([-10, 10])
    plt.xlabel(r"$x$")
    plt.ylabel(r"$y$")
    

    0 讨论(0)
  • 2021-02-04 06:33

    I had a lot of fun making one of these as a hobby project using pygame. I plotted the slope at each pixel, using shades of blue for positive and shades of red for negative. Black is for undefined. This is dy/dx = log(sin(x/y)+cos(y/x)):

    You can zoom in & out - here is zoomed in on the middle upper part here:

    as above zoomed in

    and also click on a point to graph the line going through that point:

    and as it is such so also as such is it unto you

    It's just 440 lines of code, so here is the .zip of all the files. I guess I'll excerpt relevant bits here.

    The equation itself is input as a valid Python expression in a string, e.g. "log(sin(x/y)+cos(y/x))". This is then compiled. This function here graphs the color field, where self.func.eval() gives the dy/dx at the given point. The code is a bit complicated here because I made it render in stages - first 32x32 blocks, then 16x16, etc. - to make it snappier for the user.

    def graphcolorfield(self, sqsizes=[32,16,8,4,2,1]):
        su = ScreenUpdater(50)
        lastskip = self.xscreensize
        quitit = False
        for squaresize in sqsizes:
            xsquaresize = squaresize
            ysquaresize = squaresize
    
            if squaresize == 1:
                self.screen.lock()
            y = 0
            while y <= self.yscreensize:
                x = 0
                skiprow = y%lastskip == 0
                while x <= self.xscreensize:
                    if skiprow and x%lastskip==0:
                        x += squaresize
                        continue
    
                    color = (255,255,255)
                    try:
                        m = self.func.eval(*self.ct.untranscoord(x, y))
                        if m >= 0:
                            if m < 1:
                                c = 255 * m
                                color = (0, 0, c)
                            else:
                                #c = 255 - 255 * (1.0/m)
                                #color = (c, c, 255)
                                c = 255 - 255 * (1.0/m)
                                color = (c/2.0, c/2.0, 255)
    
                        else:
                            pm = -m
                            if pm < 1:
                                c = 255 * pm
                                color = (c, 0, 0)
                            else:
                                c = 255 - 255 * (1.0/pm)
                                color = (255, c/2.0, c/2.0)                        
                    except:
                        color = (0, 0, 0)
    
                    if squaresize > 1:
                        self.screen.fill(color, (x, y, squaresize, squaresize))
                    else:
                        self.screen.set_at((x, y), color)
    
                    if su.update():
                        quitit = True
                        break
    
                    x += xsquaresize
    
                if quitit:
                    break
    
                y += ysquaresize
    
            if squaresize == 1:
                self.screen.unlock()
            lastskip = squaresize
            if quitit:
                break
    

    This is the code which graphs a line through a point:

    def _grapheqhelp(self, sx, sy, stepsize, numsteps, color):
        x = sx
        y = sy
        i = 0
    
        pygame.draw.line(self.screen, color, (x, y), (x, y), 2)
        while i < numsteps:
            lastx = x
            lasty = y
    
            try:
                m = self.func.eval(x, y)
            except:
                return
    
            x += stepsize            
            y = y + m * stepsize
    
            screenx1, screeny1 = self.ct.transcoord(lastx, lasty)
            screenx2, screeny2 = self.ct.transcoord(x, y)
    
            #print "(%f, %f)-(%f, %f)" % (screenx1, screeny1, screenx2, screeny2)
    
            try:
                pygame.draw.line(self.screen, color,
                                 (screenx1, screeny1),
                                 (screenx2, screeny2), 2)
            except:
                return
    
            i += 1
    
        stx, sty = self.ct.transcoord(sx, sy)
        pygame.draw.circle(self.screen, color, (int(stx), int(sty)), 3, 0)
    

    And it runs backwards & forwards starting from that point:

    def graphequation(self, sx, sy, stepsize=.01, color=(255, 255, 127)):
        """Graph the differential equation, given the starting point sx and sy, for length
        length using stepsize stepsize."""
        numstepsf = (self.xrange[1] - sx) / stepsize
        numstepsb = (sx - self.xrange[0]) / stepsize
    
        self._grapheqhelp(sx, sy,  stepsize, numstepsf, color)
        self._grapheqhelp(sx, sy, -stepsize, numstepsb, color)
    

    I never got around to drawing actual lines because the pixel approach looked too cool.

    0 讨论(0)
  • 2021-02-04 06:48

    Try changing your values for the parameters to this:

    XSCL = .2
    YSCL = .2
    

    These parameters determine how many points are sampled on the axes.


    As per your comment, you'll need to also plot the functions for which the derivation dy_dx(x, y) applies.

    Currently, you're only calculating and plotting the slope lines as calculated by your function dy_dx(x,y). You'll need to find (in this case 3) functions to plot in addition to the slope.

    Start by defining a function:

    def f1_x(x):
        return x**3-x**2-2x;
    

    and then, in your loop, you'll have to also write the desired values for the functions into the fileobj file.

    0 讨论(0)
提交回复
热议问题