julia语言的2D变换动画演示

科技一点鑫得 2024-03-10 00:10:11

在计算机图形学中,将一个几何模型物体放入到某个场景中,通常需要做三件事:

移动:将该物体移动到某一位置,

缩放:对该物体进行缩放使其与场景中其他物体的大小相匹配,

旋转:对该物体进行旋转使其具有正确的朝向。

这些操作(移动、缩放、旋转)都属于对物体顶点坐标的线性变换。本文使用julia语言来实现5种2D变换实例的动画演示,但是对线性变换的数学原理不做讲解,请自行温习相关的知识点。

作为一个认真的喜爱计算机图形学的人,都应该用心学习线性代数

旋转

下面的变换矩阵公式表示将向量[x,y]逆时针旋转θ角度,旋转后的向量变为[x',y']

动画中展示的是由坐标(0,0),(1,0),(1,1),(0,1)组成的正方形绕原点旋转0~360°的旋转变换效果

julia代码及说明:

# 引用可视化库using GLMakieusing GLMakie.Colorsusing GLMakie.GeometryBasics# 引用格式化输出库using Printf# 生成30个角度序列,范围从0~360deg_iterator = range(0, 360, length=30)# 组成的正方形的四个坐标(0,0),(1,0),(1,1),(0,1)# 表示成矩阵形式,每一列表示为一对坐标square = [0 1 1 0 0 0 1 1]# 创建图形窗口fig = Figure()# Axis表示在Figure中创建一个子图ax = Axis(fig[1, 1])# 设置x,y轴坐标范围为-3~3xlims!(ax, -3, 3)ylims!(ax, -3, 3)function update(deg) # 旋转矩阵,deg2rad方法的功能是从角度转换为弧度 M1 = [cos(deg2rad(deg)) -sin(deg2rad(deg)) sin(deg2rad(deg)) cos(deg2rad(deg))] # 计算旋转变换后的向量 square2 = M1*square # 每一帧绘制前清空之前的绘制画面 empty!(ax) # 绘制旋转变换后的正方形区域 # color定义填充区域的颜色,填充颜色随着角度不断变化 # strokecolor为正方形边框的颜色 # strokewidth为边框的宽度 poly!(Point2f[square2[1:2], square2[3:4],square2[5:6], square2[7:8]], color = HSV(deg, 1, 0.75), strokecolor = :black, strokewidth = 1) # 分别绘制四个顶点坐标数据 x, y = @sprintf("%1.1f",square2[1]), @sprintf("%1.1f",square2[1]) text!("($x,$y)", position=(square2[1],square2[2])) x, y = @sprintf("%1.1f",square2[3]), @sprintf("%1.1f",square2[4]) text!("($x,$y)", position=(square2[3],square2[4])) x, y = @sprintf("%1.1f",square2[5]), @sprintf("%1.1f",square2[6]) text!("($x,$y)", position=(square2[5],square2[6])) x, y = @sprintf("%1.1f",square2[7]), @sprintf("%1.1f",square2[8]) text!("($x,$y)", position=(square2[7],square2[8]))end# 动画录制方法,动画生成gif文件# deg_iterator为旋转角度的序列,每个值对应动画的一帧# framerate控制动画的快慢# update方法是具体的画面绘制,输入参数为deg_iterator中的角度record(update, fig, "2d_transform_rotate.gif", deg_iterator; framerate = 10)非均匀缩放

下面的变换矩阵为缩放矩阵,当a和b不相同时会产生不均匀缩放

动画展示的是由坐标(0,0),(1,0),(1,1),(0,1)组成的正方形沿x、y轴保持2/3的比例进行缩放的动画效果

julia代码及说明:

