使用ThreeJs从零开始构建3D智能仓库——第五章(添加货架、货物与侧边栏)

写在前面

本章我们来讲解下如何添加货架货物和显示各类信息的侧边栏。效果如下图所示:
2019.11.26 更新:我最近建立了个人网站,大家可以访问下面的链接查看演示
3D仓库演示
2019.11.28 更新:代码和图片资源等已上传至GitHub
https://github.com/xiao149/ThreeJsDemo
在这里插入图片描述
其中货架的层数和每层的货位数量是可以自定义的,比如我这边就是三层每层两个货位。方便起见我在每个货位上都放置了货物,大家当然可以自行选择。左上角的侧边栏使用了ThreeJs官方的dat.gui,这里为了演示也只是随便列了几个信息,当点击货物时相关信息便会显示出来。

创建货架对象

创建货架、货位类

因为我本身主要从事JAVA后台的开发,对前台一知半解,所以这部分写得不好也请各位轻喷,或许是受JAVA对象类的影响,我想在JS中也创建对象,比如货架的对象,货位的对象。奈何我水平有限,不知如何在JS中创建对象,只能用一些比较“愚蠢”的方式实现:

//创建货架对象
function shelf(storageZoneId, shelfId, shelfName,
               planeLength , planeWidth , planeHeight ,
               holderLength , holderWidth , holderHeight ,
               positionX , positionY , positionZ ,
               layerNum , columnNum)
{
    this.storageZoneId=storageZoneId;
    this.shelfId=shelfId;
    this.shelfName=shelfName;
    this.planeLength=planeLength;
    this.planeWidth=planeWidth;
    this.planeHeight=planeHeight;
    this.holderLength=holderLength;
    this.holderWidth=holderWidth;
    this.holderHeight=holderHeight;
    this.positionX=positionX;
    this.positionY=positionY;
    this.positionZ=positionZ;
    this.layerNum=layerNum;
    this.columnNum=columnNum;
}

//根据货架编码获取货架对象
function getShelfById(shelfId) {
    for(var i = 0; i < shelfSize; i++){
        if(shelfList[i].shelfId == shelfId){
            return shelfList[i];
        }
    }
}

//创建货位对象
function storageUnit(storageZoneId, shelfId, shelfName,
               inLayerNum , inColumnNum ,
               positionX , positionY , positionZ, storageUnitId)
{
    this.storageZoneId=storageZoneId;
    this.shelfId=shelfId;
    this.shelfName=shelfName;
    this.inLayerNum=inLayerNum;
    this.inColumnNum=inColumnNum;
    this.positionX=positionX;
    this.positionY=positionY;
    this.positionZ=positionZ;
    this.storageUnitId=storageUnitId;
}

//根据货架ID、层数、列数获取货位对象
function getStorageUnitById(shelfId,inLayerNum,inColumnNum) {
    for(var i = 0; i < storageUnitSize; i++){
        if(storageUnitList[i].shelfId == shelfId && storageUnitList[i].inLayerNum == inLayerNum && storageUnitList[i].inColumnNum == inColumnNum){
            return storageUnitList[i];
        }
    }
}

//根据库位编码获取货位对象
function getStorageUnitByUnitId(storageUnitId) {
    for(var i = 0; i < storageUnitSize; i++){
        if(storageUnitList[i].storageUnitId == storageUnitId){
            return storageUnitList[i];
        }
    }
}

上述的创建货架货位对象有点像创建JAVA类的初始化,对象的获取也是仿照了JAVA的GET方法。这样在需要创建货架货位的地方只需要调用对象的初始化方法,赋予相应的值。在需要获取对象的地方调用GET方法就好了。

根据配置添加货架

我们创建的货架没有使用外部导入的3D模型,全部使用ThreeJs创建各类长方体拼接而成,比如单层货架就是一个长方体作货架板面,再加上四个小长方体作支架。代码如下:

//region 货架货位

/** 放置单层货架 */
/** x,y,z 整个模型在场景中的位置 */
/** plane_x,plane_y,plane_z 货架板面的长高宽 */
/** holder_x,holder_y,holder_z 货架支架的长高宽 */
/** scene,name,num 要添加的场景,货架的名字,单层货架的库位数量 */
function addRack(x,y,z,plane_x,plane_y,plane_z,holder_x,holder_y,holder_z,scene,name,num) {
    var plane = new THREE.BoxGeometry( plane_x, plane_y, plane_z/num );
    var gz = [];
    for(var i = 0; i < num; i++){
        gz.push( z + plane_z/num/2 + (plane_z/num)*i );
        var obj = new THREE.Mesh( plane, RackMat );
        obj.position.set(x , y, gz[i]) ;
        var msg = name+"$"+(2-i);

        var storageUnitId = msg.split("$")[1] + "$" + msg.split("$")[3] + "$" + msg.split("$")[4];

        //添加货位
        var storageUnit_obj = new storageUnit(msg.split("$")[0],
            msg.split("$")[1],
            msg.split("$")[2],
            msg.split("$")[3],
            msg.split("$")[4],
            x, y, gz[i], storageUnitId);
        storageUnitList.push(storageUnit_obj);
        storageUnitSize++;

        var Unit = getStorageUnitById(msg.split("$")[1],msg.split("$")[3],msg.split("$")[4]);
        obj.name = "货位"+"$"+Unit.storageUnitId;
        scene.add(obj);
    }

    var holder = new THREE.BoxGeometry( holder_x, holder_y, holder_z );
    var obj2 = new THREE.Mesh( holder, RackMat2 );
    var obj3 = new THREE.Mesh( holder, RackMat2 );
    var obj4 = new THREE.Mesh( holder, RackMat2 );
    var obj5 = new THREE.Mesh( holder, RackMat2 );

    obj2.position.set(x-plane_x/2+holder_x/2,y-holder_y/2-plane_y/2,z+holder_z/2);
    obj3.position.set(x+plane_x/2-holder_x/2,y-holder_y/2-plane_y/2,z+holder_z/2);
    obj4.position.set(x-plane_x/2+holder_x/2,y-holder_y/2-plane_y/2,z+plane_z-holder_z/2);
    obj5.position.set(x+plane_x/2-holder_x/2,y-holder_y/2-plane_y/2,z+plane_z-holder_z/2);
    scene.add(obj2);scene.add(obj3);scene.add(obj4);scene.add(obj5);
}

/** 放置一叠货架 */
/** stack_num 货架的叠数 */
function addStackOfRack(x,y,z,plane_x,plane_y,plane_z,holder_x,holder_y,holder_z,scene,name,num,stack_num) {
    for(var i = 0; i < stack_num; i++){
        addRack(x,y*(i+1),z,plane_x,plane_y,plane_z,holder_x,holder_y,holder_z,scene,name+"$"+(i+1),num);
    }
}

/** 根据货架配置添加货架 */
function addShelf(scene) {
    var shelf_list = new Array();
    shelf_list.push({StorageZoneId:'Z1',shelfId:'A2',shelfName:'货架A2',x:0,y:27,z:0});
    shelfSize = shelf_list.length;
    for(var i = 0; i < shelfSize; i++){
        var shelf_obj = new shelf(shelf_list[i].StorageZoneId,
            shelf_list[i].shelfId,
            shelf_list[i].shelfName,
            PLANE_LENGTH,PLANE_WIDTH,PLANE_HEIGHT,
            HOLDER_LENGTH,HOLDER_WIDTH,HOLDER_HEIGHT,
            shelf_list[i].x,
            shelf_list[i].y,
            shelf_list[i].z,
            LAYER_NUM,COLUMN_NUM);
        shelfList.push(shelf_obj);
    }

    for(var i = 0;i < shelfSize; i++){
        addStackOfRack(shelfList[i].positionX,shelfList[i].positionY,shelfList[i].positionZ,shelfList[i].planeLength,shelfList[i].planeHeight,shelfList[i].planeWidth,shelfList[i].holderLength,shelfList[i].holderHeight,shelfList[i].holderWidth,scene,shelfList[i].storageZoneId+"$"+shelfList[i].shelfId+"$"+shelfList[i].shelfName,shelfList[i].columnNum,shelfList[i].layerNum);
    }
}

在HTML文件的init()方法中添加addShelf(scene);就可以实现添加货架的功能啦。
addShelf方法中我原本是读取数据库货架的配置的,但是既然拿出来展示了肯定就不能用数据库了,所以我只能写死一些数据。

var shelf_list = new Array();
shelf_list.push({StorageZoneId:'Z1',shelfId:'A2',shelfName:'货架A2',x:0,y:27,z:0});

这行是添加一个货架,参数的意义是:货架所在的库区、货架的编号、货架的中文名、货架的xyz位置。效果如下:
在这里插入图片描述
点击货位能以高亮显示,并显示出货位的信息:货位&货架编码&货位的所在层&所在列。
在这里插入图片描述
若是想添加多个货架也很简单,在addShelf方法里加上:

var shelf_list = new Array();
shelf_list.push({StorageZoneId:'Z1',shelfId:'A1',shelfName:'货架A1',x:-100,y:27,z:0});
shelf_list.push({StorageZoneId:'Z1',shelfId:'A2',shelfName:'货架A2',x:0,y:27,z:0});
shelf_list.push({StorageZoneId:'Z1',shelfId:'A3',shelfName:'货架A3',x:100,y:27,z:0});

需要几个货架就在shelf_list里添加几个。效果如下:
在这里插入图片描述

添加货物

在我的设计中,货物是跟货位绑定的,一个货位上可能会放很多箱的货物,为了显示简洁方便,我们统一成一个大箱子,然后双击这个大箱子弹出画面显示里面的详细信息(这章暂不讲解),Modules.js代码如下:

//region 货物
/** 放置单个货物 */
function addCargo(x,y,z,box_x,box_y,box_z,scene,name) {
    var geometry = new THREE.BoxGeometry( box_x, box_y, box_z );
    var obj = new THREE.Mesh( geometry, CargoMat );
    obj.position.set(x,y,z);
    obj.name = name;
    scene.add(obj);
}

/** 添加单个货位上的货物 */
function addOneUnitCargos(shelfId,inLayerNum,inColumnNum,scene) {
    var storageUnit = getStorageUnitById(shelfId,inLayerNum,inColumnNum);
    var shelf = getShelfById(storageUnit.shelfId);
    var storageUnitid = storageUnit.storageUnitId;
    var x = storageUnit.positionX;
    var y = storageUnit.positionY + 8 + shelf.planeHeight/2;
    var z = storageUnit.positionZ;
    addCargo(x,y,z,16,16,16,scene,"货物"+"$"+storageUnitid)
}
//endregion

在HTML的init()方法中添加:

//添加货物
for(var i = 1; i <= 3; i++){
    for(var j = 1; j <= 2; j++){
        for(var k = 1; k <= 3; k++) {
            addOneUnitCargos("A" + k, i, j, scene);
        }
    }
}

效果如下,点击货物箱子也会显示货物的信息,跟货位类似也是:货物&货架编码&货物的所在层&所在列。
在这里插入图片描述

添加信息侧边栏

首先在HTML中引入这个JS

<script src="./ThreeJs/js/dat.gui.min.js"></script>

然后添加这个方法,大家可以自定义需要显示的内容。我这里添加了四个标签,每个都添加了.listen()监听事件,这样就可以在点击到物体的时候改变侧边栏上的内容。

// 初始化GUI
function initGui() {
    options = new function () {
        this.batchNo ='';this.qty = 0;this.qtyUom ='';this.qty2 = 0;
    };
    var gui = new dat.GUI();
    gui.domElement.style = 'position:absolute;top:50px;left:0px;height:600px';
    gui.add(options, 'batchNo').name("物料批号:").listen();
    gui.add(options, 'qty').name("数量:").listen();
    gui.add(options, 'qtyUom').name("单位:").listen();
    gui.add(options, 'qty2').name("件数:").listen();
}

init()中添加initGui()就ok了
我们回到ThreeJs_Composer.js这个自定义的JS,我们需要在鼠标点击事件里添加代码实现:

var Msg = intersects[0].object.name.split("$");
if(Msg[0] == "货物") {
    _options.batchNo = "一个货物";
    _options.qty = "100";
    _options.qtyUom = "kg";
    _options.qty2 = "10";
}

这里限定了只有点击到货物时,侧边栏_options才会更新,一般数据也是从数据库取出来的,这里我仅仅为了演示写死了一些数据。完成后效果如下:
在这里插入图片描述

结束语

最近陆续有朋友来加我微信交流评论,说实话当初我写文章只是为了记录下自己的历程免得以后忘记,看到这么多朋友催更交流我感到很开心。就如大家所说,ThreeJs网上的资料实在太过杂乱,很难从啥都不会开始学习,我虽然只是个后台程序猿,但也愿意贡献自己的一份力量帮助大家共同学习。因为我是从事仓库系统的开发,3D仓库显示只是其中的一小部分功能,到这章为止已经是我现在能做到的全部内容了。未来可能会开发其他的内容,比如场景切换等等。最后还是要谢谢大家支持!
我跟广大学习ThreeJs的初学者一样,仍带着懵懂的心去探索这片新大陆,CSDN上的许多前辈都给了我很多关键的灵感和技术方法,如果大家有兴趣,也可以互相交流成长,欢迎大家指导咨询。PS:大家有兴趣可以点进去我的头像,陆陆续续也写了十来篇了。
链接:使用ThreeJs从零开始构建3D智能仓库——第一章: 点我跳转.
链接:使用ThreeJs从零开始构建3D智能仓库——第二章: 点我跳转.
链接:使用ThreeJs从零开始构建3D智能仓库——第三章: 点我跳转.
链接:使用ThreeJs从零开始构建3D智能仓库——第四章: 点我跳转.
链接:使用ThreeJs从零开始构建3D智能仓库——第五章: 点我跳转.

©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页