Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Visualzing digits
using Luxor, ColorSchemes
using UnicodeFun
using Primes
struct PNGScene
opts::Dict{Symbol, Any}
end
function get_coord(val, radius)
θ = 2π*0.1*val
return Point(
radius*sin(θ),
-radius*cos(θ),
)
end
function draw_background(scene, framenumber)
background("black")
radius = scene.opts[:radius]
show_dots = scene.opts[:show_dots]
show_dots && (radius += 20)
show_bars = scene.opts[:show_bars]
show_bars && (radius += 5)
colors = scene.opts[:colors]
center_text = scene.opts[:center_text]
setline(10)
fontsize(20)
for i in 0:9
from = get_coord(i, radius)
to = get_coord(i+1, radius)
sethue(colors[i+1])
θ = 2π*0.1*i+0.1*π
mid = Point(
radius*sin(θ),
-radius*cos(θ),
)
if show_bars
label(string(i), -π/2+θ, mid; offset=35)
else
label(string(i), -π/2+θ, mid; offset=15)
end
move(from)
arc2r(O, from, to, :stroke)
end
sethue("white")
fontsize(60)
text(center_text, Point(-2, 0), valign=:middle, halign=:center)
end
function draw_bars(scene, framenumber)
next_digit_mat = scene.opts[:next_digit_mat]
next_digit_mat .= 0
digits = scene.opts[:digits]
colors = scene.opts[:colors]
max_digits = scene.opts[:max_digits]
radius = scene.opts[:radius]
show_dots = scene.opts[:show_dots]
show_dots && (radius += 20)
show_bars = scene.opts[:show_bars]
show_bars && (radius += 8)
last_i = min(framenumber, max_digits-1)
multiplier = 1000
for i=1:last_i
from_val = digits[i]
to_val = digits[i+1]
next_digit_mat[from_val+1, to_val+1] += 1
ndm = next_digit_mat[from_val+1, to_val+1]
sethue(colors[to_val+1])
θ = 2π*0.1*(from_val+to_val/10)
height = ndm/max_digits*multiplier
bl = Point((radius+5)*sin(θ), -(radius+5)*cos(θ))
tl = Point((radius+5+height)*sin(θ), -(radius+5+height)*cos(θ))
θ = 2π*0.1*(from_val+(to_val+1)/10)
tr = Point((radius+5+height)*sin(θ), -(radius+5+height)*cos(θ))
br = Point((radius+5)*sin(θ), -(radius+5)*cos(θ))
poly([bl,tl,tr,br,bl], :fill)
end
end
function dig_line(scene, framenumber)
radius = scene.opts[:radius]
colors = scene.opts[:colors]
center_text = scene.opts[:center_text]
bezier_radius = scene.opts[:bezier_radius]
max_digits = scene.opts[:max_digits]
digits = scene.opts[:digits]
setline(0.1)
for i in 1:min(framenumber, max_digits-1)
from_val = digits[i]
to_val = digits[i+1]
f = from_val+(i-1)/max_digits
t = to_val+i/max_digits
from = get_coord(f, radius)
to = get_coord(t, radius)
# get the correct mid point for example for 0-9 it should be 9.5 and not 4.5
mid_val = (f+t)/2
mid_control = get_coord(mid_val, bezier_radius)
if abs(f-t) >= 5
mid_control = get_coord(mid_val+5, bezier_radius)
end
pts = Point[from, mid_control, mid_control, to]
bezpath = BezierPathSegment(pts...)
# reverse the color to see where it is going
setblend(blend(from, to, colors[to_val+1], colors[from_val+1]))
drawbezierpath(bezpath, :stroke, close=false)
end
end
function draw_dots(scene, framenumber)
radius = scene.opts[:radius]
colors = scene.opts[:colors]
center_text = scene.opts[:center_text]
bezier_radius = scene.opts[:bezier_radius]
max_digits = scene.opts[:max_digits]
digits = scene.opts[:digits]
current_len = 1
for i in 1:min(framenumber, max_digits-1)
from_val = digits[i]
to_val = digits[i+1]
if from_val == to_val
current_len += 1
continue
end
current_len == 1 && continue
f = from_val+(i-1)/max_digits
from = get_coord(f, radius + 10)
sethue(colors[to_val+1])
circle(from, current_len, :fill)
current_len = 1
end
end
function viz(;radius=250, bezier_radius=50, colors=ColorSchemes.rainbow[7:end],
max_digits=1000, show_dots=false, show_bars=false, number=:pi, anim=true, width=500, height=500,
fname="testing", show_lines=true
)
center_text = ""
if number isa Symbol
if number == :primes
println("You have chosen to visualize the last digit of primes.")
println("Be aware that max_digits now means the largest number.")
digits_arr = [digits(x)[1] for x in primes(max_digits)]
max_digits = length(digits_arr)
else
center_text = to_latex("\\$number")
digits_arr = setprecision(BigFloat, Int(ceil(log2(10) * max_digits+10))) do
if number == :pi
return parse.(Int, collect(string(BigFloat(pi))[3:max_digits+2]))
elseif number == :tau
return parse.(Int, collect(string(2*BigFloat(pi))[3:max_digits+2]))
end
end
end
elseif number isa Integer
digits_arr = reverse(digits(number))
elseif eltype(number) <: Integer
digits_arr = number
else
throw(DomainError(number, "$number should be either :pi, :tau, :primes or an integer"))
end
args = Dict(:radius => radius,
:bezier_radius => bezier_radius,
:colors => colors, :max_digits => max_digits,
:digits => digits_arr, :center_text => center_text,
:show_dots => show_dots,
:show_bars => show_bars, :next_digit_mat => zeros(Int, (10,10))
)
if anim
movie = Movie(width, height, "test")
scenes = [
Scene(movie, draw_background, 0:max_digits+50, optarg=args),
]
if show_lines
push!(scenes, Scene(movie, dig_line, 0:max_digits+50, optarg=args))
end
if show_dots
push!(scenes, Scene(movie, draw_dots, 0:max_digits+50, optarg=args))
end
if show_bars
push!(scenes, Scene(movie, draw_bars, 0:max_digits+50, optarg=args))
end
animate(movie, scenes,
creategif = true,
pathname = "./$fname.gif"
)
else
scene = PNGScene(args)
@png begin
draw_background(scene, max_digits)
if show_lines
dig_line(scene, max_digits)
end
if show_dots
draw_dots(scene, max_digits+50)
end
if show_bars
draw_bars(scene, max_digits+50)
end
end 700 700 "./$fname.png"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.