Stock composition version #1

Open
aniva wants to merge 5 commits from stock-composition into main
1 changed files with 78 additions and 11 deletions
Showing only changes of commit a189981d4e - Show all commits

View File

@ -1,5 +1,14 @@
using Base using Base
using Statistics, Images using Statistics, Images
import Luxor as L
@kwdef struct Region
pos::Tuple{Int,Int}
size::Tuple{Int,Int}
# If true, this region increases from x to y. This direction variable steers
# the direction of cut.
dir::Bool
end
# Time-dependent packing # Time-dependent packing
@kwdef struct Rect @kwdef struct Rect
@ -16,41 +25,53 @@ function aspect_ratio(r::Rect)::Real
end end
""" """
divide_image(img::BitMatrix, size_min::Int=16, size_max::Int=256) divide_image(img::Matrix{}, size_min::Int=16, size_max::Int=256)
Divides an image into rectangles of the same colour. The rectangles are not Divides an image into rectangles of the same colour. The rectangles are not
allowed to have extreme aspect ratios. There is a minimal size of each retangle. allowed to have extreme aspect ratios. There is a minimal size of each retangle.
""" """
function divide_image(img::Matrix{}, size_min::Int=16, size_max::Int=256, threshold_aspect::Real=3.0, threshold_std::Real=0.4)::Array{Rect} function divide_image(img::Matrix{}; size_min::Int=16, size_max::Int=128, threshold_aspect::Real=3.0, threshold_std::Real=0.4)::Array{Rect}
remainder = Rect[Rect(pos=(1,1), size=size(img), color=mean(img))] remainder = [Region(pos=(1,1), size=size(img), dir=false)]
result = Rect[] result = Rect[]
# Loop until the entire field is filled # Loop until the entire field is filled
while length(remainder) > 0 while length(remainder) > 0
section = pop!(remainder) section = pop!(remainder)
width = size_min (px, py) = section.pos
height = size_min (sx, sy) = section.size
@assert px + sx - 1 <= size(img)[1]
@assert py + sy - 1 <= size(img)[2]
width = 1
height = 1
width_max = min(sx, size_max)
height_max = min(sy, size_max)
# Try to expand this rectangle to the maximum size # Try to expand this rectangle to the maximum size
while width < section.size[1] || height < section.size[2] while width < width_max || height < height_max
subimg = img[section.pos[1]:section.pos[1]+width-1, section.pos[2]:section.pos[2]+height-1] subimg = img[px:px+width-1, py:py+height-1]
if std(subimg) >= threshold_std if std(subimg) >= threshold_std
break break
end end
colour = mean(subimg) colour = mean(subimg)
ar = width / height ar = width / height
if width < section.size[1] && ar < threshold_aspect if width < width_max && ar < threshold_aspect
width += 1 width += 1
elseif height < section.size[1] elseif height < height_max && ar > 1/threshold_aspect
height += 1 height += 1
else else
break break
end end
end end
subimg = img[section.pos[1]:section.pos[1]+width-1, section.pos[2]:section.pos[2]+height-1] @assert width <= sx
@assert height <= sy
subimg = img[px:px+width-1, py:py+height-1]
rect = Rect( rect = Rect(
pos=section.pos, pos=section.pos,
size=(width, height), size=(width, height),
@ -58,8 +79,54 @@ function divide_image(img::Matrix{}, size_min::Int=16, size_max::Int=256, thresh
) )
push!(result, rect) push!(result, rect)
# FIXME: Add next iteration region if section.dir
if width < sx
push!(remainder, Region(
pos=(px + width, py),
size=(sx - width, height),
dir=false,
))
end
if height < sy
push!(remainder, Region(
pos=(px, py + height),
size=(sx, sy - height),
dir=true,
))
end
else
if height < sy
push!(remainder, Region(
pos=(px, py + height),
size=(width, sy - height),
dir=true,
))
end
if width < sx
push!(remainder, Region(
pos=(px + width, py),
size=(sx - width, sy),
dir=false,
))
end
end
end end
return result return result
end end
"""
draw_rect(size::Tuple{Int, Int}, rects::Array{Rect})
Displays the divided image in greyscale.
"""
function draw_rect(size::Tuple{Int, Int}, rects::Array{Rect})
L.Drawing(size[1], size[2], :png)
L.background("black")
for rect in rects
c = N0f8(rect.color)
setcolor(c, c, c)
L.rect(L.Point(rect.pos), rect.size[1], rect.size[2], action = :fill)
end
L.finish()
end