通過矩陣變化實現的 3D 模型自動佈局

圖撲軟體發表於2020-04-06

在數學中,矩陣是以行和列排列的數字,符號或表示式的矩形陣列,任何矩陣都可以通過相關欄位的標量乘以元素。矩陣的主要應用是表示線性變換,即 f(x)= 4 x 等線性函式的推廣。例如,旋轉的載體在三維空間是一個線性變換,這可以通過一個表示旋轉矩陣:如果v是一個列向量描述(只有一列的矩陣)的位置在空間中的點,該產品器 Rv 是列向量描述旋轉後該點的位置。兩個變換矩陣的乘積是表示兩個變換組成的矩陣。矩陣的另一個應用是線性方程組的解。如果矩陣是方形的,可以通過計算其行列式來推斷它的一些性質。例如,當且僅當其行列式不為零時,方陣具有相反的 。從矩陣的特徵值和特徵向量中可以看到線性變換的幾何結構(以及其他資訊)。

矩陣的應用可以在大多數科學領域找到。在物理學的每一個分支,包括經典力學,光學,電磁學,量子力學和量子電動力學,都被用來研究物理現象,比如剛體的運動。在計算機圖形學中,它們被用來操作三維模型並將其投影到二維螢幕上。在概率論和統計學中,隨機矩陣被用來描述概率集; 例如,它們在 PageRank 演算法中用於對 Google 搜尋中的頁面進行排名。矩陣演算概括經典分析概念,如衍生物和指數更高的尺寸。矩陣在經濟學中被用來描述經濟關係系統。
在這裡插入圖片描述
首先,這個實現真的是非常的酷,我從來不知道分子中的引力和斥力結合矩陣能做出這麼炫酷的效果,而且還是程式碼量非常少的情況下,這個例子在醫療界還有生物界應該是應用非常廣泛的,但是如果好好利用,在工業方面也一定有不小的成就。

http://www.hightopo.com/demo/pipeline/index.html
在這裡插入圖片描述
接下來我的任務就是幫助你們也能輕鬆地實現這個效果,HT 將彈力佈局的 js 檔案放到了一個獨立的檔案中,在引用這個 js 檔案需要先引入 ht.js,因為我們還做了一個 form 表單,所以也要引入 form 表單的 js 檔案, 並不是所有的 HT 封裝的的功能都需要引入一個特別的 js 檔案,需要引入額外的 js 檔案的手冊中頂部都會有介紹,這裡 forcelayout 彈力佈局的 js 和 form 表單的 js 的排放順序沒有關係:

<script src="../../guide/lib/core/ht.js"></script>
<script src="../../guide/lib/plugin/ht-forcelayout.js"></script>
<script src="../../guide/lib/plugin/ht-form.js"></script>

首先我們定義一個顏色陣列變數,儲存各個彈力球的顏色,還定義了一個隨機函式,用來生成數隨機的陣列中的顏色:

var colorList = ['#FFAFA4', '#B887C5', '#B9EA9C', '#CFD9E7', '#4590B8', '#FF9C30'],
colorLen = colorList.length;
var randomColor = function() {
    var ran = Math.random() * colorLen;
    return colorList[Math.floor(ran)]; // 隨機6種顏色
};

接著建立彈力球,簡單生成一個 3D 節點,通過設定這個節點的 style 樣式屬性來控制節點的顯示方式,其中將“shape3d”設定為“sphere”即可將 ht.Node 六面體變成 3D 球體模型,再設定“shape3d”屬性為前面定義的隨機顏色,s3 也是 HT 封裝的設定 3D 節點大小的函式,最後將這個節點新增進資料模型 dataModel 中:

var createNode = function(dm) { // 建立 node 節點 圓
    var node = new ht.Node();
    node.s({
        'shape3d': 'sphere',
	'shape3d.color': randomColor()
    });
    node.s3(40, 40, 40);
    dm.add(node);
    return node;
};

現在效果圖上出現的還有各個彈力球之間的連線,這個連線我們一看就覺得很不一般,也是通過構造一個一個節點,這個節點是通過 HT for Web 建模手冊 setShape3dModel函式自定義的一個 3D 模型,將其命名為‘custom’:

ht.Default.setShape3dModel( // 建立模型 根據 xy 平面的曲線,環繞一週形成 3D 模型。
    'custom', ht.Default.createRingModel( [0.5, 0.5, -0.2, 0, 0.5, -0.5], [1, 3] )
);

HT 將使用者自定義的屬性和 HT 預設的屬性呼叫方法分為 node.a 和 node.s 這樣就能將兩者有效地區分開來,我們在建立管線的時候就呼叫了這種方法:

var updatePipeline = function(edge) { // 重新設定 edge 的樣式
    var pipeline = edge.a('pipeline');
    pipeline.s3(1, 1, 1);
    pipeline.p3(0, 0, 0);

    var node1 = edge.getSourceAgent(), // 獲取圖形上連線的起始節點
    node2 = edge.getTargetAgent();
    pipeline.s('mat', createMatrix(node1.p3(), node2.p3(), 20)); // 3d 整體圖形矩陣變化
};

我們知道,矩陣能描述任意線性變換。線性變換保留了直線和平行線,線性變換保留直線的同時,其他的幾何性質如長度、角度、面積和體積可能被變換改變了。簡單的說,線性變換可能“拉伸”座標系,但不會“彎曲”或“卷折”座標系。這個函式主要是將我們的連線線在拖動彈力球后被拖拉的連線線的進行一個“變化矩陣”的操作,變化矩陣也是 HT 封裝的 ht.Default.createMatrix 函式,能夠非常輕鬆地建立出變化矩陣:

var createMatrix = function(p1, p2, width) { // createMatrix(array, matrix) 將一組 JSON 描述的縮放、移動和旋轉等操作轉換成對應的變化矩陣
    var vec = [p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2]],
        dist = ht.Default.getDistance(p1, p2); // 獲取兩點之間距離,或向量長度
    return ht.Default.createMatrix({
        s3: [width, dist, width],
	r3: [Math.PI/2 - Math.asin(vec[1]/dist), Math.atan2(vec[0], vec[2]), 0],
	rotationMode: 'xyz',
	t3: [(p1[0]+p2[0])/2, (p1[1]+p2[1])/2, (p1[2]+p2[2])/2]
    });
};

基礎配件全部定義完畢,接著就是將“shape3d”屬性設定為自定義的 3D 模型“custom” ,並將“layoutable”屬性設定為“false”阻止圖元參與佈局,並將點之間的連線通過edge.a(‘pipeline’, node)重新重新整理,並新增進資料模型 dataModel 中:

var createEdge = function(dm, node1, node2) { // 建立‘custom’模型的 edge
    var node = new ht.Node();
    node.s({
        'shape3d': 'custom',
        'shape3d.color': '#ECE0D4',
        'layoutable': false
    });
    dm.add(node);

    var edge = new ht.Edge(node1, node2);
    edge.a('pipeline', node);
    edge.s('edge.color', 'rgba(0, 0, 0, 0)');
    dm.add(edge);
    return edge;
};

我們還可以在工業上用 HeatMap 熱圖上做文章,效果依舊很炫,具體地址 http://hightopo.com/guide/guide/plugin/forcelayout/examples/example_heatmap3d.html
在這裡插入圖片描述
這裡我們還是專注地繼續完成這個例子。

介面上的圖形全部繪製完畢,剩下的就只有 form 表單,首先將 form 表單新增進 HTML 頁面,用的是 HT 封裝的 ht.widget.FormPane 函式:

var formPane = new ht.widget.FormPane();
formPane.setWidth(230);
formPane.setHeight(125);
formPane.addToDOM();

記住,form 表單要設定寬高,不然不顯示。

form 表單新增行是通過 addRow 函式,我們重點來說一下下面的幾行,Color、Range 和 Intensity,這三個名字主要是用來控制“頭燈”的。在 HT 中直接通過 setHeadlightColor/setHeadlightRange/setHeadlightIntensity 三個函式來控制“頭燈”的顏色、範圍以及燈的強度,onValueChanged 屬性,顧名思義屬性值改變之後觸發的事件:

['Color', 'Range', 'Intensity'].forEach(function(name) {
    var obj = { id: name },
    func = function(oV, nV) {
        g3d['setHeadlight' + name](nV); // === g3d.setHeadlightColor(nV)/g3d.setHeadlightRange(nV)/g3d.setHeadlightIntensity(nV)
    };
    if (name === 'Color')
        obj.colorPicker = { // ht.widget.ColorPicker為顏色選擇框 
	    instant: true,
	    value: g3d['getHeadlight' + name](), // === g3d.getHeadlightColor()
	    onValueChanged: func
	};
	else 
	    obj.slider = { // 滑動條
	        min: 0,
		max: name === 'Range' ? 20000 : 3,
		step: 0.1,
		value: g3d['getHeadlight' + name](),
		onValueChanged: func
	    };
    formPane.addRow([ name, obj ], [ 70, 0.1 ]);
});

slider 和 colorPicker 都是 HT 自定義的滑動條和顏色選擇器,詳情請參考 HT for Web 表單手冊

如果還有不懂的請諮詢我,或者直接上 HT for Web 官網查閱手冊。

相關文章