Skip to content

Commit

Permalink
Merge pull request #61 from KristofferC/tp/options-reorg
Browse files Browse the repository at this point in the history
Options cleanup.
  • Loading branch information
tpapp authored Feb 13, 2018
2 parents 43458c7 + 79be436 commit dd09bcb
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 127 deletions.
16 changes: 1 addition & 15 deletions src/PGFPlotsX.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export @pgf, print_tex, latexengine, latexengine!, CUSTOM_PREAMBLE, push_preambl

const DEBUG = haskey(ENV, "PGFPLOTSX_DEBUG")
const CUSTOM_PREAMBLE_PATH = joinpath(@__DIR__, "..", "deps", "custom_preamble.tex")
const PGFOption = Union{Pair, String, OrderedDict}
const AbstractDict = Union{Dict, OrderedDict}

if !isfile(joinpath(@__DIR__, "..", "deps", "deps.jl"))
Expand All @@ -31,20 +30,7 @@ include("../deps/deps.jl")
print_tex(io::IO, a, b) = print_tex(io, a)
print_tex(a) = print_tex(STDOUT, a)

# TODO: Make OptionType a trait somehow?
abstract type OptionType end

Base.getindex(a::OptionType, s::String) = a.options[s]
Base.setindex!(a::OptionType, v, s::String) = (a.options[s] = v; a)
Base.delete!(a::OptionType, s::String) = (delete!(a.options, s); a)
Base.copy(a::OptionType) = deepcopy(a)
function Base.merge!(a::OptionType, d::OrderedDict)
for (k, v) in d
a[k] = v
end
return a
end

include("options.jl")
include("utilities.jl")

function print_tex(io_main::IO, str::AbstractString)
Expand Down
26 changes: 12 additions & 14 deletions src/axiselements.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

