第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

d3.js 多重關(guān)系視覺/linkHorizo??ntal()/糾結(jié)樹

d3.js 多重關(guān)系視覺/linkHorizo??ntal()/糾結(jié)樹

慕無忌1623718 2023-05-19 19:47:13
我試圖模仿一個按時間段描述多種關(guān)系的視覺效果,就像這樣(時間段=世代):但是,到目前為止,我的努力還沒有成功。我仍然在瀏覽器中得到空白輸出。據(jù)我所知,所有的部分都已到位。我有我的數(shù)據(jù)levels,然后使用以下方法整理了必要的層次結(jié)構(gòu)坐標:  var links = []  nodes.forEach(d => {    d.parents.forEach(p => links.push({source: d, bundle: d.bundle, target: p}))  })和  const cluster = d3.cluster()    .size([width, height]);  const root = d3.hierarchy(links);  cluster(root);  var nodeG = svg.selectAll('.node')      .data(root.links())      .attr('class','node')      .enter()      .append('g');從這里開始,我使用了d3.linkHorizontal()我的鏈接功能:      nodeG.append("path")        .attr("class", "link")        .attr("d", d3.linkHorizontal()          .x(function(d) { return d.y; })          .y(function(d) { return d.x; }))        .style('stroke-width', '3px');從概念上講,我看不出每個節(jié)點包含多個關(guān)系會如何改變。而且,控制臺日志中沒有任何錯誤,我不確定如何進一步排除故障。問題是什么阻止了我的視覺效果按預(yù)期呈現(xiàn)(在上圖中)?如果可能的話,想要精確的復(fù)制品。編輯如果有幫助,這里是 observable 的視覺效果,但不能將其視為獨立的視覺效果。https://observablehq.com/@nitaku/tangled-tree-visualization-ii?collection=@nitaku/tangled-trees
查看完整描述

1 回答

?
茅侃侃

TA貢獻1842條經(jīng)驗 獲得超21個贊

我認為你所做的很多事情,特別是圍繞數(shù)據(jù)爭論,都是沒有必要的,特別是因為你打了電話,后來d3.hierarchy()又打了d3.cluster()電話。我已將其替換為d3.stratify(處理尚未采用正確格式的分層數(shù)據(jù))。

我也替換d3.clusterd3.tree()因為我不清楚你為什么要d3.cluster在這里使用。您的數(shù)據(jù)有多個父節(jié)點、多個根甚至浮動節(jié)點,而 d3 并不打算處理這些問題。我的解決方法是將偽節(jié)點附加到每一層,以確保只有一個節(jié)點并且所有節(jié)點始終處于正確的層級。為了確保正確繪制鏈接,我編寫了一個自定義getLinks函數(shù),可以處理多個父母。

我還編寫了一個自定義鏈接生成器,它以您希望的方式繪制鏈接。d3 在這里沒有提供太多的靈活性,但您可以使用源代碼來獲得靈感。


編輯

我改變了邏輯,更關(guān)注哪些“伙伴”有孩子,所以指向同一個孩子的兩個鏈接都處于同一級別 - 就像你的照片一樣。我還根據(jù)他們有多少合作伙伴繪制了節(jié)點,并為每個鏈接提供了一個偏移量,以便線條更加清晰。

我對節(jié)點進行了排序,以便真正的專業(yè)創(chuàng)作者位于頂部(Zeus),這樣可以提供更平衡且不那么擁擠的視圖。

const margins = {

  top: 20,

  bottom: 300,

  left: 30,

  right: 100,

};


const height = 900;

const width = 900;


const totalWidth = width + margins.left + margins.right;

const totalHeight = height + margins.top + margins.bottom;


const svg = d3.select('body')

  .append('svg')

  .attr('width', totalWidth)

  .attr('height', totalHeight);


const graphGroup = svg.append('g')

  .attr('transform', "translate(" + margins.left + "," + margins.top + ")");


