使用ThreeJs從零開始構建3D智能倉庫——第五章(添加貨架、貨物與側邊欄)

寫在前面

本章我們來講解下如何添加貨架貨物和顯示各類信息的側邊欄。效果如下圖所示:
在這裏插入圖片描述
其中貨架的層數和每層的貨位數量是可以自定義的,比如我這邊就是三層每層兩個貨位。方便起見我在每個貨位上都放置了貨物,大家當然可以自行選擇。左上角的側邊欄使用了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上的許多前輩都給了我很多關鍵的靈感和技術方法,如果大家有興趣,也可以互相交流成長,這裏給出我的郵箱[email protected],歡迎大家指導諮詢。
交流微信
在這裏插入圖片描述
鏈接:使用ThreeJs從零開始構建3D智能倉庫——第一章: 點我跳轉.
鏈接:使用ThreeJs從零開始構建3D智能倉庫——第二章: 點我跳轉.
鏈接:使用ThreeJs從零開始構建3D智能倉庫——第三章: 點我跳轉.
鏈接:使用ThreeJs從零開始構建3D智能倉庫——第四章: 點我跳轉.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章