Drawing directions fields

后端 未结 3 1228
予麋鹿
予麋鹿 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条回答
  •  猫巷女王i
    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.

提交回复
热议问题