const levels = [

  [{

    id: 'Chaos',

  }],

  [{

    id: 'Gaea',

    parents: ['Chaos'],

  },

    {

      id: 'Uranus',

    },

  ],

  [{

    id: 'Oceanus',

    parents: ['Gaea', 'Uranus'],

  },

    {

      id: 'Thethys',

      parents: ['Gaea', 'Uranus'],

    },

    {

      id: 'Pontus',

    },

    {

      id: 'Rhea',

      parents: ['Gaea', 'Uranus'],

    },

    {

      id: 'Cronus',

      parents: ['Gaea', 'Uranus'],

    },

    {

      id: 'Coeus',

      parents: ['Gaea', 'Uranus'],

    },

    {

      id: 'Phoebe',

      parents: ['Gaea', 'Uranus'],

    },

    {

      id: 'Crius',

      parents: ['Gaea', 'Uranus'],

    },

    {

      id: 'Hyperion',

      parents: ['Gaea', 'Uranus'],

    },

    {

      id: 'Iapetus',

      parents: ['Gaea', 'Uranus'],

    },

    {

      id: 'Thea',

      parents: ['Gaea', 'Uranus'],

    },

    {

      id: 'Themis',

      parents: ['Gaea', 'Uranus'],

    },

    {

      id: 'Mnemosyne',

      parents: ['Gaea', 'Uranus'],

    },

  ],

  [{

    id: 'Doris',

    parents: ['Oceanus', 'Thethys'],

  },

    {

      id: 'Neures',

      parents: ['Pontus', 'Gaea'],

    },

    {

      id: 'Dionne',

    },

    {

      id: 'Demeter',

      parents: ['Rhea', 'Cronus'],

    },

    {

      id: 'Hades',

      parents: ['Rhea', 'Cronus'],

    },

    {

      id: 'Hera',

      parents: ['Rhea', 'Cronus'],

    },

    {

      id: 'Alcmene',

    },

    {

      id: 'Zeus',

      parents: ['Rhea', 'Cronus'],

    },

    {

      id: 'Eris',

    },

    {

      id: 'Leto',

      parents: ['Coeus', 'Phoebe'],

    },

    {

      id: 'Amphitrite',

    },

    {

      id: 'Medusa',

    },

    {

      id: 'Poseidon',

      parents: ['Rhea', 'Cronus'],

    },

    {

      id: 'Hestia',

      parents: ['Rhea', 'Cronus'],

    },

  ],

  [{

    id: 'Thetis',

    parents: ['Doris', 'Neures'],

  },

    {

      id: 'Peleus',

    },

    {

      id: 'Anchises',

    },

    {

      id: 'Adonis',

    },

    {

      id: 'Aphrodite',

      parents: ['Zeus', 'Dionne'],

    },

    {

      id: 'Persephone',

      parents: ['Zeus', 'Demeter'],

    },

    {

      id: 'Ares',

      parents: ['Zeus', 'Hera'],

    },

    {

      id: 'Hephaestus',

      parents: ['Zeus', 'Hera'],

    },

    {

      id: 'Hebe',

      parents: ['Zeus', 'Hera'],

    },

    {

      id: 'Hercules',

      parents: ['Zeus', 'Alcmene'],

    },

    {

      id: 'Megara',

    },

    {

      id: 'Deianira',

    },

    {

      id: 'Eileithya',

      parents: ['Zeus', 'Hera'],

    },

    {

      id: 'Ate',

      parents: ['Zeus', 'Eris'],

    },

    {

      id: 'Leda',

    },

    {

      id: 'Athena',

      parents: ['Zeus'],

    },

    {

      id: 'Apollo',

      parents: ['Zeus', 'Leto'],

    },

    {

      id: 'Artemis',

      parents: ['Zeus', 'Leto'],

    },

    {

      id: 'Triton',

      parents: ['Poseidon', 'Amphitrite'],

    },

    {

      id: 'Pegasus',

      parents: ['Poseidon', 'Medusa'],

    },

    {

      id: 'Orion',

      parents: ['Poseidon'],

    },

    {

      id: 'Polyphemus',

      parents: ['Poseidon'],

    },

  ],

  [{

    id: 'Deidamia',

  },

    {

      id: 'Achilles',

      parents: ['Peleus', 'Thetis'],

    },

    {

      id: 'Creusa',

    },

    {

      id: 'Aeneas',

      parents: ['Anchises', 'Aphrodite'],

    },

    {

      id: 'Lavinia',

    },

    {

      id: 'Eros',

      parents: ['Hephaestus', 'Aphrodite'],

    },

    {

      id: 'Helen',

      parents: ['Leda', 'Zeus'],

    },

    {

      id: 'Menelaus',

    },

    {

      id: 'Polydueces',

      parents: ['Leda', 'Zeus'],

    },

  ],

  [{

    id: 'Andromache',

  },

    {

      id: 'Neoptolemus',

      parents: ['Deidamia', 'Achilles'],

    },

    {

      id: 'Aeneas(2)',

      parents: ['Creusa', 'Aeneas'],

    },

    {

      id: 'Pompilius',

      parents: ['Creusa', 'Aeneas'],

    },

    {

      id: 'Iulus',

      parents: ['Lavinia', 'Aeneas'],

    },

    {

      id: 'Hermione',

      parents: ['Helen', 'Menelaus'],

    },

  ],

];


