Engee 文档

食谱

该页面正在翻译中。

食谱允许您扩展 马基 使用您自己的自定义类型和绘图命令。

请注意,如果您是软件包开发人员,则可以在不添加所有配方的情况下添加配方。 麦琪jl 作为依赖。 相反,您可以使用 马基科 包,这是一个轻量级的包,它提供了所有必要的元素来创建一个配方,如 @食谱 宏, 转换/转换convert_attribute 函数,甚至一些基本的绘图类型定义。

有两种类型的食谱:

  • _type recipes_定义从用户定义类型到现有绘图类型的简单映射

  • _Full recipes_定义新的自定义绘图函数。

型配方

类型配方大多只是从一种类型或一组输入参数类型的转换,但Makie不知道,到Makie已经可以处理的另一个类型。

这是尝试在Makie中进行转换的顺序逻辑:

  • 派遣 convert_arguments(::PlotType,args...)

  • 如果没有找到匹配的方法,通过 conversion_trait(::PlotType)

  • 派遣 convert_arguments(::ConversionTrait,args...)

  • 如果没有找到匹配的方法,请尝试递归地转换每个参数 转换_single_argument 直到每种类型不再改变

  • 派遣 convert_arguments(::PlotType,converted_args...)

  • 如果找不到方法,则失败

多个参数转换与 转换/转换

绘制一个 例如,可以通过转换为任何现有绘图类型的点矢量来定义:

Makie.convert_arguments(::Type{<: AbstractPlot}, x::Circle) = (decompose(Point2f, x),)

# or if you picked up MakieCore as a light-weight recipe system dependency
MakieCore.convert_arguments(::Type{<: AbstractPlot}, x::Circle) = (decompose(Point2f, x),)

警告 转换/转换 必须始终返回一个元组。

为每种绘图类型定义转换可能没有意义,因此可以将转换限制为绘图类型的子集,例如仅用于散点图:

Makie.convert_arguments(P::Type{<:Scatter}, ::MyType) = convert_arguments(P, rand(10))

通过转换特性,可以更轻松地为一组具有相同特性的绘图类型定义行为。 以点为基础的 例如适用于 散点,散点, 线条 等。 预定义的特征是 NoConversion缧, 以点为基础的, CellGrid<:基于网格, VertexGrid<:GridBased, 像样的,像样的, [医]容积式采样/采样. 他们都继承自 [医]转换,有时是间接的。

Makie.convert_arguments(::PointBased, ::MyType) = ...

也可以将多个参数转换在一起。

Makie.convert_arguments(::Type{<:Scatter}, x::MyType, y::MyOtherType) = ...

或者,您可以定义默认绘图类型,以便 情节(x::MyType) 将始终绘制为例如表面图:

plottype(::MyType) = Surface

单参数转换 转换_single_argument

一些Makie未知的类型可以转换为其他类型, 转换/转换 方法是可用的。 这是用 转换_single_argument.

例如, [医]抽象真实的s和 失踪s通常可以安全地转换为 漂浮物32 数组与 s代替 失踪s.

之间的区别 转换_single_argument转换/转换 具有单个参数的是,前者可以应用于任何签名的任何参数,而后者仅匹配单参数签名。

完整的食谱与 @食谱

完整的食谱分为两部分。 首先是绘图类型名称,例如 我的计划,然后使用 @食谱 宏。

二是至少一个自定义 阴谋! 的方法 我的计划 它使用其他现有的绘图函数创建一个实际的可视化。

我们用一个例子来说明这是如何工作的:

@recipe(MyPlot, x, y, z) do scene
    Theme(
        plot_color = :red
    )
end

这个宏扩展到几件事。 首先是类型定义:

const MyPlot{ArgTypes} = Plot{myplot, ArgTypes}

的类型参数 情节 包含函数 我的计划 而不是例如符号 我的计划. 这样的映射从 我的计划我的计划 更安全、更简单。 自动定义以下签名以使 我的计划 好用:

myplot(args...; kw_args...) = ...
myplot!(args...; kw_args...) = ...

专业的 论证名称 如果你有一个参数列表,就会发出 (x,y,z) 提供给配方宏:

argument_names(::Type{<: MyPlot}) = (:x, :y, :z)

这是可选的,但它将允许使用 plot_object的。x 从调用中获取第一个参数 plot_object=myplot(兰德(10),兰德(10),兰德(10)) 例如。

或者,您可以随时获取 ith参数使用 plot_对象[i],如果你漏掉 (x,y,z),默认版本的 论证名称 将提供 plot_object的。arg1 等。

在正文中给出的主题 @食谱 调用被插入到 默认值 它将主题插入到任何绘制的场景中 我的计划:

function default_theme(scene, ::MyPlot)
    Theme(
        plot_color => :red
    )
end

此外,您可以通过定义来控制绘图使用哪种轴

Makie.args_preferred_axis(::Type{<: MyPlot}, x, y, z) =  Makie.LScene

Makie.preferred_axis_type(plot::MyPlot) = Makie.LScene # if you need the entire plot object as information

注意Makie默认为 麦琪轴心,轴心 作为优选的轴。

作为定义的第二部分 我的计划,你应该实现的实际绘图 我的计划 专业对象 阴谋!:

function Makie.plot!(myplot::MyPlot)
    # normal plotting code, building on any previously defined recipes
    # or atomic plotting operations, and adding to the combined `myplot`:
    lines!(myplot, rand(10), color = myplot.plot_color)
    plot!(myplot, myplot.x, myplot.y, myplot.z)
    myplot
end

可以在此处添加特化,具体取决于提供给的参数_types_ 我的计划. 例如,对 我的图(a) 何时 a 是浮点数的3d数组:

const MyVolume = MyPlot{Tuple{<:AbstractArray{<: AbstractFloat, 3}}}
argument_names(::Type{<: MyVolume}) = (:volume,) # again, optional
function plot!(plot::MyVolume)
    # plot a volume with a colormap going from fully transparent to plot_color
    volume!(plot, plot[:volume], colormap = :transparent => plot[:plot_color])
    plot
end

例子:股票图表

假设我们想用经典的开盘/收盘和低/高组合来可视化股票价值。 在这个例子中,我们将创建一个特殊的类型来保存这些信息,以及一个可以绘制这种类型的配方。

首先,我们创建一个结构来保存某一天的股票价值:

using CairoMakie

struct StockValue{T<:Real}
    open::T
    close::T
    high::T
    low::T
end

现在我们创建一个新的绘图类型,称为 存货图. 该 做场景 闭包只是一个返回我们默认属性的函数,在这种情况下,它们将股票颜色为红色,股票颜色为绿色。

@recipe(StockChart) do scene
    Attributes(
        downcolor = :red,
        upcolor = :green,
    )
end

然后我们进入食谱的肉,这实际上是创建一个情节方法。 我们需要重载一个特定的方法 麦琪阴谋! 作为它的论点,它有一个新的子类型 存货图 情节类型。 该类型的类型参数是一个元组,描述此方法应该为其工作的参数类型。

请注意,我们在 阴谋! 方法,我们可以通过索引到 存货图,由Makie自动转换为可观察值。

这意味着我们必须以动态的方式构造我们的绘图函数,以便每当输入可观察值发生变化时,它就会自我更新。 这可能比您可能从其他绘图包中知道的食谱有点棘手,这些绘图包主要产生静态图。

function Makie.plot!(
        sc::StockChart{<:Tuple{AbstractVector{<:Real}, AbstractVector{<:StockValue}}})

    # our first argument is an observable of parametric type AbstractVector{<:Real}
    times = sc[1]
    # our second argument is an observable of parametric type AbstractVector{<:StockValue}}
    stockvalues = sc[2]

    # we predefine a couple of observables for the linesegments
    # and barplots we need to draw
    # this is necessary because in Makie we want every recipe to be interactively updateable
    # and therefore need to connect the observable machinery to do so
    linesegs = Observable(Point2f[])
    bar_froms = Observable(Float32[])
    bar_tos = Observable(Float32[])
    colors = Observable(Bool[])

    #这个帮助函数将更新我们的可观察对象
    #无论何时 `时代` 或 `股票价值` 改变
    函数update_plot(times,stockvalues)
        颜色[]

        #清除observables里面的向量
        空!(linesegs[])
        空!(bar_froms[])
        空!(bar_tos[])
        空!(颜色[])

        #然后用我们更新的值重新填充它们
        for(t,s)in zip(times,stockvalues)
            推!(linesegs[],Point2f(t,s.low))
            推!(linesegs[],Point2f(t,s.high))
            推!(bar_froms[],s.开放)
            推!(bar_tos[],s.关闭)
        结束
        追加!(颜色[],[x.关闭>x.以股票价值为x打开])
        颜色[]=颜色[]
    结束

    #连接 `更新_plot` 所以它被称为每当 `时代`
    #或 `股票价值` 改变
    麦琪可观察的。onany(update_plot,times,stockvalues)

    #然后用第一个手动调用一次 `时代` 和 `股票价值`
    #contents所以我们用正确的值预填充所有observable
    update_plot(times[],stockvalues[])

    #对于颜色,我们只使用布尔值或0和1的向量,它们是
    #根据2元素颜色表着色
    #我们用我们的 `[医]下色` 和 `上颜色;上颜色`
    #我们给出observable元素类型 `任何` 所以当我们改变时它不会出错
    #从符号的颜色,如:红色到不同类型的RGBf(1,0,1)
    colormap=可观察{Any}()
    地图!(colormap,sc。downcolor,sc。upcolor)做dc,uc
        [dc,uc]
    结束

     在最后一步,我们计划进入我们的 `sc` StockChart对象,这意味着
    #我们的新情节是由两个简单的食谱组成的。
    #互相顶
    行距!(sc,linesegs,color=colors,colormap=colormap)
    巴普洛!(sc,times,bar_froms,fillto=bar_tos,color=colors,strokewidth=0,colormap=colormap)

    #最后,我们返回新的图表
    sc
结束

最后,让我们尝试一下,并绘制一些股票: </无翻译>

timestamps = 1:100

# we create some fake stock values in a way that looks pleasing later
startvalue = StockValue(0.0, 0.0, 0.0, 0.0)
stockvalues = foldl(timestamps[2:end], init = [startvalue]) do values, t
    open = last(values).close + 0.3 * randn()
    close = open + randn()
    high = max(open, close) + rand()
    low = min(open, close) - rand()
    push!(values, StockValue(
        open, close, high, low
    ))
end

# now we can use our new recipe
f = Figure()

stockchart(f[1, 1], timestamps, stockvalues)

# and let's try one where we change our default attributes
stockchart(f[2, 1], timestamps, stockvalues,
    downcolor = :purple, upcolor = :orange)
f
ef6e517

作为最后一个例子,让我们假装我们的股票数据是动态进来的,我们想创建一个动画。 如果我们使用observables作为输入参数,然后逐帧更新,这很容易:

timestamps = Observable(collect(1:100))
stocknode = Observable(stockvalues)

fig, ax, sc = stockchart(timestamps, stocknode)

record(fig, "stockchart_animation.mp4", 101:200,
        framerate = 30) do t
    # push a new timestamp without triggering the observable
    push!(timestamps[], t)

    # push a new StockValue without triggering the observable
    old = last(stocknode[])
    open = old.close + 0.3 * randn()
    close = open + randn()
    high = max(open, close) + rand()
    low = min(open, close) - rand()
    new = StockValue(open, close, high, low)
    push!(stocknode[], new)

    # now both timestamps and stocknode are synchronized
    # again and we can trigger one of them by assigning it to itself
    # to update the whole stockcharts plot for the new frame
    stocknode[] = stocknode[]
    # let's also update the axis limits because the plot will grow
    # to the right
    autolimits!(ax)
end

Makie包扩展

有关Makie的包扩展的简单示例,请参阅https://github.com/jkrumbiegel/MakiePkgExtTest。以下文档解释了链接示例中实现的基础知识。

设置您的https://pkgdocs.julialang.org/v1/creating-packages/#Conditional-loading-of-code-in-packages-(Extensions)[包扩展]有 马基 作为依赖,而不是 马基科 或者任何一个Makie的后端。

例如,您必须在主包中定义和导出完整的配方函数:

module SomePackage

export someplot
export someplot!

# functions with no methods
function someplot end
function someplot! end

end # module

然后你的Makie扩展包将添加方法到 某个情节!.

module MakieExtension

使用一些包装
导入SomePackage:someplot,someplot!

麦琪convert_single_argument(v::SomeVector)=v.v

@食谱(SomePlot)做场景
    主题()
结束

功能Makie。阴谋!(p::SomePlot)
    台词!(p,p[1])
    散开!(p,p[1])
    返回p
结束

结束#模块

请参阅上面的链接示例以获取更多功能,例如容纳扩展和 需要。jl,或为绘制尚未有方法的函数提供错误提示,或设置您的 项目。汤姆尔.