摘要:設(shè)置力導(dǎo)圖點(diǎn)陣中心碰撞其他引用模塊構(gòu)造常量函數(shù)微小晃動(dòng)隨機(jī)數(shù)四叉樹模塊設(shè)置力導(dǎo)圖點(diǎn)陣中心此處代碼使用的是單例對(duì)象模式,讀者要注意,切勿與類對(duì)象理解混了。
首先先推薦一下某呆翻譯的d3-force的中文文檔:https://github.com/xswei/D3-V... 。
在我們解讀源碼前還請(qǐng)讀者先熟悉一下force相關(guān)的API,以及es6語法 .
如有分析不當(dāng)之處還請(qǐng)留言指出,謝謝~
那我們進(jìn)入正題吧
D3-force 有什么我們來看一下index.js 這個(gè)文件大家可以理解為force的對(duì)外統(tǒng)一出口。當(dāng)然你也可以自定義使用這些模塊。
// index.js export {default as forceCenter} from "./src/center"; // 設(shè)置力導(dǎo)圖點(diǎn)陣中心 export {default as forceCollide} from "./src/collide"; // 碰撞 export {default as forceLink} from "./src/link"; export {default as forceManyBody} from "./src/manyBody"; export {default as forceSimulation} from "./src/simulation"; export {default as forceX} from "./src/x"; export {default as forceY} from "./src/y";
其他引用模塊
//collide.js import constant from "./constant"; // 構(gòu)造常量函數(shù) import jiggle from "./jiggle"; // 微小晃動(dòng)隨機(jī)數(shù) import {quadtree} from "d3-quadtree"; // 四叉樹模塊1:center.js 設(shè)置力導(dǎo)圖點(diǎn)陣中心
此處代碼使用的是單例對(duì)象模式,讀者要注意,切勿與類對(duì)象理解混了。
export default function(x, y) { var nodes; // 使用閉包構(gòu)建私有變量,存儲(chǔ)nodes。 if (x == null) x = 0; // 力導(dǎo)圖中心位置 x 默認(rèn)值為0 if (y == null) y = 0; // 力導(dǎo)圖中心位置 y 默認(rèn)值為0 // force 單例對(duì)象 function force() { var i, n = nodes.length, node, // 臨時(shí)變量用于循環(huán) sx = 0, // 臨時(shí)變量用于計(jì)算 sy = 0; // 臨時(shí)變量用于計(jì)算 for (i = 0; i < n; ++i) { // sx = sum(node.x); 節(jié)點(diǎn)x之和 // sy = sum(node.y); 節(jié)點(diǎn)y之和 node = nodes[i], sx += node.x, sy += node.y; } for (sx = sx / n - x, sy = sy / n - y, i = 0; i < n; ++i) { // sx / n 是點(diǎn)陣的中心x坐標(biāo);sy / n 是點(diǎn)陣的中心y坐標(biāo)。 // node.x = node.x + (x - (sx / n)); 該計(jì)算與此表達(dá)式等價(jià),這樣讀者應(yīng)該更好理解; // 坐標(biāo)加減即平移坐標(biāo),即將整個(gè)點(diǎn)陣中心平移到坐標(biāo)(x,y) node = nodes[i], node.x -= sx, node.y -= sy; } } // 初始化,為nodes私有變量賦值 force.initialize = function(_) { nodes = _; }; // 如果傳入?yún)?shù)x則設(shè)置x,否則返回當(dāng)前力導(dǎo)圖中心位置 x force.x = function(_) { return arguments.length ? (x = +_, force) : x; }; // 如果傳入?yún)?shù)y則設(shè)置y,否則返回當(dāng)前力導(dǎo)圖中心位置 y force.y = function(_) { return arguments.length ? (y = +_, force) : y; }; return force; // 返回 force對(duì)象 }模塊2:constant.js 創(chuàng)建一個(gè)常量函數(shù)
// 構(gòu)造一個(gè)返回參數(shù)值的常量函數(shù) // let a = constant(123); a() 輸出: 123 export default function(x) { return function() { return x; }; }模塊3:jiggle.js 微小晃動(dòng)隨機(jī)數(shù)
// jiggle.js // 微小晃動(dòng)隨機(jī)數(shù) export default function() { return (Math.random() - 0.5) * 1e-6; // 1e-6 ==> 1*10的-6次方 }模塊4:collide.js 碰撞
import constant from "./constant"; // 構(gòu)造常量函數(shù) import jiggle from "./jiggle"; // 微小晃動(dòng)隨機(jī)數(shù) import {quadtree} from "d3-quadtree"; // 四叉樹 // vx vy 是指當(dāng)前節(jié)點(diǎn)的運(yùn)動(dòng)速度 function x(d) { return d.x + d.vx; // 運(yùn)動(dòng)一步 x + vx } function y(d) { return d.y + d.vy; // 運(yùn)動(dòng)一步 y + vy } export default function(radius) { var nodes, radii, strength = 1, // 力度 iterations = 1; // radius 設(shè)置默認(rèn)值,值類型為常量函數(shù); if (typeof radius !== "function") radius = constant(radius == null ? 1 : +radius); // 單例對(duì)象模式 function force() { var i, n = nodes.length, tree, node, xi, yi, ri, // 半徑 ri2; // 半徑平方 // -------------- 四叉樹相關(guān),**后文有詳細(xì)分析**---------- for (var k = 0; k < iterations; ++k) { // 以x,y訪問器構(gòu)建一個(gè)四叉樹,即節(jié)點(diǎn)運(yùn)動(dòng)到下一步位置為坐標(biāo)(就像我們走夜路,探出一步試試看) // visitAfter是后序遍歷樹的節(jié)點(diǎn),執(zhí)行prepare為每個(gè)節(jié)點(diǎn)求半徑r,參數(shù)為各個(gè)節(jié)點(diǎn), // 返回樹的跟節(jié)點(diǎn)root。 tree = quadtree(nodes, x, y).visitAfter(prepare); // for循環(huán)普通遍歷節(jié)點(diǎn) for (i = 0; i < n; ++i) { node = nodes[i]; ri = radii[node.index], ri2 = ri * ri; // r平方(勾股定理用) xi = node.x + node.vx;// 運(yùn)動(dòng)一步 x + vx yi = node.y + node.vy;// 運(yùn)動(dòng)一步 y + vy // 前序遍歷所有節(jié)點(diǎn),apply返回true則不訪問其子節(jié)點(diǎn) tree.visit(apply); } } function apply(quad, x0, y0, x1, y1) { var data = quad.data, rj = quad.r, r = ri + rj;// 兩個(gè)點(diǎn)與其作用域構(gòu)成兩個(gè)圓,請(qǐng)參考之前的文章,圓與圓的碰撞測(cè)驗(yàn)。 if (data) { // 存在data即葉子節(jié)點(diǎn),每個(gè)葉子節(jié)點(diǎn)為一個(gè)坐標(biāo)點(diǎn) if (data.index > node.index) { // 因?yàn)檫@是二重循環(huán),所有index小于自身的點(diǎn)坐標(biāo)已經(jīng)與自身判斷過了,此處是為了避免重復(fù)測(cè)驗(yàn) // 設(shè)第一重循環(huán)Node[i]為節(jié)點(diǎn)A(xi,yi) 第二重循環(huán)為節(jié)點(diǎn)B(data.x,data.y)下一步運(yùn)動(dòng)(+=vx,+=vy) var x = xi - data.x - data.vx, // Ax - Bx y = yi - data.y - data.vy, // Ay - By l = x * x + y * y; // 勾股定理 d^2 = x^2 +y^2 if (l < r * r) { // 判斷是否碰撞,如果碰撞執(zhí)行以下,l:實(shí)際距離平方,r:半徑之和 if (x === 0) x = jiggle(), l += x * x; // 避免x值為0 if (y === 0) y = jiggle(), l += y * y; // 避免y值為0 // strength:碰撞力的強(qiáng)度,可以理解為兩點(diǎn)之間的斥力系數(shù) // 見后文碰撞測(cè)驗(yàn)的圖 // l = 重疊長(zhǎng)度/實(shí)際距離 * 碰撞力度 // 重疊約多,斥力越大。斥力影響點(diǎn)的運(yùn)動(dòng)速度 l = (r - (l = Math.sqrt(l))) / l * strength; // 根據(jù)求出的斥力計(jì)算AB點(diǎn)新的運(yùn)動(dòng)速度與方向 // A點(diǎn)x方向的運(yùn)動(dòng)速度 // A速度 += B速度 -= 使得AB兩點(diǎn)往相反方向運(yùn)動(dòng)。注意,這里的x是B到A的距離,所有是A+= ,B-= // 但斥力的原因會(huì)使得節(jié)點(diǎn)的vx ,vy 趨近于0. // node.vx = B-A點(diǎn)x方向距離 *= 斥力 * B半徑平方(rj = B半徑平方)/( A半徑平方+B半徑平方);r = B半徑平方/( A半徑平方+B半徑平方) node.vx += (x *= l) * (r = (rj *= rj) / (ri2 + rj)); // 同x方向 node.vy += (y *= l) * r; data.vx -= x * (r = 1 - r); data.vy -= y * r; } } return; } // 如果是父節(jié)點(diǎn),這里需要讀者理解四叉樹【后面一篇文章會(huì)講解】 // 節(jié)點(diǎn)坐標(biāo)為中心的正方形,如果沒有覆蓋到該父節(jié)點(diǎn)的正方形區(qū)域,這改點(diǎn)與此父節(jié)點(diǎn)的任何子節(jié)點(diǎn)都不會(huì)發(fā)生碰撞,則無需遍歷其子節(jié)點(diǎn)校驗(yàn)。 // 返回true 不遍歷子節(jié)點(diǎn) // 這也是v4 相比v3對(duì)性能優(yōu)化最重要的一個(gè)步驟,成倍的減少計(jì)算量 return x0 > xi + r || x1 < xi - r || y0 > yi + r || y1 < yi - r; } } // 遍歷樹節(jié)點(diǎn)過濾器,返回true節(jié)點(diǎn)不可見 function prepare(quad) { // quad.data是葉子節(jié)點(diǎn)才有的,所以這里是判斷是否是葉子節(jié)點(diǎn) if (quad.data) return quad.r = radii[quad.data.index]; for (var i = quad.r = 0; i < 4; ++i) { // 因?yàn)槭呛笮虮闅v,所以節(jié)點(diǎn)的葉子節(jié)點(diǎn)一定在之前已經(jīng)遍歷過。 // 取葉子節(jié)點(diǎn)四個(gè)象限最大的r if (quad[i] && quad[i].r > quad.r) { quad.r = quad[i].r; } } } //--------------------------------------------------------------------------------------- function initialize() { if (!nodes) return; // 判斷是否有節(jié)點(diǎn) var i, n = nodes.length, node; radii = new Array(n); // 按照node.index索引排序nodes 并又 radius【后文解析】 計(jì)算出半徑 后 存儲(chǔ)在 radii for (i = 0; i < n; ++i) node = nodes[i], radii[node.index] = +radius(node, i, nodes); } force.initialize = function(_) { nodes = _; // 賦值節(jié)點(diǎn) initialize(); // 初始化 }; force.iterations = function(_) { // get or set iterations (迭代次數(shù)) return arguments.length ? (iterations = +_, force) : iterations; }; force.strength = function(_) { // get or set strength(力度) return arguments.length ? (strength = +_, force) : strength; }; force.radius = function(_) { // 前端加+號(hào) 將字符串轉(zhuǎn)為number +"123" === 123 // 有參數(shù): // 執(zhí)行1:(radius = typeof _ === "function" ? _ : constant(+_) //radius 值是一個(gè)返回自身的函數(shù) // 執(zhí)行2:initialize() // 執(zhí)行3:return force // 無參數(shù): // 執(zhí)行:return radius return arguments.length ? (radius = typeof _ === "function" ? _ : constant(+_), initialize(), force) : radius; }; return force; }
碰撞測(cè)驗(yàn)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/81936.html
摘要:我們?cè)谏衔脑创a解析發(fā)現(xiàn)版的節(jié)點(diǎn)碰撞采用四叉樹進(jìn)行了優(yōu)化。那么版本的力導(dǎo)圖具體和版的有何不同點(diǎn)呢,四叉樹又如何優(yōu)化碰撞校驗(yàn)的呢原文鏈接被重命名為。性能的提高歸功于的新的四叉樹。 我們?cè)谏衔脑创a解析發(fā)現(xiàn)v4版的節(jié)點(diǎn)碰撞采用四叉樹進(jìn)行了優(yōu)化。那么V4版本的力導(dǎo)圖具體和v3版的有何不同點(diǎn)呢,四叉樹又如何優(yōu)化碰撞校驗(yàn)的呢? v3-force VS v4-force https://github...
摘要:表示的是兩個(gè),當(dāng)其中任意一個(gè)計(jì)算完并發(fā)編程之是線程安全并且高效的,在并發(fā)編程中經(jīng)常可見它的使用,在開始分析它的高并發(fā)實(shí)現(xiàn)機(jī)制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購(gòu),是兩個(gè)比較典型的互聯(lián)網(wǎng)高并發(fā)場(chǎng)景。 干貨:深度剖析分布式搜索引擎設(shè)計(jì) 分布式,高可用,和機(jī)器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽名字多牛逼,來,我們一步一步來擊破前兩個(gè)名詞,今天我們首先來說說分布式。 探究...
摘要:表示的是兩個(gè),當(dāng)其中任意一個(gè)計(jì)算完并發(fā)編程之是線程安全并且高效的,在并發(fā)編程中經(jīng)常可見它的使用,在開始分析它的高并發(fā)實(shí)現(xiàn)機(jī)制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購(gòu),是兩個(gè)比較典型的互聯(lián)網(wǎng)高并發(fā)場(chǎng)景。 干貨:深度剖析分布式搜索引擎設(shè)計(jì) 分布式,高可用,和機(jī)器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽名字多牛逼,來,我們一步一步來擊破前兩個(gè)名詞,今天我們首先來說說分布式。 探究...
摘要:表示的是兩個(gè),當(dāng)其中任意一個(gè)計(jì)算完并發(fā)編程之是線程安全并且高效的,在并發(fā)編程中經(jīng)常可見它的使用,在開始分析它的高并發(fā)實(shí)現(xiàn)機(jī)制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購(gòu),是兩個(gè)比較典型的互聯(lián)網(wǎng)高并發(fā)場(chǎng)景。 干貨:深度剖析分布式搜索引擎設(shè)計(jì) 分布式,高可用,和機(jī)器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽名字多牛逼,來,我們一步一步來擊破前兩個(gè)名詞,今天我們首先來說說分布式。 探究...
此專欄文章是對(duì)力扣上算法題目各種方法的總結(jié)和歸納, 整理出最重要的思路和知識(shí)重點(diǎn)并以思維導(dǎo)圖形式呈現(xiàn), 當(dāng)然也會(huì)加上我對(duì)導(dǎo)圖的詳解. 目的是為了更方便快捷的記憶和回憶算法重點(diǎn)(不用每次都重復(fù)看題解), 畢竟算法不是做了一遍就能完全記住的. 所以本文適合已經(jīng)知道解題思路和方法, 想進(jìn)一步加強(qiáng)理解和記憶的朋友, 并不適合第一次接觸此題的朋友(可以根據(jù)題號(hào)先去力扣看看官方題解, 然后再看本文內(nèi)容). 關(guān)...
閱讀 1584·2021-09-26 09:46
閱讀 2670·2021-09-07 09:59
閱讀 2754·2021-09-07 09:59
閱讀 1874·2019-08-30 14:20
閱讀 928·2019-08-26 13:39
閱讀 3179·2019-08-26 12:24
閱讀 777·2019-08-26 11:55
閱讀 1219·2019-08-23 16:49