强基初中数学&学Python——第四十五课 海龟版单调函数轨迹模块发布

'''初中平面直角坐标系中单调函数轨迹、画直线和打点

 

@作者  码老师

@微信公众号  学思营 IDxuesying

@公司  深圳五行星软件有限公司

@日期  2020-12-19

 

@使用方法

1、导入海龟画图并初始化窗口和画布,例如:

    import turtle as t

    t.setup(600,600)

    t.screensize(400,400)

    t.shape("turtle")

    

2、导入本模块:

    from rcs import *

 

3、用下面函数:

    build(aTurtle, xUnt = None, yUnt = None, wc = None):

    @aTurtle 一个海龟

    @xUnt  x轴数值与像素对应元组

    @yUnt  y轴数值与像素对应元组

    @wc 自定义坐标元组(llx, lly, urx, ury)

        @llx -- 一个数值, 画布左下角的 x-坐标(像素)

        @lly -- 一个数值, 画布左下角的 y-坐标

        @urx -- 一个数值, 画面右上角的 x-坐标

        @ury -- 一个数值, 画布右上角的 y-坐标

    画平面直角坐标系,例如:

    build(t) #, worldcoordinates=(0,0,400,400))

 

4、使用模块进行画单调函数轨迹、直线和打点  

1)画函数轨迹

第一步:定义一个单调函数例如:

        def fun(x):

            return 5*x + 3

第二步:获取x,y的取值范围:

        minx, maxx, miny, maxy = getRange()

第三步(可选):根据x,y的取值范围和函数的实际要求,确定自变量取值范围:

       x1,x2

第四步:调用下面函数:

        trace(aTurtle, x1, x2, fun):

        @aTurtle 一个海龟

        @x1 自变量一端点值

        @x2 自变量另一端点值

        @fun 单调函数

        画函数轨迹,例如:

    #一元一次函数

    def fun(x):

        return 2*x + 1

    trace(t, 50, -50, fun)        

 

2)画直线段

第一步:获取x,y的取值范围:

        minx, maxx, miny, maxy = getRange()

第二步:调用下面函数:

        line(aTurtle, x1, y1, x2, y2):

        @aTurtle 一个海龟

        @x1,y1 始点

        @x2,y2 终点

        画直线段,例如画对角线:

        line(t, minx, miny, maxx, maxy)

 

3)打圆点

第一步:获取x,y的取值范围:

        minx, maxx, miny, maxy = getRange()

第二步:调用下面函数:

        circularDot(aTurtle, x, y, size=None, *color):

        @aTurtle 一个海龟

        @x,y 坐标

        @size 圆点大小

        @color 圆点颜色

        打圆点,例如下降对角线打点:

    x, y = minx, maxy

    size = 1

    while True:

        if x > maxx or y < miny:

            break

        circularDot(t, x, y, size, "red")

        x += 1

        y -= 1

        size += 1

 

'''

 

#导入分数

from fractions import Fraction

 

#坐标轴线宽

axisWidth = 1

#坐标轴和坐标点颜色

axisColor = "black"

#坐标箭头

axisTurtle = "classic"

#箭头大小 原来四分之一 轮廓线1

axisShapesize = (0.8, 0.8, 1)

#数轴单位(整数)及其对应的像素大小(整数) 1对应10像素

xUnit = [1, 20]

yUnit = [1, 20]

#方格线宽

squareWidth = 1

#方格线色 浅蓝

squareColor = "#EFEFFF"

#数点线长

markLen = 5

#数字字体

markFont=("Arial", 8, "normal")

#坐标系,调用build后才有值

worldcoordinates = None

 

 

#检测数轴单位

def __checkUnit(unt):

    if unt != None and len(unt) == 2:

        return isinstance(unt[0],int) and isinstance(unt[0],int)

    return False

 

 

 

#建立直角坐标系

def build(aTurtle, xUnt = None, yUnt = None, wc = None):

    '''按初中课本使用的直角坐标系绘图

    @aTurtle 一个海龟

    @xUnt  x轴数值与像素对应元组

    @yUnt  y轴数值与像素对应元组

    @wc 自定义坐标元组(llx, lly, urx, ury)

        @llx -- 一个数值, 画布左下角的 x-坐标(像素)

        @lly -- 一个数值, 画布左下角的 y-坐标

        @urx -- 一个数值, 画面右上角的 x-坐标

        @ury -- 一个数值, 画布右上角的 y-坐标'''

    #保持海龟原来的参数后设置参数

    speed = aTurtle.speed()

    aTurtle.speed(0)

    width = aTurtle.pensize()

    pencolor = aTurtle.pencolor()

    shape = aTurtle.shape()

    shapesize = aTurtle.shapesize()

    isdown = aTurtle.isdown()

    pos = aTurtle.pos()

    heading =aTurtle.heading()

 

    #数量与像素的对应关系

    if __checkUnit(xUnt):

        xUnit[0] = xUnt[0]

        xUnit[1] = xUnt[1]

    if __checkUnit(yUnt):

        yUnit[0] = yUnt[0]

        yUnit[1] = yUnt[1]

 

    global worldcoordinates

 

    #自定义坐标

    if wc != None and len(wc) == 4:

        worldcoordinates = wc

        aTurtle.setworldcoordinates(*worldcoordinates) #自定义坐标系

    else:

        temp = aTurtle.screensize()

        worldcoordinates = (-temp[0]/2,-temp[1]/2,temp[0]/2,temp[1]/2)

 

    #定坐标方向

    xh = worldcoordinates[0] < worldcoordinates[2]

    if xh:  #True从左到右

        x1,x2 = int(worldcoordinates[0]), int(worldcoordinates[2])

    else:

        x1,x2 = int(worldcoordinates[2]), int(worldcoordinates[0])

    yh = worldcoordinates[1] < worldcoordinates[3]

    if yh:  #True从下到上

        y1,y2 = int(worldcoordinates[1]), int(worldcoordinates[3])

    else:

        y1,y2 = int(worldcoordinates[3]), int(worldcoordinates[1])

 

 

    #画方格线

    #画与y轴平行的线

    if isdown:

        aTurtle.up()

    for i in range(-xUnit[1], x1, -xUnit[1]):

        aTurtle.pencolor(squareColor)

        aTurtle.pensize(squareWidth)

        aTurtle.setpos(i,y1)

        aTurtle.down()

        aTurtle.sety(y2)

        aTurtle.up()

        aTurtle.sety(0)

        aTurtle.down()        

        aTurtle.pencolor(axisColor)

        aTurtle.pensize(axisWidth)

        aTurtle.sety(markLen)

        aTurtle.up()

        if yh:   #x数字

            aTurtle.sety(-markLen-markFont[1])  #还要下降字体高度

        else:

            aTurtle.sety(-markLen//2)           #无需下降字体高度

        aTurtle.write(i//xUnit[1]*xUnit[0], align="center", font = markFont)        

    for i in range(xUnit[1], x2, xUnit[1]):

        aTurtle.pencolor(squareColor)

        aTurtle.pensize(squareWidth)

        aTurtle.setpos(i,y1)

        aTurtle.down()

        aTurtle.sety(y2)

        aTurtle.up()

        aTurtle.sety(0)

        aTurtle.down()        

        aTurtle.pencolor(axisColor)

        aTurtle.pensize(axisWidth)

        aTurtle.sety(markLen)

        aTurtle.up()

        if yh:

            aTurtle.sety(-markLen-markFont[1])

        else:

            aTurtle.sety(-markLen//2)

        aTurtle.write(i//xUnit[1]*xUnit[0], align="center", font = markFont)

    #画与x轴平行的线

    for i in range(-yUnit[1], y1, -yUnit[1]):

        aTurtle.pencolor(squareColor)

        aTurtle.pensize(squareWidth)

        aTurtle.setpos(x1, i)

        aTurtle.down()

        aTurtle.setx(x2)

        aTurtle.up()

        aTurtle.setx(0)

        aTurtle.down()        

        aTurtle.pencolor(axisColor)

        aTurtle.pensize(axisWidth)

        aTurtle.setx(markLen)

        aTurtle.up()

        if yh:  #y数字

            aTurtle.setpos(-markLen//2, i-markFont[1]//2) #下降字体高度一半

        else:

            aTurtle.setpos(-markLen//2, i+markFont[1]//2)

        if xh:  

            aTurtle.write(i//yUnit[1]*yUnit[0], align="right", font = markFont)

        else:

            aTurtle.write(i//yUnit[1]*yUnit[0], font = markFont)            

    for i in range(yUnit[1], y2, yUnit[1]):

        aTurtle.pencolor(squareColor)

        aTurtle.pensize(squareWidth)

        aTurtle.setpos(x1, i)

        aTurtle.down()

        aTurtle.setx(x2)

        aTurtle.up()

        aTurtle.setx(0)

        aTurtle.down()        

        aTurtle.pencolor(axisColor)

        aTurtle.pensize(axisWidth)

        aTurtle.setx(markLen)

        aTurtle.up()

        if yh:

            aTurtle.setpos(-markLen//2, i-markFont[1]//2)

        else:

            aTurtle.setpos(-markLen//2, i+markFont[1]//2)

        if xh:

            aTurtle.write(i//yUnit[1]*yUnit[0], align="right", font = markFont)

        else:

            aTurtle.write(i//yUnit[1]*yUnit[0], font = markFont)

 

    #画坐标

    aTurtle.pencolor(axisColor)

    aTurtle.pensize(axisWidth)

    aTurtle.shape(axisTurtle)

    aTurtle.shapesize(*axisShapesize)

    aTurtle.up()

    aTurtle.setpos(x1, 0)

    aTurtle.down()

    aTurtle.setx(x2)

    if xh:

        aTurtle.seth(0)

    else:

        aTurtle.seth(180)

    aTurtle.stamp()

    aTurtle.up()

    if yh:

        aTurtle.sety(-markLen-markFont[1])

    else:

        aTurtle.sety(-markLen//2)

    aTurtle.write('x', align="center", font = markFont)

    aTurtle.setpos(0, y1)

    aTurtle.down()

    aTurtle.sety(y2)

    if yh:

        if xh:

            aTurtle.seth(90)

        else:

            aTurtle.seth(-90)

    else:

        if xh:

            aTurtle.seth(90)

        else:

            aTurtle.seth(-90)

    aTurtle.stamp()

    aTurtle.up()

    if yh:

        aTurtle.setpos(-markLen//2, y2-markFont[1])

    else:

        aTurtle.setx(-markLen//2)

    if xh:

        aTurtle.write('y', align="right", font = markFont)

    else:

        aTurtle.write('y', font = markFont)

 

    #标注原点

    if yh:

        aTurtle.setpos(-markLen//2, -markLen-markFont[1])

    else:

        aTurtle.setpos(-markLen//2, -markLen//2)

    if xh:

        aTurtle.write(0, align="right", font = markFont)

    else:

        aTurtle.write(0, font = markFont)

        

 

    #恢复海龟原来的设置参数

    aTurtle.speed(speed)

    aTurtle.shape(shape)

    aTurtle.pensize(width)

    aTurtle.pencolor(pencolor)

    aTurtle.shapesize(*shapesize)

    aTurtle.setpos(*pos)

    aTurtle.seth(heading)

    if isdown:

        aTurtle.down()

 

#检查是否已经初始化

def __checkBuild():

    if worldcoordinates == None:

        print("还没有画坐标,请调用build(aTurtle, xUnt = None, yUnt = None, worldcoordinates = None)函数画坐标")

        return False

    else:

        return True

 

#屏幕上进1像素实际的值之间的转换

def __pixchange():

    #屏幕上进1像素实际的值之间的转换

    dx, udx, dy, udy = Fraction(*xUnit), Fraction(xUnit[1], xUnit[0]), Fraction(*yUnit), Fraction(yUnit[1], yUnit[0])

    #x坐标作图范围

    if worldcoordinates[0] < worldcoordinates[2]:

        minx, maxx = dx * worldcoordinates[0], dx * worldcoordinates[2]

    else:

        minx, maxx = dx * worldcoordinates[2], dx * worldcoordinates[0]

    #y坐标作图范围

    if worldcoordinates[1] < worldcoordinates[3]:

        miny, maxy = dy * worldcoordinates[1], dy * worldcoordinates[3]

    else:

        miny, maxy = dy * worldcoordinates[3], dy * worldcoordinates[1]

    return dx, udx, dy, udy, minx, maxx, miny, maxy

 

#函数轨迹

def trace(aTurtle, x1, x2, fun):

    '''函数轨迹

    @aTurtle 一个海龟

    @x1 自变量一端点值

    @x2 自变量另一端点值

    @fun 单调函数'''

 

    #自变量检查

    if x1 == x2:

        print("自变量不可以相等")

        return False        

 

    #检查是否已经画坐标

    if not __checkBuild():        

        return False

    

    #保持海龟落笔状态

    isdown = aTurtle.isdown()

    speed = aTurtle.speed()

    aTurtle.speed(0)

    pos = aTurtle.pos()

    if isdown:

        aTurtle.up()

    

    if x1 > x2:    #交换自变量

        temp = x1

        x1 = x2

        x2 = temp

 

    #屏幕上进1像素实际的值之间的转换

    dx, udx, dy, udy, minx, maxx, miny, maxy  =  __pixchange()

 

 

    if x2 <= minx or x1 >= maxx:

        print("自变量不在%s%s范围内!" % (str(minx), str(maxx)))

        return False

    if x1 < minx:

        x1 = minx

    if x2 > maxx:

        x2 = maxx

        

 

    #起点

    x = x1

    y = fun(x1)

    if y >= miny and y<= maxy:

        aTurtle.setpos(udx*x, udy*y)

        aTurtle.down()

        

 

    #描图

    while True:

        x += dx

        if x > x2: #过了终点

            break

        y = fun(x)

        if y >= miny and y<= maxy:

            aTurtle.setpos(udx*x, udy*y)

            if not aTurtle.isdown():

                aTurtle.down()

        else:

            if aTurtle.isdown():

               aTurtle.setpos(udx*x, udy*y) 

            aTurtle.up()

 

    if aTurtle.isdown():

        aTurtle.up()

    aTurtle.setpos(*pos)

    aTurtle.speed(speed)

    #恢复下笔状态 

    if isdown:

        aTurtle.down()

 

    return True

 

#作图范围

def getRange():

    '''作图范围

    @return x坐标最小值,x坐标最大值,y坐标最小值,y坐标最大值'''

    

    #检查是否已经画坐标

    if not __checkBuild():        

        return None    

 

    #屏幕上进1像素实际的值之间的转换

    dx, udx, dy, udy, minx, maxx, miny, maxy  =  __pixchange()

    return minx, maxx, miny, maxy

 

#作图范围字符串

def __rangestr(minx, maxx, miny, maxy):

    return "%s≤x≤%s%s≤y≤%s" % (str(minx),str(maxx),str(miny),str(maxy))

    

#画线段

def line(aTurtle, x1, y1, x2, y2):

    '''画线段

    @aTurtle 一个海龟

    @x1,y1 始点

    @x2,y2 终点'''

 

    #检查是否已经画坐标

    if not __checkBuild():        

        return False    

 

    #屏幕上进1像素实际的值之间的转换

    dx, udx, dy, udy, minx, maxx, miny, maxy  =  __pixchange()

 

    if min(x1, x2) < minx or max(x1, x2) > maxx or min(y1, y2) < miny or max(y1, y2) > maxy:

        print("有端点不在作图范围(%s)内!" % __rangestr(minx, maxx, miny, maxy))

        return False   

 

    #保持海龟落笔状态

    isdown = aTurtle.isdown()

    pos = aTurtle.pos()

    speed = aTurtle.speed()

    aTurtle.speed(0)

    if isdown:

        aTurtle.up()

 

    aTurtle.setpos(udx*x1, udy*y1)

    aTurtle.down()

    aTurtle.setpos(udx*x2, udy*y2)

    aTurtle.up()

    aTurtle.setpos(*pos)

    aTurtle.speed(speed)

    if isdown:

        aTurtle.down()

    return True

 

#打圆点

def circularDot(aTurtle, x, y, size=None, *color):

    '''打点

    @aTurtle 一个海龟

    @x,y  坐标

    @size 圆点大小

    @color 圆点颜色'''

 

    #检查是否已经画坐标

    if not __checkBuild():        

        return False    

 

    #屏幕上进1像素实际的值之间的转换

    dx, udx, dy, udy, minx, maxx, miny, maxy  =  __pixchange()

 

    if x< minx or x > maxx or y < miny or y > maxy:

        print("该点点不在作图范围(%s)内!" % __rangestr(minx, maxx, miny, maxy))

        return False

    

    isdown = aTurtle.isdown()

    pos = aTurtle.pos()

    speed = aTurtle.speed()

    aTurtle.speed(0)

    if isdown:

        aTurtle.up()

    aTurtle.setpos(udx*x, udy*y)

    if color == None:

        aTurtle.dot(size)

    else:

        aTurtle.dot(size, *color)

 

    aTurtle.setpos(*pos)

    aTurtle.speed(speed)

    if isdown:

        aTurtle.down()

    return True    

    

 

if __name__ == "__main__":

    import turtle as t

    t.setup(600,600)

    t.screensize(400,400)

    t.shape("turtle")

    build(t) #, worldcoordinates=(0,0,400,400))

    #一元一次函数

    def fun1(x):

        return 2*x + 1

    trace(t, 50, -50, fun1)

    #一元二次函数

    def fun2(x):

        return x**2 + 2*x + 2

    trace(t, 50, -50, fun2)

    #画圆

    def fun3(x):

        return (5**2 - x**2)**0.5

    trace(t, 5, -5, fun3)

    def fun4(x):

        return -(5**2 - x**2)**0.5

    trace(t, 5, -5, fun4)

    #画对角线

    minx, maxx, miny, maxy = getRange()

    line(t, minx, miny, maxx, maxy)

    #打点对角线

    x, y = minx, maxy

    size = 1

    while True:

        if x > maxx or y < miny:

            break

        circularDot(t, x, y, size, "red")

        x += 1

        y -= 1

        size += 1

        

    t.mainloop()