Какой алгоритм используется для отображения нескольких фото в виде мозаики?

1,00
р.
Например, как в ВК при загрузке фотографий. Речь идет о таком отображении:

UPD: Возможно, для вывода изображения используется алгоритм TreeMap. Но не ясно, как задавать "веса" изображениям.

Ответ
Я так и не нашел универсального решения, но опишу как это работает сейчас, может быть это кому-то поможет.
Сейчас я исходя из кол-ва картиночек делю высоту (350 пикселей) на N-ое кол-во строк. Максимум - три, т.к. картиночек может быть всего 10.
Далее, я заполняю строки следующим образом:
Считаю для каждой строки суммарное соотношение сторон В строку у которой это значение самое низкое добавляю картинку Если картинки еще есть - пункт 1.
Теперь я задаю ширину картинкам (высота, напомню, это просто 350 / кол-во строк). Делаю я это так:
Выбираю очередную строку Достаю очередную картинку Ширина картинки = 510 * (соотношение_сторон_картинки / суммарное_соотношение_сторон_картинок_в_сроке) И т.д.
Ну и код, который работает по этому алгоритму:
'use strict'
angular.module('imageGrid').controller('imageGridController', function($scope) {
var divWidth = 510 var divHeight = 350 var padding = 5
var imagesList = $scope.photos angular.forEach(imagesList, function(image, index) { image.aspectRatio = image.width / image.height image.blockWidth = 0 image.blockHeight = 0 image.marginBottom = 0 image.marginRight = 0 }, this)
var sumOfWidth = function(set) { var totalWidth = 0 if (set.length > 0) { for (var i = 0 i < set.length i++) { totalWidth += set[i].width } } return totalWidth }
var sumOfAspectRatio = function(set) { var totalAspectRatio = 0 if (set.length > 0) { for (var i = 0 i < set.length i++) { totalAspectRatio += set[i].aspectRatio } } return totalAspectRatio }
var medianAspectRatio = function(set) { return this.sumOfAspectRatio(set) }
var linearPartition = function(set) { var rows = Math.min(3, Math.ceil(set.length / 3))
var rowsArray = [] for (var i = 0 i < rows i++) { rowsArray.push([]) }
for (var l = 0 l < set.length l++) { var rowsAspectRatio = [] for (var i = 0 i < rowsArray.length i++) { rowsAspectRatio.push(sumOfAspectRatio(rowsArray[i])) } var minIndex = 0, minValue = Infinity for (var i = 0 i < rowsArray.length i++) { if (rowsAspectRatio[i] < minValue) { minValue = rowsAspectRatio[i] minIndex = i } } rowsArray[minIndex].push(set[l]) }
return rowsArray }
var setByRows = linearPartition(imagesList) var rowsCount = setByRows.length
var virtualHeight = Math.floor(divHeight / rowsCount)
for (var i = 0 i < setByRows.length i++) { var rowAspectRatio = sumOfAspectRatio(setByRows[i]) var imagesCount = setByRows[i].length var virtualWidth = imagesCount > 1 ? divWidth - (imagesCount - 1) * padding : divWidth for (var l = 0 l < imagesCount l++) { var image = setByRows[i][l] image.blockWidth = Math.floor(virtualWidth / imagesCount) image.blockHeight = virtualHeight }
}
var resultList = [] for (var i = 0 i < rowsCount i++) { var imagesCount = setByRows[i].length for (var l = 0 l < setByRows[i].length l++) { var image = setByRows[i][l] image.marginBottom = rowsCount > 1 && i < rowsCount - 1 ? padding : 0 image.marginRight = imagesCount > 1 && l < imagesCount - 1 ? padding : 0 resultList.push(image) } }
$scope.imageList = resultList })
Обновление 01.09.2016 16:50
По просьбе, поясню, что в моем понимании "не универсальное решение". Мой алгоритм не умеет разбивать картинки максимально эффективно.
Вот так отображается три картинки с помощью моего алгоритма:
Заметьте, у всех одинаковая ширина. Первая картинка (красная), обрезается очень, очень сильно, т.к. у нее самая большая ширина из всех картинок.
А вот так эти же картинки отображает ВК:
Не трудно заметить, что в этом варианте расположения отображается на много большая часть первой картинки (возможно, и всех остальных).
Именно из за этого я и считаю свой алгоритм "не универсальным".