home

Web Graph

Creator: Petar Veličković (original)

A directed web graph whose nodes are documents and edges are hyperlinks, including self-links, the structure underlying the PageRank algorithm.


Web Graph

Download

PNG PDF SVG

Code

web-graph.typ (85 lines)

#import "@preview/cetz:0.5.2": canvas, draw
#import draw: bezier, circle, content, line

#set page(width: auto, height: auto, margin: 8pt, fill: none)
#set text(size: 12pt)

#canvas({
  let r = .42
  let (dx, dy) = (2.3, 1.85)
  let nodes = (
    d0: (0, 0),
    d1: (0, -dy),
    d2: (dx, 0),
    d3: (2 * dx, 0),
    d4: (3 * dx, -dy),
    d5: (3 * dx, dy),
    d6: (3 * dx, 0),
  )
  for (idx, (name, pos)) in nodes.pairs().enumerate() {
    circle(
      pos,
      radius: r,
      fill: rgb("#cdd3da").transparentize(40%),
      stroke: 1.1pt,
      name: name,
    )
    content(name, $d_#idx$)
  }

  let arrow = (
    mark: (end: "stealth", fill: black, scale: .7),
    stroke: black + 1.4pt,
  )
  let straight(from, to) = line(from, to, ..arrow)

  // bezier doesn't clip at node borders like line does, so start/end curved
  // edges on the node rim in the direction of the control point
  let rim(name, toward) = {
    let (cx, cy) = nodes.at(name)
    let (tx, ty) = toward
    let len = calc.sqrt((tx - cx) * (tx - cx) + (ty - cy) * (ty - cy))
    (cx + (tx - cx) / len * r, cy + (ty - cy) / len * r)
  }
  let curved(from, to, ctrl) = bezier(
    rim(from, ctrl),
    rim(to, ctrl),
    ctrl,
    ..arrow,
  )

  // self loop drawn as a bezier starting/ending on the node boundary
  let self-loop(name, angle) = {
    let center = nodes.at(name)
    let endpoint(offset, dist) = (
      center.at(0) + dist * r * calc.cos(angle + offset),
      center.at(1) + dist * r * calc.sin(angle + offset),
    )
    bezier(
      endpoint(-14deg, 1),
      endpoint(14deg, 1),
      endpoint(-24deg, 3.3),
      endpoint(24deg, 3.3),
      ..arrow,
    )
  }

  for (from, to, ctrl) in (
    ("d2", "d0", (dx / 2, 1.0)),
    ("d0", "d2", (dx / 2, -1.0)),
    ("d1", "d2", (dx * .85, -dy * .85)),
  ) { curved(from, to, ctrl) }
  for (from, to) in (("d2", "d3"), ("d6", "d3"), ("d5", "d6")) {
    straight(from, to)
  }
  for (from, to, ctrl) in (
    ("d3", "d4", (2.35 * dx, -dy * .9)),
    ("d4", "d6", (3 * dx + .85, -dy / 2)),
    ("d6", "d4", (3 * dx - .85, -dy / 2)),
  ) { curved(from, to, ctrl) }

  for (name, angle) in (("d1", 180deg), ("d2", 90deg), ("d3", 90deg), ("d5", 0deg), ("d6", 0deg)) {
    self-loop(name, angle)
  }
})