levels.unshift([]);


// We add one pseudo node to every level to deal with parentless nodes

levels.forEach((l, i) => {

  l.forEach((n, j) => {

    n.level = i;

    if (n.parents !== undefined) {

      n.parent = n.parents[0];

    } else {

      n.parent = `pseudo-${i - 1}`;

    }

  });

  l.unshift({

    id: `pseudo-${i}`,

    parent: i > 0 ? `pseudo-${i - 1}` : "",

    level: i

  });

});


const nodes = levels.flat();

const colours = d3.scaleOrdinal()

  .domain(nodes.filter(n => n.parents)

    .map(n => n.parents.sort()

      .join("-")))

  .range(d3.schemePaired);


function getLinks(nodes) {

  return nodes

    .filter(n => n.data.parents !== undefined)

    .map(n => n.data.parents.map(p => ({

      source: nodes.find(n => n.id === p),

      target: n

    })))

    .flat();

}


const offsetPerPartner = 3;

const drawNodePath = d => {

  const radius = 5;

  // The number of partners determines the node height

  // But when a node has only one partner,

  // treat it the same as when it has zero

  const nPartners = (d.data.partners && d.data.partners.length > 1)

    ? d.data.partners.length

    : 0;


  // We want to centre each node

  const straightLineOffset = (nPartners * offsetPerPartner) / 2;


  const context = d3.path();

  context.moveTo(-radius, 0);

  context.lineTo(-radius, -straightLineOffset);

  context.arc(0, -straightLineOffset, radius, -Math.PI, 0);

  context.lineTo(radius, straightLineOffset);

  context.arc(0, straightLineOffset, radius, 0, Math.PI);

  context.closePath();


  return context + "";

};


const drawLinkCurve = (x0, y0, x1, y1, offset, radius) => {

  const context = d3.path();

  context.moveTo(x0, y0);

  context.lineTo(x1 - 2 * radius - offset, y0);


  // If there is not enough space to draw two corners, reduce the corner radius

  if (Math.abs(y0 - y1) < 2 * radius) {

    radius = Math.abs(y0 - y1) / 2;

  }


  if (y0 < y1) {

    context.arcTo(x1 - offset - radius, y0, x1 - offset - radius, y0 + radius, radius);

    context.lineTo(x1 - offset - radius, y1 - radius);

    context.arcTo(x1 - offset - radius, y1, x1 - offset, y1, radius);

  } else if (y0 > y1) {

    context.arcTo(x1 - offset - radius, y0, x1 - offset - radius, y0 - radius, radius);

    context.lineTo(x1 - offset - radius, y1 + radius);

    context.arcTo(x1 - offset - radius, y1, x1 - offset, y1, radius);

  }

  context.lineTo(x1, y1);

  return context + "";

};


const partnershipsPerLevel = {};

const getPartnershipOffset = (parent, partner) => {

  let partnershipId, level;

  if(partner !== undefined) {

    // On every level, every relationship gets its own offset. If a relationship

    // spans multiple levels, the furthest level is chosen

    level = Math.max(parent.depth, partner.level);

    if(!partnershipsPerLevel[level]) { partnershipsPerLevel[level] = []; }

    partnershipId = [parent.id, partner.id].sort().join("-");

  } else {

    level = parent.depth;

    if(!partnershipsPerLevel[level]) { partnershipsPerLevel[level] = []; }

    partnershipId = parent.id;

  }


  // Assume that the partnership already has a slot assigned

  const partnershipOffset = partnershipsPerLevel[level].indexOf(partnershipId);

  if(partnershipOffset === -1) {

    // Apparently not

    return partnershipsPerLevel[level].push(partnershipId) - 1;

  }

  return partnershipOffset;

}


