一个布局算法问题解析

已知条件:

宽度已知且有限,高度无限的区域内,有n个方格,已知这些方格的x ,y 坐标以及宽度w和高度h,方格不重叠

需求:

在空间内找个一个位置,可以放的下一个固定宽高(如4 x 4)的方格,原则:离原点最近且Y轴最上

关键点:

  1. 位置信息转化为二维数组
  2. 目标点:每个已知方格的左下角点 A,右下角点 B,和右上角点 C
  3. 目标点位置调整:A点依照高度向左平移,B C点按照宽度向上平移
  4. 补充点:原点检测
具体实现思路:

位置信息转化为二维数组 => 目标点获取  => 目标点位置调整 => 空间检测 => 最优点获取,原则:原点最近 && Y轴最上

代码实现:

/** 
 *  spotGenerator
 *     将该文件中的 spotGenerator 方法暴露出去以供调用
 * 
 *  1. 实现思想:位置信息转化为二维数组 => 目标点获取  => 目标点位置调整 => 空间检测 => 最优点获取,原则:原点最近 && Y轴最上
 *  2. 使用:调用spotGenerator(data, column, initialWidth, initialHeight) 方法
 *          参数:
 *              1. data :  类型 Array, 现有gird的位置信息,示例[{ x: 9, y: 9, w: 6, h: 7 }]
 *              2. column: 类型 Number,分割的列数,例如 24
 *              3. initialWidth: 类型 Number,新添加grid的默认宽度,如 4
 *              4. initialHeight: 类型 Number,新添加grid时默认高度,如 4
 *          返回值:
 *              最优点的位置坐标,类型:Object  示例 { x:1 , y:1 }
*/



