Skip to content

Instantly share code, notes, and snippets.

@0x09
Created April 10, 2016 19:53
Show Gist options
  • Save 0x09/c0d6e5bc7bc37d47fccdd9117bfdebcf to your computer and use it in GitHub Desktop.
Save 0x09/c0d6e5bc7bc37d47fccdd9117bfdebcf to your computer and use it in GitHub Desktop.
# © 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