const lineRadius = 10;

const offsetStep = 5;

const linkFn = link => {

  const thisParent = link.source;

  const partnerId = link.target.data.parents.find(p => p !== thisParent.id);

  const partners = thisParent.data.partners || [];


  // Let the first link start with this negative offset

  // But when a node has only one partner,

  // treat it the same as when it has zero

  const startOffset = (partners.length > 1)

    ? -(partners.length * offsetPerPartner) / 2

    : 0;


  const partner = partners.find(p => p.id === partnerId);


  // Chaos has no partner, nor Zeus with Athena

  const nthPartner = partner !== undefined

    ? partners.indexOf(partner)

    : (partners || []).length;

  const partnershipOffset = getPartnershipOffset(thisParent, partner);


  return drawLinkCurve(

    thisParent.y,

    thisParent.x + startOffset + offsetPerPartner * nthPartner,

    link.target.y,

    link.target.x,

    offsetStep * partnershipOffset,

    lineRadius

  );

};


function draw(root) {

  // Now every node has had it's position set, we can draw them now

  const nodes = root.descendants()

    .filter(n => !n.id.startsWith("pseudo-"));

  const links = getLinks(nodes)

    .filter(l => !l.source.id.startsWith("pseudo-"));


  const link = graphGroup.selectAll(".link")

    .data(links);

  link.exit().remove();

  link.enter()

    .append("path")

    .attr("class", "link")

    .merge(link)

    .attr("stroke", d => colours(d.target.data.parents.sort().join("-")))

    .attr("d", linkFn);


  const node = graphGroup.selectAll(".node")

    .data(nodes);

  node.exit().remove();

  const newNode = node.enter()

    .append("g")

    .attr("class", "node");


  newNode.append("path")

    .attr("d", drawNodePath);

  newNode.append("text")

    .attr("dy", -3)

    .attr("x", 6);


  newNode.merge(node)

    .attr("transform", d => `translate(${d.y},${d.x})`)

    .selectAll("text")

    .text(d => d.id);

}


const root = d3.stratify()

  .parentId(d => d.parent)

  (nodes);


// Map the different sets of parents,

// assigning each parent an array of partners

getLinks(root.descendants())

  .filter(l => l.target.data.parents)

  .forEach(l => {

    const parentNames = l.target.data.parents;

    if (parentNames.length > 1) {

      const parentNodes = parentNames.map(p => nodes.find(n => n.id === p));


      parentNodes.forEach(p => {

        if (!p.partners) {

          p.partners = [];

        }

        parentNodes

          .filter(n => n !== p && !p.partners.includes(n))

          .forEach(n => {

            p.partners.push(n);

          });

      });

    }

  });


// Take nodes with more partners first,

// also counting the partners of the children

root

  .sum(d => (d.value || 0) + (d.partners || []).length)

  .sort((a, b) => b.value - a.value);


const tree = d3.tree()

  .size([height, width])

  .separation((a, b) => {

    // More separation between nodes with many children

    const totalPartners = (a.data.partners || []).length + (b.data.partners || []).length;

    return 1 + (totalPartners / 5);

  });


draw(tree(root));

.link {

  fill: none;

  stroke-width: 2;

}


.node path {

  stroke: black;

  stroke-width: 3;

  fill: white;

}

<script src="https://d3js.org/d3.v5.js"></script>


查看完整回答
反對 回復(fù) 2023-05-19
?
開滿天機

TA貢獻1786條經(jīng)驗 獲得超13個贊

svg除 外,元素中沒有附加任何內(nèi)容graphGroup。顯然root.links()返回一個空數(shù)組,并且svg. 這也是您沒有收到任何錯誤的原因。

通過創(chuàng)建此數(shù)組并在其上迭代,如果您還更改,則可以實現(xiàn)您希望在樹中實現(xiàn)的基本形狀:

.attr("d", d3.linkHorizontal()
          .x(function(d) { return d.y; })
          .y(function(d) { return d.x; }))

和:

 .attr("d", d3.linkHorizontal()
          .source(d => [d.xs,d.ys] )
          .target(d => [d.xt,d.yt]))

您要實現(xiàn)的樹的基本形狀可以在下面的代碼片段中看到。嘗試查看此示例是否有助于根據(jù)需要設(shè)置樹的樣式。

var margins = {

  top: 20,

  bottom: 300,

  left: 30,

  right: 100

};


var height = 600;

var width = 900;


var totalWidth = width + margins.left + margins.right;

var totalHeight = height + margins.top + margins.bottom;


var svg = d3.select('body')

  .append('svg')

  .attr('width', totalWidth)

  .attr('height', totalHeight);


var graphGroup = svg.append('g')

  .attr('transform', "translate(" + margins.left + "," + margins.top + ")");


var levels = [

  [{

    id: 'Chaos'

  }],

  [{

      id: 'Gaea',

      parents: ['Chaos']

    },

    {

      id: 'Uranus'

    }

  ],

  [{

      id: 'Oceanus',

      parents: ['Gaea', 'Uranus']

    },

    {

      id: 'Thethys',

      parents: ['Gaea', 'Uranus']

    },

    {

      id: 'Pontus'

    },

    {

      id: 'Rhea',

      parents: ['Gaea', 'Uranus']

    },

    {

      id: 'Cronus',

      parents: ['Gaea', 'Uranus']

    },

    {

      id: 'Coeus',

      parents: ['Gaea', 'Uranus']

    },

    {

      id: 'Phoebe',

      parents: ['Gaea', 'Uranus']

    },

    {

      id: 'Crius',

      parents: ['Gaea', 'Uranus']

    },

    {

      id: 'Hyperion',

      parents: ['Gaea', 'Uranus']

    },

    {

      id: 'Iapetus',

      parents: ['Gaea', 'Uranus']

    },

    {

      id: 'Thea',

      parents: ['Gaea', 'Uranus']

    },

    {

      id: 'Themis',

      parents: ['Gaea', 'Uranus']

    },

    {

      id: 'Mnemosyne',

      parents: ['Gaea', 'Uranus']

    }

  ],

  [{

      id: 'Doris',

      parents: ['Oceanus', 'Thethys']

    },

    {

      id: 'Neures',

      parents: ['Pontus', 'Gaea']

    },

    {

      id: 'Dionne'

    },

    {

      id: 'Demeter',

      parents: ['Rhea', 'Cronus']

    },

    {

      id: 'Hades',

      parents: ['Rhea', 'Cronus']

    },

    {

      id: 'Hera',

      parents: ['Rhea', 'Cronus']

    },

    {

      id: 'Alcmene'

    },

    {

      id: 'Zeus',

      parents: ['Rhea', 'Cronus']

    },

    {

      id: 'Eris'

    },

    {

      id: 'Leto',

      parents: ['Coeus', 'Phoebe']

    },

    {

      id: 'Amphitrite'

    },

    {

      id: 'Medusa'

    },

    {

      id: 'Poseidon',

      parents: ['Rhea', 'Cronus']

    },

    {

      id: 'Hestia',

      parents: ['Rhea', 'Cronus']

    }

  ],

  [{

      id: 'Thetis',

      parents: ['Doris', 'Neures']

    },

    {

      id: 'Peleus'

    },

    {

      id: 'Anchises'

    },

    {

      id: 'Adonis'

    },

    {

      id: 'Aphrodite',

      parents: ['Zeus', 'Dionne']

    },

    {

      id: 'Persephone',

      parents: ['Zeus', 'Demeter']

    },

    {

      id: 'Ares',

      parents: ['Zeus', 'Hera']

    },

    {

      id: 'Hephaestus',

      parents: ['Zeus', 'Hera']

    },

    {

      id: 'Hebe',

      parents: ['Zeus', 'Hera']

    },

    {

      id: 'Hercules',

      parents: ['Zeus', 'Alcmene']

    },

    {

      id: 'Megara'

    },

    {

      id: 'Deianira'

    },

    {

      id: 'Eileithya',

      parents: ['Zeus', 'Hera']

    },

    {

      id: 'Ate',

      parents: ['Zeus', 'Eris']

    },

    {

      id: 'Leda'

    },

    {

      id: 'Athena',

      parents: ['Zeus']

    },

    {

      id: 'Apollo',

      parents: ['Zeus', 'Leto']

    },

    {

      id: 'Artemis',

      parents: ['Zeus', 'Leto']

    },

    {

      id: 'Triton',

      parents: ['Poseidon', 'Amphitrite']

    },

    {

      id: 'Pegasus',

      parents: ['Poseidon', 'Medusa']

    },

    {

      id: 'Orion',

      parents: ['Poseidon']

    },

    {

      id: 'Polyphemus',

      parents: ['Poseidon']

    }

  ],

  [{

      id: 'Deidamia'

    },

    {

      id: 'Achilles',

      parents: ['Peleus', 'Thetis']

    },

    {

      id: 'Creusa'

    },

    {

      id: 'Aeneas',

      parents: ['Anchises', 'Aphrodite']

    },

    {

      id: 'Lavinia'

    },

    {

      id: 'Eros',

      parents: ['Hephaestus', 'Aphrodite']

    },

    {

      id: 'Helen',

      parents: ['Leda', 'Zeus']

    },

    {

      id: 'Menelaus'

    },

    {

      id: 'Polydueces',

      parents: ['Leda', 'Zeus']

    }

  ],

  [{

      id: 'Andromache'

    },

    {

      id: 'Neoptolemus',

      parents: ['Deidamia', 'Achilles']

    },

    {

      id: 'Aeneas(2)',

      parents: ['Creusa', 'Aeneas']

    },

    {

      id: 'Pompilius',

      parents: ['Creusa', 'Aeneas']

    },

    {

      id: 'Iulus',

      parents: ['Lavinia', 'Aeneas']

    },

    {

      id: 'Hermione',

      parents: ['Helen', 'Menelaus']

    }

  ]

]


