成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

d3-force 力導(dǎo)圖 源碼解讀與原理分析【一】

luckyyulin / 2607人閱讀

摘要:設(shè)置力導(dǎo)圖點陣中心碰撞其他引用模塊構(gòu)造常量函數(shù)微小晃動隨機數(shù)四叉樹模塊設(shè)置力導(dǎo)圖點陣中心此處代碼使用的是單例對象模式,讀者要注意,切勿與類對象理解混了。

首先先推薦一下某呆翻譯的d3-force的中文文檔:https://github.com/xswei/D3-V... 。
在我們解讀源碼前還請讀者先熟悉一下force相關(guān)的API,以及es6語法 .

如有分析不當之處還請留言指出,謝謝~

那我們進入正題吧

D3-force 有什么

我們來看一下index.js 這個文件大家可以理解為force的對外統(tǒng)一出口。當然你也可以自定義使用這些模塊。

// index.js
export {default as forceCenter} from "./src/center"; // 設(shè)置力導(dǎo)圖點陣中心
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";        // 微小晃動隨機數(shù)
import {quadtree} from "d3-quadtree"; // 四叉樹
模塊1:center.js 設(shè)置力導(dǎo)圖點陣中心

此處代碼使用的是單例對象模式,讀者要注意,切勿與類對象理解混了。

export default function(x, y) {
  var nodes; // 使用閉包構(gòu)建私有變量,存儲nodes。

  if (x == null) x = 0; // 力導(dǎo)圖中心位置 x 默認值為0
  if (y == null) y = 0; // 力導(dǎo)圖中心位置 y 默認值為0
   
  // force 單例對象
  function force() {
    var i,
        n = nodes.length,
        node,   // 臨時變量用于循環(huán)
        sx = 0, // 臨時變量用于計算
        sy = 0; // 臨時變量用于計算
    
    for (i = 0; i < n; ++i) {
      // sx = sum(node.x);  節(jié)點x之和
      // sy = sum(node.y);  節(jié)點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 是點陣的中心x坐標;sy / n 是點陣的中心y坐標。
      // node.x = node.x + (x - (sx / n)); 該計算與此表達式等價,這樣讀者應(yīng)該更好理解;
      // 坐標加減即平移坐標,即將整個點陣中心平移到坐標(x,y)
      node = nodes[i], node.x -= sx, node.y -= sy;
    }
  }
  // 初始化,為nodes私有變量賦值
  force.initialize = function(_) {
    nodes = _;
  };
  // 如果傳入?yún)?shù)x則設(shè)置x,否則返回當前力導(dǎo)圖中心位置 x
  force.x = function(_) {
    return arguments.length ? (x = +_, force) : x; 
  };
  // 如果傳入?yún)?shù)y則設(shè)置y,否則返回當前力導(dǎo)圖中心位置 y
  force.y = function(_) {
    return arguments.length ? (y = +_, force) : y;  
  };

  return force; // 返回 force對象
}
模塊2:constant.js 創(chuàng)建一個常量函數(shù)
// 構(gòu)造一個返回參數(shù)值的常量函數(shù)
// let a = constant(123); a() 輸出: 123
export default function(x) {
  return function() {
    return x;
  };
}
模塊3:jiggle.js 微小晃動隨機數(shù)
// jiggle.js
// 微小晃動隨機數(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";        // 微小晃動隨機數(shù)
import {quadtree} from "d3-quadtree"; // 四叉樹

// vx vy 是指當前節(jié)點的運動速度
function x(d) {
  return d.x + d.vx; // 運動一步 x + vx 
}

function y(d) {
  return d.y + d.vy; // 運動一步 y + vy
}

export default function(radius) {
  var nodes,
      radii,
      strength = 1, // 力度
      iterations = 1;
      
  // radius 設(shè)置默認值,值類型為常量函數(shù);
  if (typeof radius !== "function") radius = constant(radius == null ? 1 : +radius);

  // 單例對象模式
  function force() {
    var i, n = nodes.length,
        tree,
        node,
        xi,
        yi,
        ri,  // 半徑
        ri2; // 半徑平方
// -------------- 四叉樹相關(guān),**后文有詳細分析**----------
    for (var k = 0; k < iterations; ++k) {
      // 以x,y訪問器構(gòu)建一個四叉樹,即節(jié)點運動到下一步位置為坐標(就像我們走夜路,探出一步試試看)
      // visitAfter是后序遍歷樹的節(jié)點,執(zhí)行prepare為每個節(jié)點求半徑r,參數(shù)為各個節(jié)點,
      // 返回樹的跟節(jié)點root。
      tree = quadtree(nodes, x, y).visitAfter(prepare); 
      // for循環(huán)普通遍歷節(jié)點
      for (i = 0; i < n; ++i) {
        node = nodes[i];
        ri = radii[node.index], ri2 = ri * ri; // r平方(勾股定理用)
        xi = node.x + node.vx;// 運動一步 x + vx 
        yi = node.y + node.vy;// 運動一步 y + vy 
        // 前序遍歷所有節(jié)點,apply返回true則不訪問其子節(jié)點
        tree.visit(apply); 
      }
    }

    function apply(quad, x0, y0, x1, y1) {
      var data = quad.data, rj = quad.r, r = ri + rj;// 兩個點與其作用域構(gòu)成兩個圓,請參考之前的文章,圓與圓的碰撞測驗。
      if (data) { // 存在data即葉子節(jié)點,每個葉子節(jié)點為一個坐標點
        if (data.index > node.index) {
          // 因為這是二重循環(huán),所有index小于自身的點坐標已經(jīng)與自身判斷過了,此處是為了避免重復(fù)測驗
          // 設(shè)第一重循環(huán)Node[i]為節(jié)點A(xi,yi) 第二重循環(huán)為節(jié)點B(data.x,data.y)下一步運動(+=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:實際距離平方,r:半徑之和
            if (x === 0) x = jiggle(), l += x * x; // 避免x值為0
            if (y === 0) y = jiggle(), l += y * y; // 避免y值為0
            // strength:碰撞力的強度,可以理解為兩點之間的斥力系數(shù)
            // 見后文碰撞測驗的圖
            // l = 重疊長度/實際距離 * 碰撞力度
            // 重疊約多,斥力越大。斥力影響點的運動速度
            l = (r - (l = Math.sqrt(l))) / l * strength; 
            // 根據(jù)求出的斥力計算AB點新的運動速度與方向
            // A點x方向的運動速度 
            // A速度 += B速度 -= 使得AB兩點往相反方向運動。注意,這里的x是B到A的距離,所有是A+= ,B-=
            // 但斥力的原因會使得節(jié)點的vx ,vy 趨近于0.
            // node.vx = B-A點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é)點,這里需要讀者理解四叉樹【后面一篇文章會講解】
      // 節(jié)點坐標為中心的正方形,如果沒有覆蓋到該父節(jié)點的正方形區(qū)域,這改點與此父節(jié)點的任何子節(jié)點都不會發(fā)生碰撞,則無需遍歷其子節(jié)點校驗。
      // 返回true 不遍歷子節(jié)點
      // 這也是v4 相比v3對性能優(yōu)化最重要的一個步驟,成倍的減少計算量
      return x0 > xi + r || x1 < xi - r || y0 > yi + r || y1 < yi - r;
    }
  }
  // 遍歷樹節(jié)點過濾器,返回true節(jié)點不可見
  function prepare(quad) {
    // quad.data是葉子節(jié)點才有的,所以這里是判斷是否是葉子節(jié)點
    if (quad.data) return quad.r = radii[quad.data.index];
    for (var i = quad.r = 0; i < 4; ++i) {
      // 因為是后序遍歷,所以節(jié)點的葉子節(jié)點一定在之前已經(jīng)遍歷過。
      // 取葉子節(jié)點四個象限最大的r
      if (quad[i] && quad[i].r > quad.r) {
        quad.r = quad[i].r;
      }
    }
  }
//---------------------------------------------------------------------------------------
  function initialize() {
    if (!nodes) return; // 判斷是否有節(jié)點
    var i, n = nodes.length, node;
    radii = new Array(n);
    // 按照node.index索引排序nodes 并又 radius【后文解析】 計算出半徑 后 存儲在 radii 
    for (i = 0; i < n; ++i) node = nodes[i], radii[node.index] = +radius(node, i, nodes);
  }

  force.initialize = function(_) {
    nodes = _; // 賦值節(jié)點
    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(_) {
    // 前端加+號 將字符串轉(zhuǎn)為number  +"123" === 123
    // 有參數(shù):
    // 執(zhí)行1:(radius = typeof _ === "function" ? _ : constant(+_)
     //radius 值是一個返回自身的函數(shù)
    // 執(zhí)行2:initialize()
    // 執(zhí)行3:return force
    // 無參數(shù):
    // 執(zhí)行:return radius
    return arguments.length ? (radius = typeof _ === "function" ? _ : constant(+_), initialize(), force) : radius;
  };

  return force;
}


碰撞測驗

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/81936.html

相關(guān)文章

  • d3-force 導(dǎo)圖 源碼解讀原理分析【二 : 四叉樹()】

    摘要:我們在上文源碼解析發(fā)現(xiàn)版的節(jié)點碰撞采用四叉樹進行了優(yōu)化。那么版本的力導(dǎo)圖具體和版的有何不同點呢,四叉樹又如何優(yōu)化碰撞校驗的呢原文鏈接被重命名為。性能的提高歸功于的新的四叉樹。 我們在上文源碼解析發(fā)現(xiàn)v4版的節(jié)點碰撞采用四叉樹進行了優(yōu)化。那么V4版本的力導(dǎo)圖具體和v3版的有何不同點呢,四叉樹又如何優(yōu)化碰撞校驗的呢? v3-force VS v4-force https://github...

    pepperwang 評論0 收藏0
  • 高并發(fā)

    摘要:表示的是兩個,當其中任意一個計算完并發(fā)編程之是線程安全并且高效的,在并發(fā)編程中經(jīng)??梢娝氖褂?,在開始分析它的高并發(fā)實現(xiàn)機制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購,是兩個比較典型的互聯(lián)網(wǎng)高并發(fā)場景。 干貨:深度剖析分布式搜索引擎設(shè)計 分布式,高可用,和機器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽名字多牛逼,來,我們一步一步來擊破前兩個名詞,今天我們首先來說說分布式。 探究...

    supernavy 評論0 收藏0
  • 高并發(fā)

    摘要:表示的是兩個,當其中任意一個計算完并發(fā)編程之是線程安全并且高效的,在并發(fā)編程中經(jīng)??梢娝氖褂茫陂_始分析它的高并發(fā)實現(xiàn)機制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購,是兩個比較典型的互聯(lián)網(wǎng)高并發(fā)場景。 干貨:深度剖析分布式搜索引擎設(shè)計 分布式,高可用,和機器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽名字多牛逼,來,我們一步一步來擊破前兩個名詞,今天我們首先來說說分布式。 探究...

    ddongjian0000 評論0 收藏0
  • 高并發(fā)

    摘要:表示的是兩個,當其中任意一個計算完并發(fā)編程之是線程安全并且高效的,在并發(fā)編程中經(jīng)??梢娝氖褂?,在開始分析它的高并發(fā)實現(xiàn)機制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購,是兩個比較典型的互聯(lián)網(wǎng)高并發(fā)場景。 干貨:深度剖析分布式搜索引擎設(shè)計 分布式,高可用,和機器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽名字多牛逼,來,我們一步一步來擊破前兩個名詞,今天我們首先來說說分布式。 探究...

    wangdai 評論0 收藏0
  • ??思維導(dǎo)圖整理大廠面試高頻數(shù)組10: 3種方法徹底解決中位數(shù)問題, 力扣4??

    此專欄文章是對力扣上算法題目各種方法的總結(jié)和歸納, 整理出最重要的思路和知識重點并以思維導(dǎo)圖形式呈現(xiàn), 當然也會加上我對導(dǎo)圖的詳解. 目的是為了更方便快捷的記憶和回憶算法重點(不用每次都重復(fù)看題解), 畢竟算法不是做了一遍就能完全記住的. 所以本文適合已經(jīng)知道解題思路和方法, 想進一步加強理解和記憶的朋友, 并不適合第一次接觸此題的朋友(可以根據(jù)題號先去力扣看看官方題解, 然后再看本文內(nèi)容). 關(guān)...

    XanaHopper 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<