Skip to content

Instantly share code, notes, and snippets.

@hsjunnesson
Created January 25, 2024 21:03
Show Gist options
  • Save hsjunnesson/feec8dc08995e4628f09af0b2aa52ff2 to your computer and use it in GitHub Desktop.
Save hsjunnesson/feec8dc08995e4628f09af0b2aa52ff2 to your computer and use it in GitHub Desktop.
poisson.rb
require 'victor'
require 'perlin_noise'
$width = 279
$height = 356
$pen_width = 0.8
$margin_left = 10
$margin_right = 10
$margin_top = 10
$margin_bottom = 30.0
$border = 2
$margin_rect_origin = Vector[$margin_left + $border * 0.5, $margin_top + $border * 0.5]
$margin_rect_size = Vector[$width - $margin_left - $margin_right - $border * 2 * 0.5, $height - $margin_top - $margin_bottom - $border * 2 * 0.5]
svg = Victor::SVG.new width: "100%", height: "100%", viewBox: "0 0 #{$width} #{$height}", style: {
stroke_width: $pen_width,
stroke: "#000",
fill: "none",
background: "white"
}
$seed = 20121018
$noise = Perlin::Noise.new 2, :seed => $seed
$contrast = Perlin::Curve.contrast(Perlin::Curve::CUBIC, 3)
$TWO_PI = 2.0 * Math::PI
def perlin(point)
return $noise[point[0] * 0.02, point[1] * 0.02]
end
def poisson_disk_sampling(width, height, min_dist, new_points_count, max_drift = 0.0)
rand = Random.new($seed)
points = Array.new
cell_size = min_dist / Math.sqrt(2.0)
cols = (width / cell_size).to_i
rows = (height / cell_size).to_i
grid = Hash.new
active_list = Array.new
initial_point = Vector[rand.rand(width.to_f), rand.rand(height.to_f)]
active_list << initial_point
grid_x = (initial_point[0] / cell_size).to_i
grid_y = (initial_point[1] / cell_size).to_i
grid_coord = Vector[grid_x, grid_y]
grid[grid_coord] = initial_point
is_far_enough = lambda do |point|
grid_x = (point[0] / cell_size).to_i
grid_y = (point[1] / cell_size).to_i
(-2..2).each do |i|
(-2..2).each do |j|
neighbor_x = grid_x + i
neighbor_y = grid_y + j
if neighbor_x >= 0 && neighbor_y >= 0 && neighbor_x < cols && neighbor_y < rows
neighbor_coord = Vector[neighbor_x, neighbor_y]
neighbor = grid[neighbor_coord]
if neighbor && (neighbor - point).magnitude() < min_dist
return false
end
end
end
end
return true
end
while not active_list.empty? do
active_index = rand.rand(active_list.size)
active = active_list[active_index]
found = false
(0..new_points_count).each do |n|
angle = rand.rand($TWO_PI)
radius = rand.rand(min_dist..2 * min_dist)
new_point = Vector[
active[0] + radius * Math.cos(angle),
active[1] + radius * Math.sin(angle)]
if new_point[0] >= 0 && new_point[1] >= 0 && new_point[0] < width && new_point[1] < height
grid_x = (new_point[0] / cell_size).to_i
grid_y = (new_point[1] / cell_size).to_i
if grid_x >= 0 && grid_y >= 0 && grid_x < cols && grid_y < rows then
grid_coord = Vector[grid_x, grid_y]
if grid[grid_coord].nil? && is_far_enough.call(new_point)
active_list << new_point
grid[grid_coord] = new_point
points << new_point
found = true
end
end
end
end
if not found
active_list.delete_at(active_index)
end
end
return points
end
points = poisson_disk_sampling($width, $height, 3, 5)
rand = Random.new($seed)
contrast_4 = Perlin::Curve.contrast(Perlin::Curve::CUBIC, 4)
curve = Perlin::Curve.contrast(Perlin::Curve::CUBIC, 3)
def inside_rect(point, rect_origin, rect_size)
point[0] >= rect_origin[0] && point[0] < rect_origin[0] + rect_size[0] &&
point[1] >= rect_origin[1] && point[1] < rect_origin[1] + rect_size[1]
end
points.each do |p|
n = $noise[(p[0] + 200.0) * 0.008, (p[1] + 100.0) * 0.008]
if rand.rand() >= contrast_4.call(n)
line_begin = p
theta = n * Math::PI
offset = Vector[Math.cos(theta), Math.sin(theta)]
line_end = p + offset * curve.call(n) * 10.0
if inside_rect(line_begin, $margin_rect_origin, $margin_rect_size) && inside_rect(line_end, $margin_rect_origin, $margin_rect_size)
svg.line x1: line_begin[0], y1: line_begin[1], x2: line_end[0], y2: line_end[1]
end
end
end
# Border
(0..$border.to_f).step($pen_width * 0.5) { |inset|
svg.rect x: $margin_left + inset, y: $margin_top + inset, width: $width-$margin_left-$margin_right-inset*2, height: $height-$margin_top-$margin_bottom-inset*2, stroke: "black", fill: "none"
}
def save(svg, filename, process = false)
puts "Saving #{filename}.svg"
svg.save filename
if process then
# crop #{$margin_left}mm #{$margin_top}mm #{$width-$margin_left-$margin_right}mm #{$height-$margin_top-$margin_bottom}mm \
vpype = "vpype read #{filename}.svg \
layout -m 0 -b #{$width}mmx#{$height}mm \
penwidth #{$pen_width}mm \
splitall linemerge \
linesimplify -t 0.01 \
linesort --two-opt \
write #{filename}_final.svg"
puts "Processing #{filename}_final.svg"
system(vpype)
end
end
save(svg, "poisson", false)
puts "Done"
@hsjunnesson
Copy link
Author

poisson

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment