gpt4 book ai didi

javascript - 在二维区域内为对象寻找空间的算法

转载 作者:塔克拉玛干 更新时间:2023-11-03 05:05:36 24 4
gpt4 key购买 nike

我正在构建一个网站,该网站使用 jQuery 允许用户将小部件添加到页面,将它们四处拖动并调整它们的大小(页面是固定宽度和无限高度。)我遇到的问题是添加时页面的新小部件我必须为其找到可用空间(小部件不能重叠,我希望页面顶部有空间。)

我一直在研究各种打包算法,但似乎没有一个适合。原因是它们设计用于将所有对象打包到容器中,这意味着之前的所有矩形都以统一的方式布局。它们通常排列矩形的边缘,以便形成行/列,这简化了计算下一行/列中适合的位置。当用户可以随意移动/调整小部件时,这些算法将无法正常工作。

我以为我有一个部分解决方案,但在这里写了一些伪代码后,我意识到它行不通。基于蛮力的方法会起作用,但如果可能的话,我更喜欢更有效的方法。谁能推荐一个合适的算法?它是我正在寻找的打包算法还是其他更好的算法?

谢谢

最佳答案

好的,我已经找到了解决方案。我不喜欢基于蛮力的方法的想法,因为我认为它效率低下,但我意识到,如果您可以查看哪些现有小部件妨碍了放置小部件,那么您可以跳过大部分网格。

这是一个例子:(在这个例子中放置的小部件是 20x20,页面宽度是 100px。)

This diagram is 0.1 scale and got messed up so I've had to add an extra column

*123456789A*
1+---+ +--+1
2| | | |2
3| | +--+3
4| | 4
5+---+ 5
*123456789A*
  1. 我们尝试在 0x0 处放置一个小部件,但它不适合,因为在该坐标处有一个 50x50 的小部件。
  2. 然后我们将当前扫描的 x 坐标推进到 51 并再次检查。
  3. 然后我们在 0x61 处找到一个 40x30 的小部件。
  4. 然后我们将 x 坐标提高到 90,但这没有为放置的小部件留出足够的空间,因此我们增加 y 坐标并将 x 重置回 0。
  5. 我们从之前的尝试中得知,前一行的小部件至少有 30 像素高,因此我们将 y 坐标增加到 31。
  6. 我们在 0x31 处遇到相同的 50x50 小部件。
  7. 所以我们将 x 增加到 51,发现我们可以在 51x31 处放置一个小部件

这是javascript:

function findSpace(width, height) {
var $ul = $('.snap-layout>ul');
var widthOfContainer = $ul.width();
var heightOfContainer = $ul.height();
var $lis = $ul.children('.setup-widget'); // The li is on the page and we dont want it to collide with itself

for (var y = 0; y < heightOfContainer - height + 1; y++) {
var heightOfShortestInRow = 1;
for (var x = 0; x < widthOfContainer - width + 1; x++) {
console.log(x + '/' + y);
var pos = { 'left': x, 'top': y };
var $collider = $(isOverlapping($lis, pos, width, height));
if ($collider.length == 0) {
// Found a space
return pos;
}

var colliderPos = $collider.position();
// We have collided with something, there is no point testing the points within this widget so lets skip them
var newX = colliderPos.left + $collider.width() - 1; // -1 to account for the ++ in the for loop
x = newX > x ? newX : x; // Make sure that we are not some how going backwards and looping forever

var colliderBottom = colliderPos.top + $collider.height();
if (heightOfShortestInRow == 1 || colliderBottom - y < heightOfShortestInRow) {
heightOfShortestInRow = colliderBottom - y; // This isn't actually the height its just the distance from y to the bottom of the widget, y is normally at the top of the widget tho
}
}
y += heightOfShortestInRow - 1;
}

//TODO: Add the widget to the bottom
}

这是一个更长、更不优雅的版本,它还可以调整容器的高度(我现在只是把它拼凑在一起,但稍后会清理它并进行编辑)

