答案 0 :(得分:5)
我会考虑某种空间散列结构。想象一下,你的整个自由空间被粗略地网格化,称之为块。当窗户来来往往时,它们占据了一些连续的矩形块。对于每个块,跟踪每个角落中最大的未使用矩形,因此您需要为每个块存储2 * 4个实数。对于空块,每个角上的矩形的大小等于块。因此,一个块只能在其角落“用完”,因此最多可以在任何一个块中放置4个窗口。
现在,每次添加窗口时,都必须搜索窗口适合的矩形块集,并在此时更新自由边角大小。您应该调整块的大小,以便少量(~4x4)它们适合典型的窗口。对于每个窗口,跟踪它接触的块(您只需要跟踪范围),以及哪个窗口触摸给定块(在此算法中最多4个)。块的粒度与每个窗口插入/移除的工作量之间存在明显的权衡。
当移除一个窗口时,遍历它接触的所有块,并且对于每个块,重新计算自由角大小(你知道哪些窗口触摸它)。这很快,因为内环最多只有4个长度。
我想象一个像
这样的数据结构struct block{
int free_x[4]; // 0 = top left, 1 = top right,
int free_y[4]; // 2 = bottom left, 3 = bottom right
int n_windows; // number of windows that occupy this block
int window_id[4]; // IDs of windows that occupy this block
};
block blocks[NX][NY];
struct window{
int id;
int used_block_x[2]; // 0 = first index of used block,
int used_block_y[2]; // 1 = last index of used block
};
修改强>
这是一张图片:
它显示了两个示例块。有色点表示块的角,从它们发出的箭头表示该角的最大自由矩形的范围。
你在编辑中提到你的窗口所在的网格已经非常粗糙(127x127),所以块大小可能就像一侧有4个网格单元,这可能不会让你获得太多。如果你的窗口角坐标可以采用很多值(我认为它们是像素),这种方法是合适的,但在你的情况下并不是那么多。你仍然可以尝试,因为它很简单。你可能还想保留一个完全空块的列表,这样如果一个窗口进入大于2个块的宽度,那么你先查看该列表中的第一个,然后在块网格中寻找一些合适的可用空间。
答案 1 :(得分:2)
经过一些错误的开始,我终于到了这里。这里是放弃使用数据结构来存储自由空间的矩形区域的地方。相反,有一个带有128 x 128个元素的2d数组可以实现相同的结果,但复杂性要低得多。
以下函数扫描数组中的区域width * height
的大小。它找到的第一个位置将左上角坐标写入resultx和resulty指向的位置。
_Bool freespace_remove( freespace* fs,
int width, int height,
int* resultx, int* resulty)
{
int x = 0;
int y = 0;
const int rx = FSWIDTH - width;
const int by = FSHEIGHT - height;
*resultx = -1;
*resulty = -1;
char* buf[height];
for (y = 0; y < by; ++y)
{
x = 0;
char* scanx = fs->buf[y];
while (x < rx)
{
while(x < rx && *(scanx + x))
++x;
int w, h;
for (h = 0; h < height; ++h)
buf[h] = fs->buf[y + h] + x;
_Bool usable = true;
w = 0;
while (usable && w < width)
{
h = 0;
while (usable && h < height)
if (*(buf[h++] + w))
usable = false;
++w;
}
if (usable)
{
for (w = 0; w < width; ++w)
for (h = 0; h < height; ++h)
*(buf[h] + w) = 1;
*resultx = x;
*resulty = y;
return true;
}
x += w;
}
}
return false;
}
2d数组初始化为零。数组中使用空格的任何区域都设置为1.此结构和功能将独立于占据标记为1的区域的实际窗口列表。
这种方法的优点是简单。它只使用一个数据结构 - 一个数组。功能很短,并且不应该太难以适应处理剩余的放置选项(这里它只处理行智能+从左到右+从上到下)。
我的初步测试在速度方面看起来也很有希望。虽然我认为这不适合窗户管理员放置窗户,例如1600 x 1200台式机具有像素精度,但就我的目的而言,我相信它会比我之前的任何一种方法都好得多。试过。
此处可编辑的测试代码:
http://jwm-art.net/art/text/freespace_grid.c
(在Linux中我用gcc -ggdb -O0 freespace_grid.c
编译)
答案 2 :(得分:1)
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define FSWIDTH 128
#define FSHEIGHT 128
#ifdef USE_64BIT_ARRAY
#define FSBUFBITS 64
#define FSBUFWIDTH 2
typedef uint64_t fsbuf_type;
#define TRAILING_ZEROS( v ) __builtin_ctzl(( v ))
#define LEADING_ONES( v ) __builtin_clzl(~( v ))
#else
#ifdef USE_32BIT_ARRAY
#define FSBUFBITS 32
#define FSBUFWIDTH 4
typedef uint32_t fsbuf_type;
#define TRAILING_ZEROS( v ) __builtin_ctz(( v ))
#define LEADING_ONES( v ) __builtin_clz(~( v ))
#else
#ifdef USE_16BIT_ARRAY
#define FSBUFBITS 16
#define FSBUFWIDTH 8
typedef uint16_t fsbuf_type;
#define TRAILING_ZEROS( v ) __builtin_ctz( 0xffff0000 | ( v ))
#define LEADING_ONES( v ) __builtin_clz(~( v ) << 16)
#else
#ifdef USE_8BIT_ARRAY
#define FSBUFBITS 8
#define FSBUFWIDTH 16
typedef uint8_t fsbuf_type;
#define TRAILING_ZEROS( v ) __builtin_ctz( 0xffffff00 | ( v ))
#define LEADING_ONES( v ) __builtin_clz(~( v ) << 24)
#else
#define FSBUFBITS 1
#define FSBUFWIDTH 128
typedef unsigned char fsbuf_type;
#define TRAILING_ZEROS( v ) (( v ) ? 0 : 1)
#define LEADING_ONES( v ) (( v ) ? 1 : 0)
#endif
#endif
#endif
#endif
static const fsbuf_type fsbuf_max = ~(fsbuf_type)0;
static const fsbuf_type fsbuf_high = (fsbuf_type)1 << (FSBUFBITS - 1);
typedef struct freespacegrid
{
fsbuf_type buf[FSHEIGHT][FSBUFWIDTH];
_Bool left_to_right;
_Bool top_to_bottom;
} freespace;
void freespace_dump(freespace* fs)
{
int x, y;
for (y = 0; y < FSHEIGHT; ++y)
{
for (x = 0; x < FSBUFWIDTH; ++x)
{
fsbuf_type i = FSBUFBITS;
fsbuf_type b = fs->buf[y][x];
for(; i != 0; --i, b <<= 1)
putchar(b & fsbuf_high ? '#' : '/');
/*
if (x + 1 < FSBUFWIDTH)
putchar('|');
*/
}
putchar('\n');
}
}
freespace* freespace_new(void)
{
freespace* fs = malloc(sizeof(*fs));
if (!fs)
return 0;
int y;
for (y = 0; y < FSHEIGHT; ++y)
{
memset(&fs->buf[y][0], 0, sizeof(fsbuf_type) * FSBUFWIDTH);
}
fs->left_to_right = true;
fs->top_to_bottom = true;
return fs;
}
void freespace_delete(freespace* fs)
{
if (!fs)
return;
free(fs);
}
/* would be private function: */
void fs_set_buffer( fsbuf_type buf[FSHEIGHT][FSBUFWIDTH],
unsigned x,
unsigned y1,
unsigned xoffset,
unsigned width,
unsigned height)
{
fsbuf_type v;
unsigned y;
for (; width > 0 && x < FSBUFWIDTH; ++x)
{
if (width < xoffset)
v = (((fsbuf_type)1 << width) - 1) << (xoffset - width);
else if (xoffset < FSBUFBITS)
v = ((fsbuf_type)1 << xoffset) - 1;
else
v = fsbuf_max;
for (y = y1; y < y1 + height; ++y)
{
#ifdef FREESPACE_DEBUG
if (buf[y][x] & v)
printf("**** over-writing area ****\n");
#endif
buf[y][x] |= v;
}
if (width < xoffset)
return;
width -= xoffset;
xoffset = FSBUFBITS;
}
}
_Bool freespace_remove( freespace* fs,
unsigned width, unsigned height,
int* resultx, int* resulty)
{
unsigned x, x1, y;
unsigned w, h;
unsigned xoffset, x1offset;
unsigned tz; /* trailing zeros */
fsbuf_type* xptr;
fsbuf_type mask = 0;
fsbuf_type v;
_Bool scanning = false;
_Bool offset = false;
*resultx = -1;
*resulty = -1;
for (y = 0; y < (unsigned) FSHEIGHT - height; ++y)
{
scanning = false;
xptr = &fs->buf[y][0];
for (x = 0; x < FSBUFWIDTH; ++x, ++xptr)
{
if(*xptr == fsbuf_max)
{
scanning = false;
continue;
}
if (!scanning)
{
scanning = true;
x1 = x;
x1offset = xoffset = FSBUFBITS;
w = width;
}
retry:
if (w < xoffset)
mask = (((fsbuf_type)1 << w) - 1) << (xoffset - w);
else if (xoffset < FSBUFBITS)
mask = ((fsbuf_type)1 << xoffset) - 1;
else
mask = fsbuf_max;
offset = false;
for (h = 0; h < height; ++h)
{
v = fs->buf[y + h][x] & mask;
if (v)
{
tz = TRAILING_ZEROS(v);
offset = true;
break;
}
}
if (offset)
{
if (tz)
{
x1 = x;
w = width;
x1offset = xoffset = tz;
goto retry;
}
scanning = false;
}
else
{
if (w <= xoffset) /***** RESULT! *****/
{
fs_set_buffer(fs->buf, x1, y, x1offset, width, height);
*resultx = x1 * FSBUFBITS + (FSBUFBITS - x1offset);
*resulty = y;
return true;
}
w -= xoffset;
xoffset = FSBUFBITS;
}
}
}
return false;
}
int main(int argc, char** argv)
{
int x[1999];
int y[1999];
int w[1999];
int h[1999];
int i;
freespace* fs = freespace_new();
for (i = 0; i < 1999; ++i, ++u)
{
w[i] = rand() % 18 + 4;
h[i] = rand() % 18 + 4;
freespace_remove(fs, w[i], h[i], &x[i], &y[i]);
/*
freespace_dump(fs);
printf("w:%d h:%d x:%d y:%d\n", w[i], h[i], x[i], y[i]);
if (x[i] == -1)
printf("not removed space %d\n", i);
getchar();
*/
}
freespace_dump(fs);
freespace_delete(fs);
return 0;
}
以上代码需要USE_64BIT_ARRAY
,USE_32BIT_ARRAY
,USE_16BIT_ARRAY
,USE_8BIT_ARRAY
一个,否则它将回退到仅使用用于存储网格单元状态的unsigned char
的高位。
函数fs_set_buffer
不会在标头中声明,并且当此代码在.h和.c文件之间拆分时,将在实现中变为静态。将提供一个更加用户友好的隐藏实现细节的函数,用于从网格中删除已用空间。
总的来说,这个实现没有优化比我的previous answer进行最优化(在64位Gentoo上使用GCC,分别使用优化选项-O0和-O3)。
关于USE_ NN BIT_ARRAY以及不同的位大小,我使用了两种不同的方法来计算代码,这些方法使1999调用freespace_remove
。
使用Unix main()
命令(并禁用代码中的任何输出)的时间time
似乎证明了我的期望是正确的 - 更高的位大小更快。
另一方面,定时调用freespace_remove
(使用gettimeofday
)并比较1999年调用所花费的最长时间似乎表明较低的位大小更快。
仅在64位系统(英特尔双核II)上进行了测试。