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 (71 lines)
#import "@preview/cetz:0.5.3": canvas, draw
#import draw: bezier, circle, content, line

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

#canvas({
  let radius = .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: radius, 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)

  // 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 * radius, cy + (ty - cy) / len * radius)
  }
  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 * radius * calc.cos(angle + offset),
      center.at(1) + dist * radius * 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)),
    ("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 (from, to) in (("d2", "d3"), ("d6", "d3"), ("d5", "d6")) { line(from, to, ..arrow) }

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

  content(
    (1.5 * dx, -dy - radius - 0.3),
    anchor: "north",
    [nodes $d_i$: web documents #h(1.5em) arrows: hyperlinks (loops: self-links)],
  )
})