// precompute level depth

levels.forEach((l, i) => l.forEach(n => n.level = i));


var nodes = levels.reduce(((a, x) => a.concat(x)), []);

var nodes_index = {};

nodes.forEach(d => nodes_index[d.id] = d);


// objectification

nodes.forEach(d => {

  d.parents = (d.parents === undefined ? [] : d.parents).map(p => nodes_index[p])

})


// precompute bundles

levels.forEach((l, i) => {

  var index = {}

  l.forEach(n => {

    if (n.parents.length == 0) {

      return

    }


    var id = n.parents.map(d => d.id).sort().join('--')

    if (id in index) {

      index[id].parents = index[id].parents.concat(n.parents)

    } else {

      index[id] = {

        id: id,

        parents: n.parents.slice(),

        level: i

      }

    }

    n.bundle = index[id]

  })

  l.bundles = Object.keys(index).map(k => index[k])

  l.bundles.forEach((b, i) => b.i = i)

})


var links = []

nodes.forEach(d => {

  d.parents.forEach(p => links.push({

    source: d,

    bundle: d.bundle,

    target: p

  }))

})


var bundles = levels.reduce(((a, x) => a.concat(x.bundles)), [])


// reverse pointer from parent to bundles

bundles.forEach(b => b.parents.forEach(p => {

  if (p.bundles_index === undefined) {

    p.bundles_index = {}

  }

  if (!(b.id in p.bundles_index)) {

    p.bundles_index[b.id] = []

  }

  p.bundles_index[b.id].push(b)

}))


nodes.forEach(n => {

  if (n.bundles_index !== undefined) {

    n.bundles = Object.keys(n.bundles_index).map(k => n.bundles_index[k])

  } else {

    n.bundles_index = {}

    n.bundles = []

  }

  n.bundles.forEach((b, i) => b.i = i)

})


links.forEach(l => {

  if (l.bundle.links === undefined) {

    l.bundle.links = []

  }

  l.bundle.links.push(l)

})


// layout

const padding = 8

const node_height = 22

const node_width = 70

const bundle_width = 14

const level_y_padding = 16

const metro_d = 4

const c = 16

const min_family_height = 16


nodes.forEach(n => n.height = (Math.max(1, n.bundles.length) - 1) * metro_d)


var x_offset = padding

var y_offset = padding

levels.forEach(l => {

  x_offset += l.bundles.length * bundle_width

  y_offset += level_y_padding

  l.forEach((n, i) => {

    n.x = n.level * node_width + x_offset

    n.y = node_height + y_offset + n.height / 2


    y_offset += node_height + n.height

  })

})


var i = 0

levels.forEach(l => {

  l.bundles.forEach(b => {

    b.x = b.parents[0].x + node_width + (l.bundles.length - 1 - b.i) * bundle_width

    b.y = i * node_height

  })

  i += l.length

})