//获取数组的深度
function getDepth(data) {
    var depths = [];
    for (var i = 0; i < data.length; i++) {
        depths.push(data[i].y + data[i].h)
    }
    return Math.max.apply(null, depths);
}
//点集填充
function initSpotByData(arr, data) {
    var x, y, w, h;
    for (var i = 0; i < data.length; i++) {
        x = data[i].x;
        y = data[i].y;
        w = data[i].w;
        h = data[i].h;
        for (var j = 0; j < h; j++) {
            for (var k = 0; k < w; k++) {
                arr[y + j][x + k] = 1;
            }
        }
    }
    for (var i = 0; i < arr.length; i++) {
        for (var j = 0; j < arr[i].length; j++) {
            !arr[i][j] && (arr[i][j] = 0)
        }
    }
    return arr;
}
//获取目标点
function getTargetSpot(data) {
    var leftBottom = [],
        rightBottom = [],
        topRight = [],
        x, y, w, h;
    for (var i = 0; i < data.length; i++) {
        x = data[i].x;
        y = data[i].y;
        w = data[i].w;
        h = data[i].h;
        leftBottom.push({ x: x, y: y + h });
        rightBottom.push({ x: x + w, y: y + h });
        topRight.push({ x: x + w, y: y })
    }
    return {
        leftBottom: leftBottom,
        rightBottom: rightBottom,
        topRight: topRight
    }
}
//调节目标点的位置
function tuneTargetSpot(spot, rootArr, initialWidth, initialHeight) {
    for (var key in spot) {
        switch (key) {
            case "leftBottom":
                for (var i = 0; i < spot[key].length; i++) {
                    var newX = leftBottomTune(spot[key][i], rootArr, initialHeight);
                    spot[key][i].x = newX;
                }
                break;
            case "rightBottom":
                for (var i = 0; i < spot[key].length; i++) {
                    var newY = rightBottomTune(spot[key][i], rootArr, initialWidth);
                    spot[key][i].y = newY;
                }
                break;
            case "topRight":
                for (var i = 0; i < spot[key].length; i++) {
                    var newY = topRightTune(spot[key][i], rootArr, initialWidth);
                    spot[key][i].y = newY;
                }
                break;
        }
    }
}
//leftBottom的点向左微调,参数 : {x:x,y:y}  
function leftBottomTune(spotPosition, rootArr, initialHeight) {
    var x = spotPosition.x,
        y = spotPosition.y,
        find = false;
    for (var j = x; j > 0; j--) {
        for (var i = 0; i < initialHeight; i++) {
            if (rootArr[y + i] && rootArr[y + i][j] == 1) {
                find = true;
                break;
            };
        }
        if (find) break;
    }
    return j == 0 ? j : ++j;//调整后的x值
}
//rightBottom的点向上微调,参数 : {x:x,y:y}  
function rightBottomTune(spotPosition, rootArr, initialWidth) {
    var x = spotPosition.x,
        y = spotPosition.y,
        find = false;
    for (var j = y; j > 0; j--) {
        for (var i = 0; i < initialWidth; i++) {
            if (rootArr[j] && rootArr[j][x + i] == 1) {
                find = true;
                break;
            };
        }
        if (find) break;
    };
    return j == 0 ? j : ++j;
}
//topRight向上调整,参数 : {x:x,y:y}  
function topRightTune(spotPosition, rootArr, initialWidth) {
    var x = spotPosition.x,
        y = spotPosition.y,
        find = false;
    for (var j = y; j > 0; j--) {
        for (var i = 0; i < initialWidth; i++) {
            if (rootArr[j] && rootArr[j][x + i] == 1) {//先取y轴
                find = true;
                break;
            };
        }
        if (find) break;
    };
    return j == 0 ? j : ++j;//是否是到边缘    
}
//检测是否可以放的下方格 参数 : {x:x,y:y} 返回值true为可以放得下 false为不能放下
function checkRoom(spotPosition, rootArr, initialWidth, initialHeight) {
    var x = spotPosition.x,
        y = spotPosition.y,
        find = false;
    for (var i = 0; i < initialHeight; i++) {
        for (var j = 0; j < initialWidth; j++) {
            if (rootArr[y + i][x + j] == 1 || rootArr[y + i][x + j] == undefined) {//右边空间有限
                find = true;
                break;
            }
        }
        if (find) break;
    }
    return !find;
}
//获取最终的点的位置
function getFinalSpot(spotPosition) {
    //取优先考上的点
    var yArr = [], yMin, indexArr = [];
    for (var i = 0; i < spotPosition.length; i++) {
        yArr.push(spotPosition[i].y);
    }
    yMin = Math.min.apply(null, yArr);
    //找到最小值的下标
    for (var j = 0; j < yArr.length; j++) {
        if (yArr[j] == yMin) {
            indexArr.push(j);
        }
    }
    if (indexArr.length == 1) {
        return spotPosition[indexArr[0]]
    } else {
        //如果y轴的坐标有相同的点,优先取靠原点最近的
        var spotArr = [], lengthArr = [], lengthMin, x, y, finalIndex;
        for (var i = 0; i < indexArr.length; i++) {
            spotArr.push(spotPosition[indexArr[i]]);
        }
        //勾股定理取最近的点
        for (var k = 0; k < spotArr.length; k++) {
            x = spotArr[k].x;
            y = spotArr[k].y;
            lengthArr.push(x * x + y * y);
        }
        lengthMin = Math.min.apply(null, lengthArr);//最短的距离
        for (var p = 0; p < lengthArr.length; p++) {
            if (lengthArr[p] == lengthMin) { finalIndex = p }
        }
        return spotArr[finalIndex];
    }
}
function spotGenerator(data, column, initialWidth, initialHeight) {//column 分割的列数
    //初始化二维数组
    var rootArr = [],
        depth = getDepth(data) + initialHeight,
        filterTargetSpot = [];//过滤可以放得下方格的点位置
    for (var i = 0; i < depth; i++) {
        rootArr[i] = new Array(column);//长度指定
    }
    //点集填充
    initSpotByData(rootArr, data);
    var targetSpot = getTargetSpot(data);
    tuneTargetSpot(targetSpot, rootArr, initialWidth, initialHeight);//位置调整
    //原点检测
    if (rootArr[0][0] == 0) {
        targetSpot.rightBottom.push({ x: 0, y: 0 });
    }
    for (var key in targetSpot) {
        for (var i = 0; i < targetSpot[key].length; i++) {
            var x = targetSpot[key][i].x;
            var y = targetSpot[key][i].y;
            if (checkRoom(targetSpot[key][i], rootArr, initialWidth, initialHeight)) {//检测是否能放得下
                filterTargetSpot.push(targetSpot[key][i]);
            }
        }
    }
    var finalSpot = getFinalSpot(filterTargetSpot);
    return finalSpot;
}
//获取当前鼠标位置可以容纳下的最大方格的尺寸 x:为
function spaceCheckByCursor(data,column,x,y){

}


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值