AnyMath 文档

变换

该页面正在翻译中。

每个情节和每个场景都包含一个 转型 持有 变换_func 并生成一个 模型 矩阵。

模型转换

模型 矩阵由平移、缩放和旋转组成,旋转首先起作用,平移最后起作用。 翻译由 翻译!(),按比例缩放 规模!() 和旋转通过 旋转!(). 此外,您可以更改用于缩放和旋转的原点 起源!(). </无翻译>

using GLMakie
using GLMakie

box = Rect2f(0.9, -0.1, 0.2, 0.2)

f = Figure(size = (500, 450))
a = Axis(f[1, 1], aspect = DataAspect())
xlims!(a, 0.2, 1.2); a.xticks[] = 0.1:0.2:1.1
ylims!(a, -0.2, 0.8); a.yticks[] = -0.1:0.2:0.7

# Initial plot for reference
scatterlines!(a, box, color = 1:4, markersize = 20, linewidth = 5)

# Transformed plot
p2 = scatterlines!(a, box, color = 1:4, markersize = 20, linewidth = 5)
origin!(p2, 1,0,0) # apply rotation & scaling relative to the center of the box
scale!(p2, 2, 2)             # double x, y
Makie.rotate!(p2, pi/2)      # 90° rotation
translate!(p2, -0.5, 0.5)    # translate 0.5 left, 0.5 up

f
be6dd3d

默认情况下,调用这些函数将复盖以前调用设置的值。 所以如果你打电话 翻译!(情节,1,0,0);翻译!(剧情,0,1,0) 翻译将是 (0,1,0). 要累积转换,您需要添加 [医]Accum 作为第一个参数,例如 翻译!(情节,情节,0,1,0).

变换函数

变换_func 是一个函数,它在之后应用于绘图的输入数据 convert_arguments() (类型规范化)和dim_converts(处理单位和类别值)。 它通常由轴管理。 例如,如果设置 斧头xscale[]=log,底层 斧头场景 将其转换函数设置为 (日志、身份) 它将传播到轴/场景内的绘图。

using Makie
f, a, p = scatter(1:10);
Makie.transform_func(a.scene) # (identity, identity)
Makie.transform_func(p) # (identity, identity)
a.xscale[] = log
Makie.transform_func(a.scene) # (log, identity)
Makie.transform_func(p) # (log, identity)

您可以通过更新设置绘图的变换函数 情节。转型。transform_func[]=new_func. 这也会改变每个子图的变换函数。 请注意,这通常会在plots父级转换函数更改时重置,例如,如果 斧头x尺度,x尺度 在上面的例子中设置。 您可以通过显式不继承转换函数或构造 转型 反对你自己。 见下文。

场景变换

这一幕也抓住了一个 转型 对象。 它不会直接影响场景,而是充当添加到场景中的情节的潜在父级转换。 是否使用取决于 转型 属性。 默认情况下,如果场景的空间与给定的情节兼容,它将被使用。

转型 属性

转换属性控制如何 转型 对象进行初始化。 它在情节建设过程中被处理一次,然后被移除。

默认情况下,属性设置为 麦琪自动的. 在这种情况下,如果两个图或场景都使用相同的坐标,则转换将从父图或场景继承 空间. 因此,如果父场景使用3D相机,并且情节使用 空间=:像素,转换不会被继承。 如果相机是像素相机,它将是。 继承转换时,父级 变换_func 被重用和父 模型 矩阵作为二次矩阵变换。 在伪代码中,我们有:

transformation.model = parent.model * local_model
transformation.transform_func = parent.transform_func

继承可以通过几个符号显式控制。 对于所有这些,父级和子级的坐标空间都被忽略。

  • :继承:继承两者 模型变换_func

  • :继承_model:只继承 模型

  • :继承_transform_func:只继承 变换_func

  • :没有:继承既不使新 转型 身份转换

转型 属性也接受输入到 变身!() 函数。 这允许您准备具有初始模型转换的绘图。 你可以通过 比例尺, 轮调 和/或 翻译 作为NamedTuple,Dict或Attributes的一部分,或旋转并将xy平面平移到另一个平面 (飞机、班次). 例如: </无翻译>

using CairoMakie
using CairoMakie

