by Simon DanischSep 16 2019
Julia & graphic enthusiast. Interested in high performance computing, GPUs and machine learning.

# Christmas at Nextjournal

30.6s
Julia
using GLMakie, AbstractPlotting
using ImageTransformations, GeometryTypes
using FileIO, LinearAlgebra, Colors

function initflake(area)
pos = minimum(area) .+ (rand(Point2f0) .* widths(area))
angle = -rand() * 0.1 + 0.5pi - 0.1/2
(pos, angle)
end

function simulate!(positions, angles, bounds)
wh = widths(bounds)
p10 = (wh[2] / 10)
spawn = FRect2D((0f0, wh[2] - p10), (wh[1], p10))
inc = rand() + 0.5
for i in 1:length(positions)
pos, angle = positions[i], angles[i]

pos = pos .- inc .* Point(cos(angle), sin(angle))
angle -= (rand() * 2.0 - 1.0) / 100.0
if !(pos in bounds)
pos, angle = initflake(spawn)
end
positions[i], angles[i] = pos, angle
end
end

N = 10_000
angles, positions = zeros(Float32, N), zeros(Point2f0, N)
sz = (880, 587)
bounds = FRect2D((0, 0), sz)
for i in 1:N
positions[i], angles[i] = initflake(bounds)
end
scene = Scene(
show_axis = false, scale_plot = false, resolution = sz,
camera = campixel!, backgroundcolor = :gray,
)

image!(0 .. size(bgimg, 2), 0 .. size(bgimg, 1), rotr90(bgimg), fxaa = true)
ms = rand(1.0:0.001:6, N)
colors = fill(RGBAf0(0.96, 0.96, 0.96, 0.5), N)
scatter!(
positions, rotation = angles,
color = colors, marker = '❄',
markersize = 6
)

snow = last(scene)

function simulate_snow!(positions, angles, bounds, snow)
sun = Point2f0(300, 460)
simulate!(positions, angles, bounds)
snow[1] = positions
snow.rotation = angles
for (i, pos) in enumerate(positions)
sundist = norm(sun - pos)
if sundist <= 150.0
colors[i] = RGBAf0(1,0.95,0.8 + rand() * 0.1, 1 - sqrt(sundist / 400) + rand() * 0.4)
else
colors[i] = RGBAf0(0.96, 0.96, 0.96, 0.5)
end
end
snow.color = colors
end
function generate_tree(nballs, pos, scale)
w, h = size(tree) ./ 2
treemesh = GLNormalUVMesh(SimpleRectangle(-h, -w, 2h, 2w), size(tree))
mesh!(treemesh, color = tree, shading = false, transparency = true)

tree_p = last(scene)
rotate!(tree_p, -pi)
scale!(tree_p, scale, scale, 1.0)
translate!(tree_p, (pos .+ (w, h))..., 1)

verts, uvs = vertices(treemesh), texturecoordinates(treemesh)
vert_orig = copy(verts)

extr = extrema(vert_orig)
min_z, max_z = getindex.(extr, 2)
function move_tree!(t)
for (j, v) in enumerate(vert_orig)
mov = sin(t) * 15
z01 = 1 - sqrt((v[2] - min_z) ./ (max_z - min_z))
treemesh.vertices[j] = v .+ Point3f0(((z01) .* mov), 0, 0)
end
tree_p[1] = treemesh
end
end

movtree1! = generate_tree(60, (50, 160), 1.5)
movtree2! = generate_tree(50, (600, 140), 1.2)

using FreeTypeAbstraction
using AbstractPlotting: layout_text
font = newface(MerryChristmasFlake.ttf)
str = "*Merry0Christmas+"
tn = length(str)
ts = 100
tn/2 * ts

pos, scale = layout_text(str, Point2f0((sz[1] / 2) + 20, 450.0), 100, font, (:center, :center), Quaternionf0(0, 0, 0, 1), Mat4f0(I))

p1 = first(pos)
pl = last(pos)
tw = pl[1] - p1[1]
rot = map(pos) do p
a = ((p[1] - p1[1]) / tw)
-(a * 2.0 - 1.0) .* 0.5
end
map!(pos, pos) do p
xpi = ((p[1] - p1[1]) / tw) * pi
Point3f0(p[1], p[2] + 50 * sin(xpi), 1)
end

text!(
"*Merry0Christmas+",
font = font, textsize = scale,
position = pos, rotation = rot,
color = :white,
)

record(scene, "/results/christmas.gif", LinRange(0, 2pi, 200)) do t
movtree1!(t)
movtree2!(t)
simulate_snow!(positions, angles, bounds, snow)
sleep(1/60)
end;
MerryChristmasFlake.ttf