The Aspect treemap layout algorithm is a simplification of the squarify algorithm described at http://www.win.tue.nl/~vanwijk/stm.pdf
The algorithm is as follows (my Swift implementation is also shown)
[i] The aspect ratio for a rectangle r is
max(r.width/r.height, r.height/r.width)
###Swift Implementation
func aspectLayout(_ items: TreemapItemList<T>, _ rect: NSRect)
{
func aspect_ratio(_ r: NSRect) -> Double
{
let ratio = max(r.width / r.height, r.height / r.width)
return Double(ratio)
}
func splitRect(_ rect: NSRect, _ scale: Double, _ r1: inout NSRect, _ r2: inout NSRect)
{
r1 = rect
r2 = rect
if rect.width >= rect.height {
r1.size.width *= CGFloat(scale)
r2.origin.x += r1.width
r2.size.width = rect.width - r1.width
} else {
r1.size.height *= CGFloat(scale)
r2.origin.y += r1.height
r2.size.height = rect.height - r1.height
}
}
if isLastItem(items, rect, aspectLayout) {
return
}
let totalWeight = items.weight
var split = 0
for i in 0 ..< items.count - 2 {
let l1 = items.sublist(first: 0, last: i)
var r1 = rect
var r2 = rect
let scale = l1.weight / totalWeight
splitRect(rect, scale, &r1, &r2)
let a1 = aspect_ratio(r1)
let a2 = aspect_ratio(r2)
if a1 <= a2 {
split = i
break
}
}
var r1 = rect
var r2 = rect
let l1 = items.sublist(first: 0, last: split)
let l2 = items.sublist(first: split + 1, last: items.count - 1)
let scale = l1.weight / totalWeight
splitRect(rect, scale, &r1, &r2)
aspectLayout(l1, r1)
aspectLayout(l2, r2)
}