struct Plot <: AxisElement
elements::AbstractVector{Any}
options::OrderedDict{Any, Any}
options::Options
label
incremental::Bool
_3d::Bool
Expand All @@ -21,10 +21,10 @@ function Plot3(element::Vector, args::Vararg{PGFOption}; incremental = true, lab
Plot(element, dictify(args), label, incremental, true)
end

Plot(options::OrderedDict, element; kwargs...) = Plot(element, options; kwargs...)
Plot(options::Options, element; kwargs...) = Plot(element, options; kwargs...)
Plot(element, args...; kwargs...) = Plot([element], args...; kwargs...)
Plot3(element, args...; kwargs...) = Plot3([element], args...; kwargs...)
Plot3(options::OrderedDict, element; kwargs...) = Plot3(element, options; kwargs...)
Plot3(options::Options, element; kwargs...) = Plot3(element, options; kwargs...)

function save(filename::String, plot::Plot; kwargs...)
save(filename, Axis(plot); kwargs...)
Expand Down Expand Up @@ -325,11 +325,11 @@ expand_scanlines(v::Vector{Int}, _) = v
expand_scanlines(itr, _) = collect(Int, itr)

struct Table <: OptionType
options::OrderedDict{Any, Any}
options::Options
data::AbstractMatrix
colnames::Union{Void, Vector{String}}
scanlines::AbstractVector{Int}
function Table(options::OrderedDict{Any, Any}, data::AbstractMatrix,
function Table(options::Options, data::AbstractMatrix,
colnames::Union{Void, Vector{String}},
scanlines::AbstractVector{Int})
nrow, ncol = size(data)
Expand All @@ -356,8 +356,7 @@ this can be used for skipping coordinates or implicitly defining the dimensions
of a matrix for `surf` and `mesh` plots. They are expanded using
[`expand_scanlines`](@ref).
"""
function Table(options::OrderedDict{Any, Any}, data::AbstractMatrix,
colnames, scanlines)
function Table(options::Options, data::AbstractMatrix, colnames, scanlines)
Table(options, data,
colnames nothing ? colnames : collect(String, colnames),
expand_scanlines(scanlines, size(data, 1)))
Expand Down Expand Up @@ -444,11 +443,11 @@ end
[`table_fields`](@ref) is used to convert the arguments after options. See its
methods for possible conversions.
"""
Table(options::OrderedDict{Any, Any}, args...; kwargs...) =
Table(options::Options, args...; kwargs...) =
Table(options, table_fields(args...; kwargs...)...)

Table(args...; kwargs...) =
Table(OrderedDict{Any, Any}(), table_fields(args...; kwargs...)...)
Table(Options(), table_fields(args...; kwargs...)...)

function print_tex(io_main::IO, table::Table)
@unpack options, data, colnames, scanlines = table
Expand Down Expand Up @@ -489,7 +488,7 @@ Placeholder for a table for which data is read directly from a file. Use the
[`Table`](@ref) constructor.
"""
struct TableFile <: OptionType
options::OrderedDict{Any, Any}
options::Options
path::AbstractString
end

Expand All @@ -501,10 +500,9 @@ accepted format.
If you don't use an absolute path, it will be converted to one.
"""
Table(options::OrderedDict{Any, Any}, path::AbstractString) =
TableFile(options, abspath(path))
Table(options::Options, path::AbstractString) = TableFile(options, abspath(path))

Table(path::AbstractString) = Table(OrderedDict{Any, Any}(), path)
Table(path::AbstractString) = Table(Options(), path)

function print_tex(io_main::IO, tablefile::TableFile)
@unpack options, path = tablefile
Expand All @@ -521,7 +519,7 @@ end

struct Graphics <: OptionType
filename::String
options::OrderedDict{Any, Any}
options::Options
end

function Graphics(filename::String, args::Vararg{PGFOption})
Expand Down
24 changes: 12 additions & 12 deletions src/axislike.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ function (T::Type{<:AxisLike})(plots::AbstractVector, args::Vararg{PGFOption})
end

(T::Type{<:AxisLike})(plot, args::Vararg{PGFOption}) = T([plot], dictify(args))
(T::Type{<:AxisLike})(options::OrderedDict, element) = T(element, options)
(T::Type{<:AxisLike})(options::Options, element) = T(element, options)

function (T::Type{<:AxisLike})(args::Vararg{PGFOption})
T([], args...)
Expand Down Expand Up @@ -42,10 +42,10 @@ _in_between(::Any, ::Any) = ""

struct Axis <: AxisLike
plots::Vector{Any}
options::OrderedDict{Any, Any}
options::Options

# get rid of default constructor or ambiguities
Axis(v::Vector, o::OrderedDict{Any, Any}) = new(v, o)
Axis(v::Vector, o::Options) = new(v, o)
end

_tex_name(::Axis) = "axis"
Expand All @@ -56,12 +56,12 @@ _tex_name(::Axis) = "axis"

struct GroupPlot <: AxisLike
plots::Vector{Any}
axisoptions::Vector{OrderedDict{Any, Any}}
options::OrderedDict{Any, Any}
# nextgroupplot::Vector{OrderedDict{Any, Any}} # options for \nextgroupplot
axisoptions::Vector{Options}
options::Options
# nextgroupplot::Vector{Options} # options for \nextgroupplot
# get rid of default constructor or ambiguities
GroupPlot(v::Vector, o::OrderedDict{Any, Any}) = new(convert(Vector{Any}, v), [OrderedDict() for i in 1:length(v)], o)
GroupPlot(o::OrderedDict{Any, Any}) = new(Any[], OrderedDict{Any, Any}[], o)
GroupPlot(v::Vector, o::Options) = new(convert(Vector{Any}, v), [Options() for i in 1:length(v)], o)
GroupPlot(o::Options) = new(Any[], Options[], o)
end

function print_tex(io::IO, v::Vector, gp::GroupPlot)
Expand All @@ -70,7 +70,7 @@ function print_tex(io::IO, v::Vector, gp::GroupPlot)
end
end

Base.push!(gp::GroupPlot, plot) = (push!(gp.plots, plot); push!(gp.axisoptions, OrderedDict()); gp)
Base.push!(gp::GroupPlot, plot) = (push!(gp.plots, plot); push!(gp.axisoptions, Options()); gp)
Base.push!(gp::GroupPlot, plot, args...) = (push!(gp.plots, plot); push!(gp.axisoptions, dictify(args)); gp)

_tex_name(::GroupPlot) = "groupplot"
Expand All @@ -88,10 +88,10 @@ end

struct PolarAxis <: AxisLike
plots::Vector{Any}
options::OrderedDict{Any, Any}
# nextgroupplot::Vector{OrderedDict{Any, Any}} # options for \nextgroupplot
options::Options
# nextgroupplot::Vector{Options} # options for \nextgroupplot
# get rid of default constructor or ambiguities
PolarAxis(v::Vector, o::OrderedDict{Any, Any}) = new(convert(Vector{Any}, v), o)
PolarAxis(v::Vector, o::Options) = new(convert(Vector{Any}, v), o)
end

_tex_name(::PolarAxis) = "polaraxis"
123 changes: 123 additions & 0 deletions src/options.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
"""
Options passed to `pgfplots` for various structures (`table`, `plot`, etc).
Contents emitted in `key = value` form, or `key` when `value ≡ nothing`. Also
see the [`@pgf`](@ref) convenience macro.
"""
const Options = OrderedDict{Any, Any}

function prockey(key)
if isa(key, Symbol) || isa(key, String)
return :($(string(key)) => nothing)
elseif @capture(key, (a_ : b_) | (a_ => b_) | (a_ = b_))
return :($(string(a))=>$b)
end
error("Invalid pgf option $key")
end

function procmap(d)
if @capture(d, f_(xs__))
return :($f($(map(procmap, xs)...)))
elseif !@capture(d, {xs__})
return d
else
return :($(Options)($(map(prockey, xs)...)))
end
end

"""
@pgf { ... }
@pgf some(nested(form({ ... })))
Construct [`Options`](@ref) from comma-delimited `key` (without value),
`key = value`, `key : value`, or `key => value` pairs enclosed in `{ ... }`,
anywhere in the expression.
Multi-word keys need to be either quoted, or written with underscores replacing
spaces.
```julia
@pgf {
"only marks",
mark_size = "0.6pt",
mark = "o",
color => "black",
}
```
"""
macro pgf(ex)
esc(prewalk(procmap, ex))
end

"""
Types also accepted as options.
"""
const PGFOption = Union{Pair, String, Options}

# TODO: Make OptionType a trait somehow?
"""
Subtypes have an `options::Options` field.
"""
abstract type OptionType end

Base.getindex(a::OptionType, s::String) = a.options[s]
Base.setindex!(a::OptionType, v, s::String) = (a.options[s] = v; a)
Base.delete!(a::OptionType, s::String) = (delete!(a.options, s); a)
Base.copy(a::OptionType) = deepcopy(a)
function Base.merge!(a::OptionType, d::Options)
for (k, v) in d
a[k] = v
end
return a
end

function print_options(io::IO, options::Options)
print(io, "[")
print_opt(io, options)
print(io, "]\n")
end

accum_opt!(d::AbstractDict, opt::String) = d[opt] = nothing
accum_opt!(d::AbstractDict, opt::Pair) = d[first(opt)] = last(opt)
function accum_opt!(d::AbstractDict, opt::AbstractDict)
for (k, v) in opt
d[k] = v
end
end

function dictify(args)
d = Options()
for arg in args
accum_opt!(d, arg)
end
return d
end

function print_opt(io::IO, d::AbstractDict)
replace_underline(x) = x
replace_underline(x::Union{String, Symbol}) = replace(string(x), "_", " ")
for (i, (k, v)) in enumerate(d)
print(io, replace_underline(k))
if v != nothing
print(io, "={")
print_opt(io, v)
print(io, "}")
end
if i != length(d)
print(io, ", ")
end
end
end

print_opt(io::IO, s) = print(io, s)
print_opt(io::IO, v::Vector) = print(io, join(v, ","))

function print_opt(io::IO, t::Tuple)
length(t) == 0 && return
for i in 1:length(t)
i != 1 && print(io, "{")
print_opt(io, t[i])
i != length(t) && print(io, "}")
end
end
2 changes: 1 addition & 1 deletion src/tikzdocument.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ end
TikzDocument(; preamble = String[]) = TikzDocument([], preamble)
TikzDocument(element::Vector) = TikzDocument(element, String[])
TikzDocument(element, args...) = TikzDocument([element], args...)
TikzDocument(options::OrderedDict, element) = TikzDocument(element, options)
TikzDocument(options::Options, element) = TikzDocument(element, options)

Base.push!(td::TikzDocument, v) = (push!(td.elements, v); td)
push_preamble!(td::TikzDocument, v) = (push!(td.preamble, v); td)
Expand Down
4 changes: 2 additions & 2 deletions src/tikzpicture.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ const TikzElementOrStr = Union{TikzElement, String}

struct TikzPicture <: OptionType
elements::Vector{TikzElementOrStr} # Plots, nodes etc
options::OrderedDict{Any, Any}
options::Options
end

function TikzPicture(options::Vararg{PGFOption})
TikzPicture(TikzElementOrStr[], dictify(options))
end

TikzPicture(element::TikzElementOrStr, args...) = TikzPicture([element], args...)
TikzPicture(options::OrderedDict, element::TikzElementOrStr) = TikzPicture(element, options)
TikzPicture(options::Options, element::TikzElementOrStr) = TikzPicture(element, options)

function TikzPicture(elements::Vector, options::Vararg{PGFOption})
TikzPicture(convert(Vector{TikzElementOrStr}, elements), dictify(options))
Expand Down
Loading

0 comments on commit dd09bcb

Please sign in to comment.