Julia & graphic enthusiast. Interested in high performance computing, GPUs and machine learning.

Christmas at Nextjournal

27.0s
Language: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(Image)
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(Image))
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