summaryrefslogtreecommitdiff
path: root/src/graph.js
blob: fdac9791c6fde22832315c1635215dc1382e51eb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import * as THREE from "three";
import ForceGraph3D from "3d-force-graph";
import SpriteText from "three-spritetext";
import { union } from "./utils/set_utils.js";

const IMG_SCALE = 20;

export default function buildGraph(db, handlers) {
  const linkable = {};
  const groups = {};
  const data = { nodes: [], links: [] };

  // console.log(db);

  /**
   * load nodes and links
   */

  db.page.forEach((item, index) => {
    const node = {
      title: item.title,
      id: index,
      data: item,
      groups: [],
    };
    data.nodes.push(node);
    for (let tagIndex = 0; tagIndex < 8; tagIndex += 1) {
      const group = item["tag_" + tagIndex];
      if (!group) continue;
      group -= 1;
      node.groups.push(group);
      if (group in groups) {
        groups[group].push(index);
      } else {
        groups[group] = [index];
      }
      if (group in linkable) {
        data.links.push({
          source: choice(linkable[group]),
          // source: linkable[group][0],
          target: index,
        });
        // option: don't link to the root node more than once
        // if (window.location.hash === "#dense" && linkable[group][0]) {
        if (linkable[group][0]) {
          linkable[group].push(index);
        } else {
          linkable[group] = [index];
        }
      } else {
        linkable[group] = [index];
      }
    }
  });

  /**
   * find common links
   */

  data.links.forEach((link) => {
    const a = data.nodes[link.source];
    const b = data.nodes[link.target];

    !a.links && (a.links = []);
    !b.links && (b.links = []);
    a.links.push(link);
    b.links.push(link);
  });

  const highlightNodes = new Set();
  const highlightLinks = new Set();
  let selectedNode = null;

  /** build group */

  let graph = ForceGraph3D();
  graph(document.querySelector("#graph"))
    .graphData(data)
    .showNavInfo(false)
    .nodeLabel((node) => node.title)
    .nodeThreeObject((node) => {
      let sprite;
      if (node.data.thumbnail?.uri) {
        const imgTexture = new THREE.TextureLoader().load(
          node.data.thumbnail.uri
        );
        // console.log(imgTexture);
        const aspect = node.data.thumbnail.width / node.data.thumbnail.height;
        const material = new THREE.SpriteMaterial({ map: imgTexture });
        sprite = new THREE.Sprite(material);
        sprite.scale.set(IMG_SCALE, IMG_SCALE / aspect);
        return sprite;
      } else {
        sprite = new SpriteText(
          node.title.split(/[ :]+/).slice(0, 3).join(" ")
        );
        sprite.material.depthWrite = false; // make sprite background transparent
        sprite.color = "#888888"; // colors[node.groups[0]]; // node.groups.length - 1]];
        sprite.textHeight = 4;
        return sprite;
      }
    })
    .onNodeClick(handlers.click);

  // graph.d3Force("charge").strength(-150);

  // camera orbit
  const distance = 250;
  let angle = 0;
  graph.cameraPosition({
    x: distance * Math.sin(angle),
    z: distance * Math.cos(angle),
  });

  // stars();
}

const randint = (limit) => Math.floor(Math.random() * limit);
const choice = (list) => list[randint(list.length)];
const commonGroups = (a, b) => union(a.groups, b.groups);