坐标系统

JAnim 的坐标系统不以像素为单位,而是采用水平方向约 -7.11 ~ 7.11,垂直方向 -4 ~ 4 的坐标范围,形成一个 16:9 的画面。

原点位于画面中心,向右向上为正方向,下图展现了创建在 JAnim 画面上的坐标系网格以及放置于原点处的单位圆:

../_images/CoordinatesGrid.png
NumberPlane(faded_line_ratio=0).show()

circle = Circle(color=YELLOW).show()

提示

这里的 xxx.show()self.show(xxx) 一样,都是将物件直接显示出来

备注

为了方便起见,这里提供的代码省略了 construct 方法之外的内容,只展示 construct 的核心部分

类似情况不再赘述

在之前的简要介绍中,我们提到 JAnim 的几何 物件 含有多个 组件,包括 pointsstrokefill 等; 而其中与坐标系统相关的组件是 points,它决定了物件的形状和位置。

所以在大多数情况下,当我们想要“操作物件的坐标”时,先 .points 再做具体操作就对了!

坐标平移

points 组件提供了多种方法来操作物件的坐标,这里先介绍两个基础方法:

方法

示例

描述

shift()

.shift(RIGHT * 3 + UP * 2)

根据给定的位移平移物件

move_to()

.move_to(ORIGIN)

将物件移动到指定位置

例如,使用 .move_to(RIGHT * 3 + UP * 2) 可以将物件移动到坐标系中 (3,2) 的位置:

../_images/MoveTo.png
circle = Circle(color=YELLOW).show()
circle.points.move_to(RIGHT * 3 + UP * 2)

上面的例子中,我们组合 UP (向上) 和 RIGHT (向右) 这两个内置向量表示出了圆的位移; 你也可以猜到,还有 DOWN (向下) 和 LEFT (向左) 这两个内置向量可以使用。

这些内置向量方便在 JAnim 中快速表示坐标位移,只需要使用位移距离乘以方向,再依次组合即可。

JAnim 还内置了更多额外方向,如图所示:

../_images/BuiltinDirections.png

提示

边上的这四个向量需要使用 Config.get 来获取,这是因为他们是由视框大小这一可配置项所决定的, 并不是像单位向量这样的固定向量。

关于 Config 的更多内容,可以参考 配置系统

除了使用内置方向的组合表示坐标,还可以直接使用坐标值来表示位置。

例如,上面提到的 .move_to(RIGHT * 3 + UP * 2) 等效于 .move_to([3, 2, 0]),也就是将物件移动到 [3, 2, 0] 的位置。

重要

尽管我们现在讨论的是画面上的二维坐标,但其实 JAnim 的坐标系统是三维的,物件和摄像机可以在空间中自由移动,所以这里还出现了第三个分量。

但在大多数情况下,我们只需要关注前两个坐标轴(x 和 y)就够用了,将 z 轴取为 0 即可。

关于三维坐标的更多内容,可以参考 三维坐标 (TODO)

相对放置

除了上述的平移方法,物件还可以放置在其它物件以及边界旁边:

方法

示例

描述

next_to()

.next_to(square, RIGHT)

将物件放置在另一个物件旁边

align_to()

.align_to(square, UP)

将物件与另一个物件在某个方向上对齐

to_border()

.to_border(UL)

将物件放到画面某个方向的边界旁

让我们结合动画来演示这些方法的使用,这里是将一个圆形四处移动,尝试放在矩形旁边以及边界旁边:

小技巧

回忆一下,对于立刻作用的 circle.points.next_to(...) 等方法, 在物件后面插入 .anim 便可使其成为可播放的动画,即

circle.anim.points.next_to(...)
square = Square().show()
square.points.move_to([-3, -1, 0])

circle = Circle(radius=0.5, color=YELLOW)

self.play(Create(circle))