function findSpace(width, height,
yStart, avoidIds // These are used if the function calls itself - see bellow
) {
var $ul = $('.snap-layout>ul');
var widthOfContainer = $ul.width();
var heightOfContainer = $ul.height();
var $lis = $ul.children('.setup-widget'); // The li is on the page and we dont want it to collide with itself

var bottomOfShortestInRow;
var idOfShortestInRow;

for (var y = yStart ? yStart : 0; y <= heightOfContainer - height + 1; y++) {
var heightOfShortestInRow = 1;
for (var x = 0; x <= widthOfContainer - width + 1; x++) {
console.log(x + '/' + y);
var pos = { 'left': x, 'top': y };
var $collider = $(isOverlapping($lis, pos, width, height));
if ($collider.length == 0) {
// Found a space
return pos;
}

var colliderPos = $collider.position();
// We have collided with something, there is no point testing the points within this widget so lets skip them
var newX = colliderPos.left + $collider.width() - 1; // -1 to account for the ++ in the for loop
x = newX > x ? newX : x; // Make sure that we are not some how going backwards and looping forever

colliderBottom = colliderPos.top + $collider.height();
if (heightOfShortestInRow == 1 || colliderBottom - y < heightOfShortestInRow) {
heightOfShortestInRow = colliderBottom - y; // This isn't actually the height its just the distance from y to the bottom of the widget, y is normally at the top of the widget tho
var widgetId = $collider.attr('data-widget-id');
if (!avoidIds || !$.inArray(widgetId, avoidIds)) { // If this is true then we are calling ourselves and we used this as the shortest widget before and it didnt work
bottomOfShortestInRow = colliderBottom;
idOfShortestInRow = widgetId;
}
}
}
y += heightOfShortestInRow - 1;
}

if (!yStart) {
// No space was found so create some
var idsToAvoid = [];

for (var attempts = 0; attempts < widthOfContainer; attempts++) { // As a worse case scenario we have lots of 1px wide colliders
idsToAvoid.push(idOfShortestInRow);

heightOfContainer = $ul.height();
var maxAvailableRoom = heightOfContainer - bottomOfShortestInRow;
var extraHeightRequired = height - maxAvailableRoom;
if (extraHeightRequired < 0) { extraHeightRequired = 0;}

$ul.height(heightOfContainer + extraHeightRequired);

var result = findSpace(width, height, bottomOfShortestInRow, idsToAvoid);
if (result.top) {
// Found a space
return result;
}

// Got a different collider so lets try that next time
bottomOfShortestInRow = result.bottom;
idOfShortestInRow = result.id;

if (!bottomOfShortestInRow) {
// If this is undefined then its broken (because the widgets are bigger then their contianer which is hardcoded atm and resets on f5)
break;
}
}

debugger;
// Something has gone wrong so we just stick it on the bottom left
$ul.height($ul.height() + height);
return { 'left': 0, 'top': $ul.height() - height };

} else {
// The function is calling itself and we shouldnt recurse any further, just return the data required to continue searching
return { 'bottom': bottomOfShortestInRow, 'id': idOfShortestInRow };
}
}


function isOverlapping($obsticles, tAxis, width, height) {
var t_x, t_y;
if (typeof (width) == 'undefined') {
// Existing element passed in
var $target = $(tAxis);
tAxis = $target.position();
t_x = [tAxis.left, tAxis.left + $target.outerWidth()];
t_y = [tAxis.top, tAxis.top + $target.outerHeight()];
} else {
// Coordinates and dimensions passed in
t_x = [tAxis.left, tAxis.left + width];
t_y = [tAxis.top, tAxis.top + height];
}

var overlap = false;

$obsticles.each(function () {
var $this = $(this);
var thisPos = $this.position();
var i_x = [thisPos.left, thisPos.left + $this.outerWidth()]
var i_y = [thisPos.top, thisPos.top + $this.outerHeight()];

if (t_x[0] < i_x[1] && t_x[1] > i_x[0] &&
t_y[0] < i_y[1] && t_y[1] > i_y[0]) {
overlap = this;
return false;
}
});
return overlap;
}

关于javascript - 在二维区域内为对象寻找空间的算法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24356096/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com