links.forEach(l => {

  l.xt = l.target.x

  l.yt = l.target.y + l.target.bundles_index[l.bundle.id].i * metro_d - l.target.bundles.length * metro_d / 2 + metro_d / 2

  l.xb = l.bundle.x

  l.xs = l.source.x

  l.ys = l.source.y

})


// compress vertical space

var y_negative_offset = 0

levels.forEach(l => {

  y_negative_offset += -min_family_height + d3.min(l.bundles, b => d3.min(b.links, link => (link.ys - c) - (link.yt + c))) || 0

  l.forEach(n => n.y -= y_negative_offset)

})


// very ugly, I know

links.forEach(l => {

  l.yt = l.target.y + l.target.bundles_index[l.bundle.id].i * metro_d - l.target.bundles.length * metro_d / 2 + metro_d / 2

  l.ys = l.source.y

  l.c1 = l.source.level - l.target.level > 1 ? node_width + c : c

  l.c2 = c

})


const cluster = d3.cluster()

  .size([width, height]);


const root = d3.hierarchy(links);

cluster(root);

let oValues = Object.values(root)[0];

let linkks = oValues.map(x => x.bundle.links);


linkks.forEach((linkk) => {

 let nodeG1 = svg.append("g")

    .selectAll("circle")

    .data(linkk)

    .join("circle")

    .attr("cx", d => d.target.x)

    .attr("cy", d => d.target.y)

    .attr("fill", "none")

    .attr("stroke", (d) => {

      return '#' + Math.floor(16777215 * Math.sin(3 * Math.PI / (5 * (parseInt(d.target.level) + 1)))).toString(16);

    })

    .attr("r", 6);

  let nodeG11 = svg.append("g")

    .selectAll("circle")

    .data(linkk)

    .join("circle")

    .attr("cx", d => d.source.x)

    .attr("cy", d => d.source.y)

    .attr("fill", "none")

    .attr("stroke", (d) => {

      return '#' + Math.floor(16777215 * Math.sin(3 * Math.PI / (5 * (parseInt(d.source.level) + 1)))).toString(16);

    })

    .attr("r", 6);



  let nodeG2 = svg.append("g")

    .attr("font-family", "sans-serif")

    .attr("font-size", 14)

    .selectAll("text")

    .data(linkk)

    .join("text")

    .attr("class", "text")

    .attr("x", d => d.target.x + padding)

    .attr("y", d => d.target.y)

    .text(d => d.target.id )

    .attr("fill", (d) => {

      return '#' + Math.floor(16777215 * Math.sin(3 * Math.PI / (5 * (parseInt(d.target.level) + 2)))).toString(16);

    });

 

 let nodeG22 = svg.append("g")

    .attr("font-family", "sans-serif")

    .attr("font-size", 14)

    .selectAll("text")

    .data(linkk)

    .join("text")

    .attr("class", "text")

    .attr("x", d => d.source.x + padding)

    .attr("y", d => d.source.y)

    .text(d => d.source.id )

    .attr("fill", (d) => {

      return '#' + Math.floor(16777215 * Math.sin(3 * Math.PI / (5 * (parseInt(d.source.level) + 1)))).toString(16);

    });

 

  let nodeG = svg.append('g')

    .attr('class', 'node')

    .selectAll("path")

    .data(linkk)

    .join('path')

    .attr("class", "link")

    .attr("d", d3.linkHorizontal()

      .source(d => [d.xs, d.ys])

      .target(d => [d.xt, d.yt]))

    .attr("fill", "none")

    .attr("stroke-opacity", 0.325)

    .attr("stroke-width", 0.75)

    .attr("stroke", (d) => {

      return '#' + Math.floor(16777215 * Math.sin(3 * Math.PI / (4 * parseInt(d.source.level)))).toString(16);

    });

});

path {

  display: block;

  z-index: 0;

}


text,

circle {

  display: block;

  z-index: 1000;

}

<script src="https://d3js.org/d3.v5.min.js"></script>


查看完整回答
反對 回復(fù) 2023-05-19
  • 1 回答
  • 0 關(guān)注
  • 289 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動學(xué)習(xí)伙伴

公眾號

掃描二維碼
關(guān)注慕課網(wǎng)微信公眾號