# 引用可视化库using GLMakieusing GLMakie.Colorsusing GLMakie.GeometryBasics# 引用格式化输出库using Printf# 生成30个角度序列,范围从0~1frame_iterator = range(0, 1, length=30)# 组成的正方形的四个坐标(0,0),(1,0),(1,1),(0,1)# 表示成矩阵形式,每一列表示为一对坐标square = [0 1 1 0 0 0 1 1]# 创建图形窗口fig = Figure()# Axis表示在Figure中创建一个子图ax = Axis(fig[1, 1])# 设置x,y轴坐标范围设置xlims!(ax, -2, 4)ylims!(ax, -2, 4)function update(frame) # 缩放矩阵 M1 = [1+2*frame 0 0 1+frame] # 计算变换后的向量 square2 = M1*square # 每一帧绘制前清空之前的绘制画面 empty!(ax) # 绘制变换后的正方形区域 # color定义填充区域的颜色,填充颜色随着角度不断变化 # strokecolor为正方形边框的颜色 # strokewidth为边框的宽度 poly!(Point2f[square2[1:2], square2[3:4],square2[5:6], square2[7:8]], color = HSV(frame*360, 1, 0.75), strokecolor = :black, strokewidth = 1) # 分别绘制四个顶点坐标数据 x, y = @sprintf("%1.1f",square2[1]), @sprintf("%1.1f",square2[1]) text!("($x,$y)", position=(square2[1],square2[2])) x, y = @sprintf("%1.1f",square2[3]), @sprintf("%1.1f",square2[4]) text!("($x,$y)", position=(square2[3],square2[4])) x, y = @sprintf("%1.1f",square2[5]), @sprintf("%1.1f",square2[6]) text!("($x,$y)", position=(square2[5],square2[6])) x, y = @sprintf("%1.1f",square2[7]), @sprintf("%1.1f",square2[8]) text!("($x,$y)", position=(square2[7],square2[8]))end# 动画录制方法,动画生成gif文件# frame_iterator为0~1范围的序列,每个值对应动画的一帧# framerate控制动画的快慢# update方法是具体的画面绘制,输入参数为frame_iterator中的值record(update, fig, "2d_transform_scale.gif", frame_iterator; framerate = 10)错切

下面两个变换矩阵分表表示y轴坐标不变沿x轴平行移动、x轴坐标不变沿y轴平行移动的错切变换。

动画以沿x轴平行移动为例进行展示,展示的是a从0~2范围内逐渐变化的情况下错切的变化效果

julia代码及说明:

# 引用可视化库using GLMakieusing GLMakie.Colorsusing GLMakie.GeometryBasics# 引用格式化输出库using Printf# 生成30个角度序列,范围从0~1frame_iterator = range(0, 1, length=30)# 组成的正方形的四个坐标(0,0),(1,0),(1,1),(0,1)# 表示成矩阵形式,每一列表示为一对坐标square = [0 1 1 0 0 0 1 1]# 创建图形窗口fig = Figure()# Axis表示在Figure中创建一个子图ax = Axis(fig[1, 1])# 设置x,y轴坐标范围设置xlims!(ax, -2, 4)ylims!(ax, -2, 4)function update(frame) # 错切变换矩阵 M1 = [1 2*frame 0 1] # 计算变换后的向量 square2 = M1*square # 每一帧绘制前清空之前的绘制画面 empty!(ax) # 绘制变换后的正方形区域 # color定义填充区域的颜色,填充颜色随着角度不断变化 # strokecolor为正方形边框的颜色 # strokewidth为边框的宽度 poly!(Point2f[square2[1:2], square2[3:4],square2[5:6], square2[7:8]], color = HSV(frame*360, 1, 0.75), strokecolor = :black, strokewidth = 1) # 分别绘制四个顶点坐标数据 x, y = @sprintf("%1.1f",square2[1]), @sprintf("%1.1f",square2[1]) text!("($x,$y)", position=(square2[1],square2[2])) x, y = @sprintf("%1.1f",square2[3]), @sprintf("%1.1f",square2[4]) text!("($x,$y)", position=(square2[3],square2[4])) x, y = @sprintf("%1.1f",square2[5]), @sprintf("%1.1f",square2[6]) text!("($x,$y)", position=(square2[5],square2[6])) x, y = @sprintf("%1.1f",square2[7]), @sprintf("%1.1f",square2[8]) text!("($x,$y)", position=(square2[7],square2[8]))end# 动画录制方法,动画生成gif文件# frame_iterator为0~1范围的序列,每个值对应动画的一帧# framerate控制动画的快慢# update方法是具体的画面绘制,输入参数为frame_iterator中的值record(update, fig, "2d_transform_miscut.gif", frame_iterator; framerate = 10)一般变换

更一般的变化矩阵如下所示,a、b、c、d的不同取值可能产生扭曲的效果,而不是简单的旋转、缩放或错切。

动画展示了由(0,0),(1,0),(1,1),(0,1)组成的正方形,变换矩阵从[1 0;0 1]逐渐变为[1 -1;2 2]过程中形状的变化过程

julia代码及说明:

# 引用可视化库using GLMakieusing GLMakie.Colorsusing GLMakie.GeometryBasics# 引用格式化输出库using Printf# 生成30个角度序列,范围从0~1frame_iterator = range(0, 1, length=30)# 组成的正方形的四个坐标(0,0),(1,0),(1,1),(0,1)# 表示成矩阵形式,每一列表示为一对坐标square = [0 1 1 0 0 0 1 1]# 创建图形窗口fig = Figure()# Axis表示在Figure中创建一个子图ax = Axis(fig[1, 1])# 设置x,y轴坐标范围设置xlims!(ax, -2, 5)ylims!(ax, -2, 5)function update(frame) # 一般变换矩阵,从[1 0;0 1]逐渐过渡到[1 -1;2 2] M1 = [1 -frame 2*frame 1+frame] # 计算变换后的向量 square2 = M1*square # 每一帧绘制前清空之前的绘制画面 empty!(ax) # 绘制变换后的正方形区域 # color定义填充区域的颜色,填充颜色随着角度不断变化 # strokecolor为正方形边框的颜色 # strokewidth为边框的宽度 poly!(Point2f[square2[1:2], square2[3:4],square2[5:6], square2[7:8]], color = HSV(frame*360, 1, 0.75), strokecolor = :black, strokewidth = 1) # 分别绘制四个顶点坐标数据 x, y = @sprintf("%1.1f",square2[1]), @sprintf("%1.1f",square2[1]) text!("($x,$y)", position=(square2[1],square2[2])) x, y = @sprintf("%1.1f",square2[3]), @sprintf("%1.1f",square2[4]) text!("($x,$y)", position=(square2[3],square2[4])) x, y = @sprintf("%1.1f",square2[5]), @sprintf("%1.1f",square2[6]) text!("($x,$y)", position=(square2[5],square2[6])) x, y = @sprintf("%1.1f",square2[7]), @sprintf("%1.1f",square2[8]) text!("($x,$y)", position=(square2[7],square2[8]))end# 动画录制方法,动画生成gif文件# frame_iterator为0~1范围的序列,每个值对应动画的一帧# framerate控制动画的快慢# update方法是具体的画面绘制,输入参数为frame_iterator中的值record(update, fig, "2d_transform_general.gif", frame_iterator; framerate = 10)退化(或奇异)变换

如下变换矩阵称为退化矩阵,因为该矩阵变换后2D平面会退化为一条直线,也就是退化到1D子空间。

动画展示了由(0,0),(1,0),(1,1),(0,1)组成的正方形从最初形状过渡到最终经变换矩阵[1 -1;2 -2]退化到一条直线的过程

julia代码及说明:

# 引用可视化库using GLMakieusing GLMakie.Colorsusing GLMakie.GeometryBasics# 引用格式化输出库using Printf# 生成30个角度序列,范围从0~1frame_iterator = range(0, 1, length=30)# 组成的正方形的四个坐标(0,0),(1,0),(1,1),(0,1)# 表示成矩阵形式,每一列表示为一对坐标square = [0 1 1 0 0 0 1 1]# 创建图形窗口fig = Figure()# Axis表示在Figure中创建一个子图ax = Axis(fig[1, 1])# 设置x,y轴坐标范围设置xlims!(ax, -2, 4)ylims!(ax, -2, 4)function update(frame) # 退化变换矩阵,从[1 0;0 1]逐渐过渡到[1 -1;2 -2] M1 = [1 -frame 2*frame 1-3*frame] # 计算变换后的向量 square2 = M1*square # 每一帧绘制前清空之前的绘制画面 empty!(ax) # 绘制变换后的正方形区域 # color定义填充区域的颜色,填充颜色随着角度不断变化 # strokecolor为正方形边框的颜色 # strokewidth为边框的宽度 poly!(Point2f[square2[1:2], square2[3:4],square2[5:6], square2[7:8]], color = HSV(frame*360, 1, 0.75), strokecolor = :black, strokewidth = 1) # 分别绘制四个顶点坐标数据 x, y = @sprintf("%1.1f",square2[1]), @sprintf("%1.1f",square2[1]) text!("($x,$y)", position=(square2[1],square2[2])) x, y = @sprintf("%1.1f",square2[3]), @sprintf("%1.1f",square2[4]) text!("($x,$y)", position=(square2[3],square2[4])) x, y = @sprintf("%1.1f",square2[5]), @sprintf("%1.1f",square2[6]) text!("($x,$y)", position=(square2[5],square2[6])) x, y = @sprintf("%1.1f",square2[7]), @sprintf("%1.1f",square2[8]) text!("($x,$y)", position=(square2[7],square2[8]))end# 动画录制方法,动画生成gif文件# frame_iterator为0~1范围的序列,每个值对应动画的一帧# framerate控制动画的快慢# update方法是具体的画面绘制,输入参数为frame_iterator中的值record(update, fig, "2d_transform_degenerate.gif", frame_iterator; framerate = 10)
0 阅读:0

科技一点鑫得

简介:感谢大家的关注