self.play(circle.anim.points.next_to(square, RIGHT))
self.play(circle.anim.points.next_to(square, RIGHT, buff=MED_LARGE_BUFF))
self.play(circle.anim.points.next_to(square, RIGHT, buff=MED_LARGE_BUFF, aligned_edge=UP))

self.forward()

self.play(circle.anim.points.to_border(UP))
self.play(circle.anim.points.to_border(UR))
self.play(circle.anim.points.to_border(UR, buff=LARGE_BUFF))
self.play(circle.anim.points.to_border(UL))

self.forward()

self.play(circle.anim.points.align_to(square, UP))
self.play(circle.anim.points.to_border(UL))
self.play(circle.anim.points.align_to(square, LEFT))

self.forward()

备注

上面提供的动画代码实际执行时没有坐标系网格和文字备注,那些是笔者额外添加的;

且进行了降速处理,方便观看动画过程。

示例中的方法出现了一些额外的参数:

  • buff

    表示物件与目标物件或边界之间的间距,间距从小到大可用 SMALL_BUFFMED_SMALL_BUFFMED_LARGE_BUFFLARGE_BUFF 以及直接数值表示

    物件之间的间距默认为 MED_SMALL_BUFF,物件与边界直接的间距默认为 MED_LARGE_BUFF

  • aligned_edge

    表示物件与目标物件的对齐边缘,

    比如示例中的 aligned_edge=UP 表示将圆形放置在方形右侧的同时,使它们的上边缘对齐。

形状变换

常用的形状变换包括缩放与旋转:

方法

示例

描述

scale()

.scale(2)

缩放物件

stretch()

.stretch(2, dim=0)

在某个方向上拉伸物件,dim=0 dim=1 dim=2 分别表示 x、y、z 轴

rotate()

.rotate(PI / 4)

旋转物件,逆时针为正方向

让我们结合动画来演示这些方法的使用,这里是将一个正六边形进行若干形状变换:

poly = RegularPolygon(6).show()

self.forward()
self.play(poly.anim.points.scale(2))
self.play(poly.anim.points.rotate(PI / 6))
self.play(poly.anim.points.stretch(2, dim=0))
self.play(poly.anim.points.scale(0.25, about_edge=RIGHT))
self.play(poly.anim.points.rotate(120 * DEGREES, about_point=ORIGIN))
self.play(Rotate(poly, 120 * DEGREES, about_point=ORIGIN))
self.play(poly.anim.points.rotate(PI / 2))
self.forward()

首先对于旋转操作,传入的数值应是弧度制,JAnim 也内置了常用角度的常量,如 PITAU。 也可以使用如 30 * DEGREES 的形式表示角度值,这与 PI / 6 等价。

对于一些额外的参数:

  • about_edge

    表示缩放或旋转时的参考边缘,默认为 ORIGIN,即物件的中心点。

    例如在 .scale(0.25, about_edge=RIGHT) 中,将缩放的参考点设置为物件的右边缘,使得物件被缩小时右边缘位置不变,其余部分向右边缘聚拢。

  • about_point

    表示缩放或旋转时的参考点,默认为 None,会根据 about_edge 取参考点,即默认以物件的中心;

    可以设置为某个点,表示以全局坐标中的该点为参考点。

    例如在 .rotate(120 * DEGREES, about_point=ORIGIN) 中,将旋转的参考点设置为全局坐标的原点,使得物件围绕原点旋转。

self.play(poly.anim.points.rotate(120 * DEGREES, about_point=ORIGIN))
self.play(Rotate(poly, 120 * DEGREES, about_point=ORIGIN))

这两行看起来都是“将物件绕原点逆时针旋转 120 度”,但其实存在本质上的差异。

前者是 poly 的组件动画,本质上是对操作前后的 poly插值效果,是直线变换而非弧线移动,并没有真正的旋转效果;

而后者 Rotate 作为专门的一个动画类,使得物件沿着圆弧路径旋转,产生真正的旋转效果。

另请参考 rotation 页面的介绍。