f = Figure()
# transform 0..1 Rect to -1..1 Rect
lines(f[1, 1], Rect2f(0, 0, 1, 1),
    transformation = (scale = Vec3f(2), translation = Vec3f(-1)))

a = LScene(f[1, 2])
heatmap!(a, rand(4,4), transformation = (:xy, 0.5))
heatmap!(a, rand(4,4), transformation = (:xz, 0.5))
heatmap!(a, rand(4,4), transformation = (:yz, 0.5))
f
6afa7b3

最后还可以构造一个 转型 反对自己,并通过 转型 属性。

构造函数

正如在转换函数部分中所提到的,在某些情况下,构造一个 转型 自己可以是有用的。 例如,您可能想要申请 变换_func 在配方中运行三角测量算法之前,这样三角测量就不会失真。 或者,您可能希望将一系列图的变换连锁起来,使它们保持相对(见下文)。 有一些构造函数有助于了解这一点。

如果要将情节与其父变换完全分离,可以使用以下方法创建它 变换=变换(). 如果您只想删除 变换_func 但不是模型转换,您可以使用 transformation=Transformation(parent,transform_func=identity). 您还可以为以下对象传递不同的起始值 翻译, 比例尺轮调 到这些功能。 这不会影响是否考虑父级模型转换。

作为一个例子,这里有两只手臂在推车上用绳子举起一个盒子。 </无翻译>

using GLMakie
using GLMakie
using Makie: Vec3d

f = Figure(size = (600, 400))
a = Axis(f[2, 2], aspect = DataAspect())
ylims!(0, 3); xlims!(-3, 3)

# Cart
cart = Transformation()
scatter!(a, [-0.32, -0.15, 0.15, 0.32], fill(0.09, 4), transformation = cart,
    marker = Circle, color = :transparent, strokewidth = 2, strokecolor = :black,
    markerspace = :data, markersize = 0.1
)
linesegments!(a, [-0.4, 0.4], [0.2, 0.2], transformation = cart,
    color = :black, linewidth = 5
)

# arms
arm1 = Transformation(cart, origin = Vec3d(0, 0.2, 0))
linesegments!(a, [0, 0], [0.2, 2], transformation = arm1,
    color = :black, linewidth = 5, linecap = :round
)
arm2 = Transformation(arm1, origin = Vec3d(0, 2, 0))
linesegments!(a, [0.0, 1.5], [2, 2], transformation = arm2,
    color = :black, linewidth = 5, linecap = :round
)

# rope - we want this to just extend downwards rather than inherit rotations
rope_length = Observable(1.0)
rope_points = map(arm2.model, rope_length) do model, len
    # position of end of arm2 after transformations apply
    rope_origin = (model * Point4(1.5, 2, 0, 1))[Vec(1,2)]
    rope_end = rope_origin - Vec2(0, len)
    return [rope_origin, rope_end]
end
crate_origin = map(ps -> ps[2] .+ Vec2(0, -0.12), rope_points)

linesegments!(a, rope_points,
    color = :black, linewidth = 3, linestyle = :dot, linecap = :round
)
scatter!(a, crate_origin,
    marker = Rect, color = :white, strokewidth = 2, strokecolor = :black,
    markerspace = :data, markersize = Vec2f(0.3, 0.2)
)

# Move cart
sl1 = Slider(f[3, 2], range = range(-4, 4, length = 101))
on(v -> translate!(cart, v, 0, 0), sl1.value)
# Pivot arm 1
sl2 = Slider(f[1, 2], range = range(-pi/3, pi/3, length = 101))
on(v -> Makie.rotate!(arm1, -v), sl2.value)
# Pivot arm 2
sl3 = Slider(f[2, 1], range = range(-pi/3, pi/3, length = 101), horizontal = false)
on(v -> Makie.rotate!(arm2, -v), sl3.value)
# Extend rope
sl4 = Slider(f[2, 3], range = range(2, 0.1, length = 101), startvalue = 1.0, horizontal = false)
on(v -> rope_length[] = v, sl4.value)

# Set up some configuration
set_close_to!(sl1, -1.0) # move cart to -1
set_close_to!(sl2, -0.5) # angle arm1 to the left
set_close_to!(sl3, 0.5) # counter-angle arm2 to be horizontal
set_close_to!(sl4, 0.5) # raise crate

f
c3df23f