Engee 文档
Notebook

使用Genie可视化线性算子的动作

本文致力于应用GenieFramework开发一个交互式web应用程序,演示线性算子的动作。 作为一个例子,我们考虑使用2x2矩阵表示圆的一组点的变换。 Stipple和PlotlyBase包用于实现。 文本详细描述了代码的关键元素,并在相关片段之后进行了解释。 特别注意使用响应式宏的理由 @in@out 控制矩阵的元素(m11, m12, m21, m22),以及使用自定义类型的可能性,例如 mutable struct,基于点画文档。

应用概念

circle.png

附录说明了2x2矩阵作为线性算子对二维点集的影响。 最初,这些点形成半径为1的圆,用户可以使用滑块更改矩阵的元素,观察拉伸,压缩或旋转等变换的影响。 这种方法通过提供学习线性代数的交互式工具具有教育潜力。

从一组点创建一个圆

using GenieFramework, Stipple, Stipple.ReactiveTools, StippleUI, PlotlyBase

const circ_range = -1:0.05:1
const circle = [[i, j] for i in circ_range for j in circ_range if i^2 + j^2 <= 1]
const x_axis_points = findall(x -> x[1] == 0 && x[2] >= 0, circle)
const y_axis_points = findall(x -> x[2] == 0 && x[1] >= 0, circle)
const circle_matrix = Base.stack(circle)

该片段形成可视化的初始数据。 范围被定义 circ_range -1到1,x和y坐标以0.05为增量。 使用列表包含生成坐标对。 [i, j] 由圆方程过滤 i^2 + j^2 <= 1,这给出了一组近似半径为1的圆的点。 变量 x_axis_pointsy_axis_points 它们分别包含位于Y轴和X轴正部分上的点的索引,以突出显示基向量。 功能 Base.stack 将坐标列表转换为矩阵,其中第一行是x坐标,第二行是y坐标。 常量(const)用于不可变的数据。 离散步骤导致圆的一些逐步轮廓,但该方法实现简单。

开发图形转换函数

function create_plot_data(m11::Float64, m12::Float64, m21::Float64, m22::Float64)
    transformed = [m11 m12; m21 m22] * circle_matrix
    [
        scatter(x=transformed[1, :], y=transformed[2, :], mode="markers", name="Точки круга"),
        scatter(x=transformed[1, x_axis_points], y=transformed[2, x_axis_points], name="Ось Y"),
        scatter(x=transformed[1, y_axis_points], y=transformed[2, y_axis_points], name="Ось X", aspect_ratio=:equal)
    ]
end

const initial_plot_data = create_plot_data(1.0, 0.0, 0.0, 1.0)

功能 create_plot_data 负责转换点并为图形准备数据。 接受四个参数—2x2矩阵的元素(m11, m12, m21, m22). 执行矩阵乘法 circle_matrix,获取点的新坐标 transformed. 返回三个对象的数组 scatter:圆的所有点,Y轴上的点和X轴上的点。 参数 aspect_ratio=:equal 提供轴的均匀比例。 常数 initial_plot_data 用不改变圆的单位矩阵设置图形的初始状态。


配置图形布局

const plot_layout = PlotlyBase.Layout(
    title="Линейное преобразование круга",
    xaxis=attr(title="Ось X", showgrid=true, range=[-2, 2]),
    yaxis=attr(title="Ось Y", showgrid=true, range=[-2, 2]),
    width=600, height=550
)

该片段使用以下方法定义图形布局 PlotlyBase.Layout. 设置了标题、轴标签、网格和值范围从-2到2。 图形尺寸是固定的:宽600像素,高550像素。 布局声明为常量,因为它在应用程序运行期间不会更改。

确保反应性

@app begin
    @in m11 = 1.0
    @in m12 = 0.0
    @in m21 = 0.0
    @in m22 = 1.0
    @out plot_data = initial_plot_data
    @out plot_layout = plot_layout

    @onchange m11, m12, m21, m22 begin
        plot_data = create_plot_data(m11, m12, m21, m22)
    end
end

@app 定义响应式应用程序模型。 宏 @in 将矩阵元素声明为具有对应于单位矩阵的初始值的输入变量。 宏 @out 设置输出数据: plot_data 对于时间表和 plot_layout 为布局。 宏 @onchange 跟踪值的变化 m11, m12, m21, m22 和原因 create_plot_data 要更新 plot_data.

自定义用户界面

function ui()
    sliders = [row([column([h6("m$(i)$(j)={{m$(i)$(j)}}"), slider(-2:0.1:2, Symbol("m$(i)$(j)"), color="purple")], size=3) for j in 1:2]) for i in 1:2]
    [
        row([
            column(sliders, size=4),
            column(plot(:plot_data, layout=:plot_layout))
        ], size=3)
    ]
end

@page("/", ui)

功能 ui 形成接口。 变量 sliders 创建一个由两行组成的数组,每行包含两个带有标题的滑块(m11, m12, m21, m22). 滑块的范围从-2到2,增量为0.1。 接口由行(row)有两列:左边的滑块,右边的图形,通过 plot. 宏 @page 将接口绑定到根路由"/"。

申请的需要 @in@out

点画中的反应性

Stipple实现了响应式模型,确保数据和接口的同步。 宏 @in@out 将元素集成到此模型中。 @in 将数据绑定到界面元素(滑块),允许用户更改它们。 @out 状态改变时更新输出数据(图形)。 使用标准广告时,例如 m11 = 1.0 接口元素失去与值的连接,不包括它们的修改; @onchange 它没有响应,因为点画不跟踪这些数据;交互性被破坏,因为值没有传递给JavaScript。

自定义类型的替代方案

根据Stipple文档(Types of variables in Stipple),有可能使用 mutable struct 作为反应变量:

mutable struct MatrixState
    m11::Float64
    m12::Float64
    m21::Float64
    m22::Float64
end

@app begin
    @in state = MatrixState(1.0, 0.0, 0.0, 1.0)
    @out plot_data = initial_plot_data
    @out plot_layout = plot_layout

    @onchange state begin
        plot_data = create_plot_data(state.m11, state.m12, state.m21, state.m22)
    end
end

结构 MatrixState 通过 @in,这允许Stipple跟踪其字段的变化。 但是,在此应用中,优先考虑单个变量(@in m11 等。),因为这通过单独的滑块简化了矩阵元素的管理。

没有 @in@out 矩阵元素在Julia中保持隔离,而不与界面交互。 响应式宏或 mutable struct@in/@out 它们提供必要的通信,方法的选择取决于数据结构和接口要求。


要启动应用程序,我们将使用[以前的статьям]中已经熟悉的应用程序(https://engee.com/community/ru/catalogs/projects/tablo-aeroporta-s-dinamicheskoi-tablitsei-v-genieframework )由建筑:

In [ ]:
using Markdown
cd(@__DIR__)

app_url = string(engee.genie.start(string(@__DIR__,"/app.jl")))

Markdown.parse(match(r"'(https?://[^']+)'",app_url)[1])
Out[0]:

using Markdown
cd(@DIR)

app_url = string(engee.genie.start(string(@DIR,"/app.jl")))

Markdown.parse(match(r"'(https?://[^']+)'",app_url)[1])

结论

本文演示了如何使用GenieFramework创建一个可视化线性算子动作的交互式应用程序。 响应式宏 @in@out 我们提供了接口和逻辑之间的链接,以及使用的可能性 mutable struct 为复杂场景提供灵活性。 该应用程序突出了Genie开发教育工具的能力。---