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,
)

bgimg = load(
winter-landscapes-20.jpg
) 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 tree = restrict(load(
Fd-8mI-go2XGSZzi2v65x29_bjE.png
)) 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