凸透镜成像规律【2】

发布时间:2024年01月21日

在这里插入图片描述


一. 前言

去年笔者发布了一篇关于使用turtle库来展示凸透镜成像规律的作品。现在发现程序有一些问题,今天就来改一下。

二. 问题

1. 文字

就是在点击时,文字 2 f 2f 2f(有时 f f f也会有)会莫名其妙的出现在轴心附近。
在这里插入图片描述

2. 线段

与文字差不多,它有时候点击会把一些线段加粗。
在这里插入图片描述

三. 解决办法及完整代码

代码是没有问题的,所以笔者放弃turtle,转而使用Pygame来做。

修改后的代码如下:

import sys
import pygame

width, height = 1000, 600  # 屏幕的宽高
pygame.init()  # Pygame初始化
screen = pygame.display.set_mode((width, height))  # 初始化屏幕
pygame.display.set_caption('凸透镜成像规律')  # 设置标题


# 绘制线段的函数
def line(x1, y1, x2, y2, color=(0, 0, 0)):  # 颜色color默认为黑色
    # 这里出现"width // 2 +"与"height // 2 -"是为了与turtle使用的坐标轴一直,下同
    pygame.draw.line(screen, color, (width // 2 + x1, height // 2 - y1), (width // 2 + x2, height // 2 - y2), 3)


# 写出字体
def text(txt, x, y):
    f = pygame.font.Font('C://Windows//Fonts//ARIALN.TTF', 20)  # 这里选用ARIALN字体
    text = f.render(txt, True, (0, 0, 0))
    t = text.get_rect()
    t.center = (width // 2 + x, height // 2 - y)
    screen.blit(text, t)


# 绘制出实心圆
def dot(x, y, size):
    pygame.draw.circle(screen, (0, 0, 0), (width // 2 + x, height // 2 - y), size)


while True:
    screen.fill((255, 255, 255))  # 设置背景为白色,但放到事件循环之外就不行

    # 绘制横线与凸透镜(物理符号)
    line(0 - width // 2, 0, width // 2, 0)
    line(0, -150, 0, 150)
    line(-10, 140, 0, 150)
    line(10, 140, 0, 150)
    line(-10, -140, 0, -150)
    line(10, -140, 0, -150)

    # 凸透镜中四个重要的点:左边的2f, f,右边的2f, f
    text('2f', -200, 12)
    dot(-200, 0, 3)
    text('2f', 200, 12)
    dot(200, 0, 3)
    text('f', -100, 12)
    dot(-100, 0, 3)
    text('f', 100, 12)
    dot(100, 0, 3)

    # 这里的作用等同于上一篇文章里的image函数,但在这里没有必要单独拎出来
    event = pygame.event.wait()  # 获取事件
    if event.type == pygame.MOUSEMOTION:  # 判断鼠标是否移动
        x, y = event.pos
        x -= width // 2  # 因为函数里的x实际上减去了width // 2,所以这里也要减掉
        if x <= 0:  # 因为物体的x坐标不能超过0,所以如果物体的x坐标大于0,就要其x坐标设为0
            n = x
        else:
            n = 0
        # 绘制在作图时必要的线段
        line(5 * n, 500, 4 * (0 - n), -400)
        line(n, 100, 0, 100)
        line(-400, 500, 500, -400)

        # 绘制物体
        line(n, 0, n, 100, (255, 0, 0))
        line(n - 10, 90, n, 100, (255, 0, 0))
        line(n + 10, 90, n, 100, (255, 0, 0))
        if n != -100:  # 因为物体在-100(左侧的f点)时不成像,所以这里要特殊处理一下
            if n == 0:  # 在x坐标等于0时会有除0报错(笔者也不知道为什么),也是特殊处理一下
                kx = 0
            else:
                kx = (100 * n) / (n + 100)  # kx的是什么后面会讲到
            line(kx, 0, kx, 100 - kx, (255, 0, 0))  # 绘制虚像
            if n <= -100:  # 虚像的箭头方向要分正立倒立来处理
                line(kx - 10, 110 - kx, kx, 100 - kx, (255, 0, 0))
                line(kx + 10, 110 - kx, kx, 100 - kx, (255, 0, 0))
            else:
                line(kx - 10, 90 - kx, kx, 100 - kx, (255, 0, 0))
                line(kx + 10, 90 - kx, kx, 100 - kx, (255, 0, 0))

    for event in pygame.event.get():  # 点击右上角的叉号时要做的
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
    pygame.display.flip()  # 刷新屏幕

四. 讲解

1. line,text与dot函数

这里就是关于在 s c r e e n screen screen上面进行绘制操作的函数。

# 绘制线段的函数
def line(x1, y1, x2, y2, color=(0, 0, 0)):  # 颜色color默认为黑色
    # 这里出现"width // 2 +"与"height // 2 -"是为了与turtle使用的坐标轴一样,下同
    pygame.draw.line(screen, color, (width // 2 + x1, height // 2 - y1), (width // 2 + x2, height // 2 - y2), 3)

# 写出字体
def text(txt, x, y):
    f = pygame.font.Font('C://Windows//Fonts//ARIALN.TTF', 20)  # 这里选用ARIALN字体
    text = f.render(txt, True, (0, 0, 0))
    t = text.get_rect()
    t.center = (width // 2 + x, height // 2 - y)
    screen.blit(text, t)

# 绘制出实心圆
def dot(x, y, size):
    pygame.draw.circle(screen, (0, 0, 0), (width // 2 + x, height // 2 - y), size)
  • c o l o r = ( 0 , 0 , 0 ) color=(0, 0, 0) color=(0,0,0): 在绘制直线是颜色 c o l o r color color默认为黑色;
  • ( w i d t h / / 2 + x 1 , h e i g h t / / 2 ? y 1 ) , ( w i d t h / / 2 + x 2 , h e i g h t / / 2 ? y 2 ) (width // 2 + x1, height // 2 - y1), (width // 2 + x2, height // 2 - y2) (width//2+x1,height//2?y1),(width//2+x2,height//2?y2): 出现这些是为了与turtle使用的坐标轴一样,下同;
  • p y g a m e . f o n t . F o n t ( ′ C : / / W i n d o w s / / F o n t s / / A R I A L N . T T F ′ , 20 ) pygame.font.Font('C://Windows//Fonts//ARIALN.TTF', 20) pygame.font.Font(C://Windows//Fonts//ARIALN.TTF,20): 在打印字体时笔者选用了 A R I A L N ARIALN ARIALN字体,如果读者默认路径下没有可以自己找其他字体。

2. 基础的凸透镜与四个点的绘制

# 绘制横线与凸透镜(物理符号)
line(0 - width // 2, 0, width // 2, 0)
line(0, -150, 0, 150)
line(-10, 140, 0, 150)
line(10, 140, 0, 150)
line(-10, -140, 0, -150)
line(10, -140, 0, -150)


# 凸透镜中四个重要的点:左边的2f, f,右边的2f, f
text('2f', -200, 12)
dot(-200, 0, 3)
text('2f', 200, 12)
dot(200, 0, 3)
text('f', -100, 12)
dot(-100, 0, 3)
text('f', 100, 12)
dot(100, 0, 3)
  • l i n e ( 0 ? w i d t h / / 2 , 0 , w i d t h / / 2 , 0 ) line(0 - width // 2, 0, width // 2, 0) line(0?width//2,0,width//2,0): 这样是为了让横线穿过整个屏幕;
  • t e x t ( ′ 2 f ′ , ? 200 , 12 ) text('2f', -200, 12) text(2f,?200,12): 这是凸透镜中四个重要的点:左边的 2 f 2f 2f点与 f f f点,右边的 2 f 2f 2f点与 f f f点。

3. 鼠标点击事件

这里的作用等同于上一篇文章里的image函数,但在这里没有必要单独拎出来。

# 这里的作用等同于上一篇文章里的image函数,但在这里没有必要单独拎出来
event = pygame.event.wait()  # 获取事件
if event.type == pygame.MOUSEMOTION:  # 判断鼠标是否移动
    x, y = event.pos
    x -= width // 2  # 因为函数里的x实际上减去了width // 2,所以这里也要减掉
    if x <= 0:  # 因为物体的x坐标不能超过0,所以如果物体的x坐标大于0,就要其x坐标设为0
        n = x
    else:
        n = 0
  • e v e n t = p y g a m e . e v e n t . w a i t ( ) event = pygame.event.wait() event=pygame.event.wait(): 获取屏幕发生的事件;
  • i f e v e n t . t y p e = = p y g a m e . M O U S E M O T I O N if event.type == pygame.MOUSEMOTION ifevent.type==pygame.MOUSEMOTION: 判断鼠标是否在移动;
  • x ? = w i d t h / / 2 x -= width // 2 x?=width//2: 因为函数里的x实际上减去了 w i d t h width width / / // // 2 2 2,所以这里需要抵消掉;
  • i f x < = 0 if x <= 0 ifx<=0: 因为物体不可能在凸透镜右侧,所以如果物体的 x x x坐标大于 0 0 0,就要其 x x x坐标设为 0 0 0
  • n = x ∣ ∣ n = 0 n=x||n=0 n=x∣∣n=0: n n n为像的 x x x坐标。

4. 绘制物体与像

    # 绘制在作图时必要的线段
    line(5 * n, 500, 4 * (0 - n), -400)
    line(n, 100, 0, 100)
    line(-400, 500, 500, -400)

    # 绘制物体
    line(n, 0, n, 100, (255, 0, 0))
    line(n - 10, 90, n, 100, (255, 0, 0))
    line(n + 10, 90, n, 100, (255, 0, 0))
    if n != -100:  # 因为物体在-100(左侧的f点)时不成像,所以这里要特殊处理一下
        if n == 0:  # 在x坐标等于0时会有除0报错(笔者也不知道为什么),也是特殊处理一下
            kx = 0
        else:
            kx = (100 * n) / (n + 100)  # kx的是什么后面会讲到
        line(kx, 0, kx, 100 - kx, (255, 0, 0))  # 绘制虚像
        if n <= -100:  # 虚像的箭头方向要分正立倒立来处理
            line(kx - 10, 110 - kx, kx, 100 - kx, (255, 0, 0))
            line(kx + 10, 110 - kx, kx, 100 - kx, (255, 0, 0))
        else:
            line(kx - 10, 90 - kx, kx, 100 - kx, (255, 0, 0))
            line(kx + 10, 90 - kx, kx, 100 - kx, (255, 0, 0))
  • l i n e ( 5 ? n , 500 , 4 ? ( 0 ? n ) , ? 400 ) line(5 * n, 500, 4 * (0 - n), -400) line(5?n,500,4?(0?n),?400): 绘制在作图时必要的线段;
  • i f n ! = ? 100 if n != -100 ifn!=?100: 因为物体在坐标为 ? 100 -100 ?100(左侧的 f f f点)时不成像,所以这里要特殊处理一下;
  • i f n = = 0 if n == 0 ifn==0: 在 x x x坐标等于 0 0 0时会有除 0 0 0报错 (笔者也不知道为什么), 也是特殊处理一下;
  • i f n < = ? 100 if n <= -100 ifn<=?100: 虚像的箭头方向要分正立与倒立来处理;
  • k x = ( 100 ? n ) / ( n + 100 ) kx = (100 * n) / (n + 100) kx=(100?n)/(n+100): 这里定义 k x kx kx是为了后面方便计算,具体干什么后面会讲到。

5. image函数里的kx变量

很多读者都会疑惑这个 k x kx kx是什么,其实这个是虚像的那个顶点的 x x x坐标。
在这里插入图片描述

但为什么是 k x = ( 100 ? n ) / ( n + 100 ) kx = (100 * n) / (n + 100) kx=(100?n)/(n+100)(即 100 ? n n + 100 {{100\,n}\over{n+100}} n+100100n?)呢?(笔者在上篇文章中没有化到最简,这次就化到了最简)其实,就是初二的一次函数的知识就可以求出来(两条直线 y = k x + b y=kx+b y=kx+b的交点)。
(笔者还是第二次用Markdown写这么长的 LaTeX \LaTeX LATE?X的代码呢)

(1). 求解

设没经过原点的直线为 l 1 l_1 l1?,则经过原点的直线为 l 2 l_2 l2?,由上图可知
直线 l 1 l_1 l1?的表达式为 y 1 = k 1 ? x 1 + b 1 y_1=k_1·x_1+b_1 y1?=k1??x1?+b1?; ①
直线 l 2 l_2 l2?的表达式为 y 2 = k 2 ? x 2 + b 2 y_2=k_2·x_2+b_2 y2?=k2??x2?+b2?. ②

由图可知 直线 l 1 l_1 l1?经过坐标为 ( 0 , 100 ) (0, 100) (0,100)与坐标为 ( 100 , 0 ) (100, 0) (100,0)的点,直线 l 2 l_2 l2?经过坐标为 ( n , 100 ) (n, 100) (n,100)与坐标为 ( 0 , 0 ) (0, 0) (0,0)的点.
将坐标为 ( 0 , 100 ) (0, 100) (0,100)的点代入①,得
100 = 0 ? x 1 + b 1 , 100=0·x_1+b_1, 100=0?x1?+b1?,
解得
b 1 = 100. b_1=100. b1?=100.
将坐标为 ( 100 , 0 ) (100, 0) (100,0)的点与 b 1 = 100 b_1=100 b1?=100代入①,得
0 = 100 k 1 + 100 , 0=100k_1+100, 0=100k1?+100,
解得
k 1 = ? 1. k_1=-1. k1?=?1.
∴ \therefore 直线 l 1 l_1 l1?的表达式为 y 1 = ? x 1 + 100 y_1=-x_1+100 y1?=?x1?+100.
同理,将坐标为 ( n , 100 ) (n, 100) (n,100) ( 0 , 0 ) (0, 0) (0,0)的点代入②,得
b 2 = 0 , k 2 = 100 n , b_2=0,k_2=\frac{100}{n}, b2?=0,k2?=n100?,
∴ \therefore 直线 l 2 l_2 l2?的表达式为 y 2 = 100 n x 2 y_2=\frac{100}{n}x_2 y2?=n100?x2?.

∵ \because 所求的点为两直线 l 1 l 2 l_1l_2 l1?l2?的交点,
∴ \therefore { y = ? x + 100 y = 100 n x \left\{\begin{matrix} y=-x+100 \\ y=\frac{100}{n}x \end{matrix}\right. {y=?x+100y=n100?x?
解得 { x = 100 ? n n + 100 y = 100 ? 100 ? n n + 100 \left\{\begin{matrix} x={{100\,n}\over{n+100}} \\ y=100-{{100\,n}\over{n+100}} \end{matrix}\right. {x=n+100100n?y=100?n+100100n??
综上所述,所求之点的坐标为 ( 100 ? n n + 100 , 100 ? 100 ? n n + 100 ) . ({{100\,n}\over{n+100}}, 100-{{100\,n}\over{n+100}}). (n+100100n?,100?n+100100n?).

(2). 转换成代码

通过结论我们可以发现 x x x y y y坐标里都有一个 100 ? n n + 100 {{100\,n}\over{n+100}} n+100100n?,所以,如果定义变量 k x = 100 ? n n + 100 kx={{100\,n}\over{n+100}} kx=n+100100n?,则那个点的坐标为 ( k x , 100 ? k x ) (kx,100-kx) (kx,100?kx).
证毕

5. 倒回来讲一些基本的部分

在这里插入代码片import sys
import pygame

width, height = 1000, 600  # 屏幕的宽高
pygame.init()  # Pygame初始化
screen = pygame.display.set_mode((width, height))  # 初始化屏幕
pygame.display.set_caption('凸透镜成像规律')  # 设置标题

...

while True:
    screen.fill((255, 255, 255))  # 设置背景为白色,但放到事件循环之外就不行
    
    ...
    
    for event in pygame.event.get():  # 点击右上角的叉号时要做的
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
    pygame.display.flip()  # 刷新屏幕
  • w i d t h width width, h e i g h t = 1000 , 600 height = 1000, 600 height=1000,600: 定义屏幕宽高的常量,因为用大写有一些麻烦1,所以笔者就不大写了;
  • p y g a m e . d i s p l a y . s e t pygame.display.set pygame.display.set_ c a p t i o n caption caption(‘凸透镜成像规律’): 设置窗口标题;
  • s c r e e n . f i l l ( ( 255 , 255 , 255 ) ) screen.fill((255, 255, 255)) screen.fill((255,255,255)): 设置背景为白色,但放到事件循环之外就会只更新一次;
  • p y g a m e . d i s p l a y . f l i p ( ) pygame.display.flip() pygame.display.flip(): 每过一次事件循环就刷新一次屏幕。

五. 程序效果

在这里插入图片描述

这个还挺好玩的

六. 总结

本文章以凸透镜成像规律为基础,使用Python的tutle库把凸透镜成像规律的原理直观地表现了出来。再次希望读者再接再厉,让多学科融入代码里,创造更美好的未来!


  1. python 常量基础教程_CSDN博客 ??

文章来源:https://blog.csdn.net/Iwashappyin1212/article/details/135569163
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。