Created
April 10, 2016 19:53
-
-
Save 0x09/c0d6e5bc7bc37d47fccdd9117bfdebcf to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# © 0x09.net MIT license | |
""" | |
simpleplot(function, dims, domain, range [, sub]; flags = []) | |
Plot samples from a continuous function onto a matrix | |
# Arguments | |
* `sub::Real`: Sampling interval in pixels | |
* `flags::Array{Symbol}`: | |
* `:fill`: fill area under graph | |
* `:open_interval`: graph draws `[ range[0], range[1] )` | |
* `:half_sample`: shift samples by half pixel | |
# Examples: | |
```julia | |
julia> simpleplot(x->sin(x), (800, 200), (-π,π), (-1,1)) |> imageplot | |
julia> simpleplot(x->(sin(x),cos(x),tan(x)), (800, 200), (0,6π), (-2,2)) |> imageplot | |
julia> simpleplot(x->[sin(x*n)/(x*n) for n=1:3], (1200, 300), (0,10π), (-0.25,1), flags=[:fill]) |> _->imageplot | |
``` | |
Example output: http://imgur.com/a/fJNdI | |
""" | |
function simpleplot(f::Function, dims::Tuple{Int,Int}, domain::Tuple{Real,Real}, range::Tuple{Real,Real}, sub::Real = 1//20; flags::Vector{Symbol} = Symbol[]) | |
shift = :half_sample in flags ? 0.5 : 0 | |
incl = :open_interval in flags ? 0 : 1 | |
fill = :fill in flags | |
plot = zeros(dims[1]+2, dims[2]+2) | |
width = -(-(domain...)) | |
height = -(-(range...)) | |
zero = floor(Integer, (dims[2]-1) + range[1]*(dims[2]-incl)/height) + 2 | |
for x = 1:sub:dims[1]+1 | |
xval = (x-(2-shift))*width/(dims[1]-incl) + domain[1] | |
yvals = f(xval) | |
# if f produces a tuple or array, stack plots depthwise | |
nplots = length(yvals) | |
if nplots > size(plot,3) | |
plot = cat(3, plot, zeros(dims[1]+2, dims[2]+2, nplots-1)) | |
end | |
xfloor = floor(Integer, x) | |
for z=1:nplots | |
yval = yvals[z] | |
# produce NaNs in f() for discontinuities | |
isnan(yval) && continue | |
y = ((dims[2]-1) - (yval-range[1])*(dims[2]-incl)/height) + (2-shift) | |
if fill | |
y = clamp(y, 1+shift, prevfloat(dims[2]+2.0)) | |
yfloor = floor(Integer, y) | |
if yval < 0 | |
cap = y-yfloor | |
cap_y = 1+yfloor | |
bar = colon(zero+1, cap_y-1) | |
else | |
cap = 1+yfloor-y | |
cap_y = yfloor | |
bar = colon(1+cap_y, zero) | |
end | |
plot[xfloor:1+xfloor, cap_y, z] += [1+xfloor-x, x-xfloor] .* cap | |
plot[xfloor:1+xfloor, bar, z] .+= [1+xfloor-x, x-xfloor] | |
elseif 1+shift <= y < dims[2]+2 | |
yfloor = floor(Integer, y) | |
plot[xfloor:1+xfloor, yfloor:1+yfloor, z] += | |
[1+xfloor-x, x-xfloor] .* [1+yfloor-y y-yfloor] | |
end | |
end | |
end | |
return plot[2:end-1, 2:end-1, :]*sub | |
end | |
""" | |
simpleplot(array, height, range [, scale]; flags = []) | |
Plot an array of samples | |
# Arguments | |
* `scale`: how many output samples correspond to 1 input sample, i.e. when array is interpolated | |
# Examples: | |
The following are equivalent: | |
```julia | |
julia> simpleplot(x->x, (100,100), (0,100), (0,100), 1) | |
julia> simpleplot([0:100], 100, (0,100), 1) | |
julia> simpleplot([0:100], 100) | |
``` | |
```julia | |
julia> simpleplot(x->x, (100,100), (0,100), (0,100), 1//10) | |
julia> simpleplot([0:1//10:100], 100, (0,100), 1//10) | |
``` | |
--- | |
""" | |
# This is a bit backwards but to plot just an array we call simpleplot with a function representing the array | |
function simpleplot{T<:Number}(a::Array{T}, height::Int, range::Tuple{Real,Real} = (minimum(a),maximum(a)+1), scale::Real = 1; flags::Vector{Symbol} = Symbol[]) | |
len = ceil(Integer, size(a,1)*scale) | |
f = x->( | |
x /= scale; | |
isinteger(x) && 0 <= x < size(a,1) ? a[convert(Integer,x)+1,:] : NaN | |
) | |
simpleplot(f, (len,height), (0,len-1), range, scale; flags = flags) | |
end | |
#discontinuity-free cutoffs from http://entropymine.com/imageworsener/srgbformula/ | |
srgbenc{T<:Number}(x::T) = x ≤ 0.00313066844250063 ? 12.92x : 1.055x^(1/2.4) - 0.055 | |
srgbenc{T<:Number}(x::Array{T}) = reshape([srgbenc(i) for i=x],size(x)) #faster than map | |
using Images | |
""" | |
imageplot(plot[, alpha, srgb]) | |
Convenience function for creating an Images.jl image out of a plot | |
# Arguments | |
* `alpha::Bool`: Make background transparent | |
* `srgb::Bool`: sRGB encode image | |
""" | |
function imageplot(plot::Array{Float64,3}, alpha::Bool = true, srgb::Bool = false) | |
plot = clamp(plot,0,1) | |
plot = srgb ? srgbenc(plot) : plot | |
planes = size(plot,3) | |
if planes == 1 && alpha | |
plot = cat(3, plot, plot, plot) #faster than repeat | |
elseif planes == 2 | |
plot = cat(3, plot, zeros(size(plot,1,2))) | |
elseif planes > 3 | |
error("Z too large to image: $planes") | |
end | |
if alpha | |
plot = cat(3, plot, maximum(plot, 3)) | |
colorim(plot, "RGBA") | |
elseif planes > 1 | |
colorim(plot) | |
else | |
grayim(plot) | |
end | |
end | |
# plotting in 22 LOC, purely for show | |
# equivalent to simpleplot(..., flags = [:open_interval]) | |
function simplerplot(f::Function, dims::Tuple{Int,Int}, domain::Tuple{Real,Real}, range::Tuple{Real,Real}, sub::Real = 1//20) | |
plot = zeros(dims[1]+2, dims[2]+2) | |
width = -(-(domain...)) | |
height = -(-(range...)) | |
zero = floor(Integer, (dims[2]-1) + range[1]*dims[2]/height) + 2 | |
for x = 1:sub:dims[1]+1 | |
xval = (x-2)*width/dims[1] + domain[1] | |
yval = f(xval) | |
isnan(yval) && continue | |
y = ((dims[2]-1) - (yval-range[1])*dims[2]/height) + 2 | |
if 1 <= y < dims[2]+2 | |
xfloor = floor(Integer, x) | |
yfloor = floor(Integer, y) | |
plot[xfloor:1+xfloor, yfloor:1+yfloor] += | |
[1+xfloor-x, x-xfloor] .* [1+yfloor-y y-yfloor] | |
end | |
end | |
return plot[2:end-1, 2:end-1]*sub | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment