main.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. package main
  2. import (
  3. "fmt"
  4. "image"
  5. "image/color"
  6. "log"
  7. "math"
  8. "math/rand"
  9. "os"
  10. "strings"
  11. "time"
  12. "gioui.org/app"
  13. "gioui.org/f32"
  14. "gioui.org/font/gofont"
  15. "gioui.org/io/key"
  16. "gioui.org/layout"
  17. "gioui.org/op"
  18. "gioui.org/op/clip"
  19. "gioui.org/op/paint"
  20. "gioui.org/text"
  21. "gioui.org/unit"
  22. "gioui.org/widget/material"
  23. )
  24. const MAX_LOOP = 100_000_000
  25. type C = layout.Context
  26. type D = layout.Dimensions
  27. var darkPalette = material.Palette{
  28. Bg: color.NRGBA{R: 0x28, G: 0x28, B: 0x28, A: 0xff},
  29. Fg: color.NRGBA{R: 0xeb, G: 0xdb, B: 0xb2, A: 0xff},
  30. ContrastBg: color.NRGBA{R: 0x48, G: 0x85, B: 0x88, A: 0xff},
  31. ContrastFg: color.NRGBA{R: 0xeb, G: 0xdb, B: 0xb2, A: 0xff},
  32. }
  33. func main() {
  34. go func() {
  35. window := new(app.Window)
  36. window.Option(app.Title("Not-a-graph-layout-algorithm graph layout algorithm"))
  37. err := run(window)
  38. if err != nil {
  39. log.Fatal(err)
  40. }
  41. os.Exit(0)
  42. }()
  43. app.Main()
  44. }
  45. func run(window *app.Window) error {
  46. theme := material.NewTheme()
  47. theme.Palette = darkPalette
  48. theme.Shaper =
  49. text.NewShaper(text.WithCollection(gofont.Collection()))
  50. seed := int64(0)
  51. g := Graph{}
  52. g.random = rand.New(rand.NewSource(seed))
  53. g.offsets = g._offsets[:]
  54. g.degrees = g._degrees[:]
  55. g.nodes = g._nodes[:0]
  56. g.allocs = g._allocs[:]
  57. g.edges = g._edges[:0]
  58. g.adjacency = g._adjacency[:0]
  59. title := material.Body1(theme, "")
  60. title.Color = theme.Fg
  61. title.Alignment = text.Start
  62. title.WrapPolicy = text.WrapWords
  63. simPeriod := 50 * time.Millisecond
  64. lastStep := time.Now()
  65. bumpShift := 1
  66. lastSecond := time.Now()
  67. frameCnt, stepCnt := 0, 0
  68. fps, sps := 0, 0
  69. simFinishedTime := time.Now()
  70. simCnt := int64(0)
  71. var (
  72. stepOutput strings.Builder
  73. statusText strings.Builder
  74. ops op.Ops
  75. screensaverMode bool = true
  76. finished,
  77. resetRequested,
  78. bumpRequested bool
  79. )
  80. for {
  81. switch e := window.Event().(type) {
  82. case app.DestroyEvent:
  83. return e.Err
  84. case app.FrameEvent:
  85. gtx := app.NewContext(&ops, e)
  86. // Handle input
  87. for {
  88. event, ok := e.Source.Event(
  89. key.Filter{Name: "B"},
  90. key.Filter{Name: "R"},
  91. key.Filter{Name: "S", Required: key.ModShift},
  92. key.Filter{Name: "<", Required: key.ModShift},
  93. key.Filter{Name: ">", Required: key.ModShift},
  94. )
  95. if !ok {
  96. break
  97. }
  98. switch evt := event.(type) {
  99. case key.Event:
  100. switch evt.Name {
  101. case "B":
  102. if evt.State == key.Press {
  103. bumpRequested = true
  104. }
  105. case "R":
  106. if evt.State == key.Press {
  107. resetRequested = true
  108. }
  109. case "S":
  110. if evt.State == key.Press {
  111. screensaverMode = !screensaverMode
  112. }
  113. case "<":
  114. if evt.State == key.Press {
  115. simPeriod += 50 * time.Millisecond
  116. simPeriod = min(simPeriod, 10*time.Second)
  117. }
  118. case ">":
  119. if evt.State == key.Press {
  120. simPeriod -= 50 * time.Millisecond
  121. simPeriod = max(simPeriod, 0)
  122. }
  123. }
  124. }
  125. }
  126. if resetRequested ||
  127. (screensaverMode && finished &&
  128. time.Since(simFinishedTime) >= 5*time.Second) {
  129. (&g).Reset()
  130. resetRequested = false
  131. finished = false
  132. simCnt++
  133. }
  134. if time.Since(lastStep) >= simPeriod {
  135. if int(g.order) == cap(g.nodes) && bumpRequested {
  136. bump(&g, bumpShift)
  137. //bumpShift++
  138. bumpRequested = false
  139. }
  140. stepOutput.Reset()
  141. stepOutput.WriteString((&g).Step())
  142. //statusText.WriteString((&g).Semilattice())
  143. lastStep = time.Now()
  144. addNodeAtRandom(&g)
  145. stepCnt++
  146. }
  147. paint.Fill(&ops, theme.Bg)
  148. layout.UniformInset(
  149. unit.Dp(10),
  150. ).Layout(gtx, func(gtx C) D {
  151. return layout.Flex{
  152. Axis: layout.Horizontal,
  153. Alignment: layout.Start,
  154. Spacing: layout.SpaceEnd,
  155. }.Layout(gtx,
  156. layout.Rigid(func(gtx C) D {
  157. return (&g).Layout(gtx, theme)
  158. }),
  159. layout.Rigid(func(gtx C) D {
  160. return layout.Flex{
  161. Axis: layout.Vertical,
  162. Alignment: layout.Start,
  163. Spacing: layout.SpaceEnd,
  164. }.Layout(gtx,
  165. layout.Rigid(func(gtx C) D {
  166. return (&g).LayoutDist(gtx, theme)
  167. }),
  168. layout.Flexed(1.0, func(gtx C) D {
  169. return title.Layout(gtx)
  170. }),
  171. )
  172. }),
  173. )
  174. })
  175. if time.Since(lastSecond) >= time.Second {
  176. fps, sps = frameCnt, stepCnt
  177. frameCnt, stepCnt = 0, 0
  178. lastSecond = time.Now()
  179. }
  180. statusText.Reset()
  181. statusText.WriteString(fmt.Sprintf("seed %d, simulation %d\n", seed, simCnt+1))
  182. statusText.WriteString(fmt.Sprintf("simulation rate: %s per step\n", simPeriod))
  183. statusText.WriteString(fmt.Sprintf("fps=%d; sps=%d\n", fps, sps))
  184. statusText.WriteString(stepOutput.String())
  185. title.Text = statusText.String()
  186. if int(g.order) == cap(g.nodes) && !finished {
  187. finished = true
  188. simFinishedTime = time.Now()
  189. }
  190. e.Source.Execute(op.InvalidateCmd{})
  191. e.Frame(gtx.Ops)
  192. frameCnt++
  193. }
  194. }
  195. }
  196. func bump(g *Graph, shift int) int {
  197. root := -1
  198. for n := 0; n < int(g.order); n++ {
  199. if g.nodes[n] == 1 {
  200. root = n
  201. break
  202. }
  203. }
  204. if root < 0 {
  205. return root
  206. }
  207. g.nodes[root] <<= shift
  208. return root
  209. nodes := make([]NodeId, 1, g.order)
  210. nextNodes := make(map[NodeId]struct{}, g.order)
  211. nodes[0] = NodeId(root)
  212. for len(nodes) > 0 {
  213. for _, n := range nodes {
  214. for _, e := range g.edges[g.offsets[n]:g.offsets[n+1]] {
  215. nextNodes[e] = struct{}{}
  216. }
  217. }
  218. nodes = nodes[:len(nextNodes)]
  219. i := 0
  220. for n := range nextNodes {
  221. delete(nextNodes, n)
  222. nodes[i] = n
  223. i++
  224. if log2(g.nodes[n])+uint32(shift) >= 30 {
  225. log.Printf("Out-of-address-space condition for node %d", n)
  226. continue
  227. }
  228. g.nodes[n] <<= shift
  229. }
  230. }
  231. return root
  232. }
  233. func addNodeAtRandom(g *Graph) (order uint32) {
  234. if g.order == uint32(cap(g.nodes)) ||
  235. g.size == uint32(cap(g.edges)) {
  236. return g.order
  237. }
  238. /*threshold := 0.05 * float64(g.order)
  239. if g.random.ExpFloat64() <= threshold {
  240. return g.order
  241. }*/
  242. g.edges = g.edges[:cap(g.edges)]
  243. g.nodes = g.nodes[:cap(g.nodes)]
  244. i := int(g.order)
  245. g.nodes[i] = 1
  246. g.offsets[i] = EdgeId(g.size)
  247. for j := i + 1; j < len(g.nodes); j++ {
  248. g.edges[g.size] = NodeId(j)
  249. distance := float64(j - i)
  250. edgeProbability := 1 / (1 + math.Exp(0.25*distance))
  251. if g.random.Float64() > edgeProbability {
  252. continue
  253. }
  254. g.degrees[i]++
  255. g.size++
  256. }
  257. g.order++
  258. g.offsets[g.order] = EdgeId(g.size)
  259. g.nodes = g.nodes[:g.order]
  260. g.edges = g.edges[:g.size]
  261. return g.order
  262. }
  263. type NodeId uint32
  264. type EdgeId uint32
  265. const GraphOrder int = 128
  266. type Graph struct {
  267. _adjacency [GraphOrder * (GraphOrder - 1)]NodeId
  268. _edges [(GraphOrder * (GraphOrder - 1)) >> 1]NodeId
  269. _nodes [GraphOrder]uint32
  270. _allocs [GraphOrder]uint32
  271. _offsets [GraphOrder + 1]EdgeId
  272. _degrees [GraphOrder]byte
  273. random *rand.Rand
  274. adjacency []NodeId // Bidirectional adjacent node ids
  275. edges []NodeId // Unidirectional adjacent node ids
  276. nodes []uint32
  277. allocs []uint32 // Address allocation state, per node
  278. offsets []EdgeId
  279. degrees []byte // Node degrees
  280. size uint32 // Number of edges added to graph
  281. order uint32 // Number of nodes added to graph
  282. source NodeId // A selected source node
  283. target NodeId // A selected target node
  284. }
  285. func (g *Graph) Reset() {
  286. for i := range g.offsets {
  287. g.offsets[i] = 0
  288. }
  289. for i := range g.nodes {
  290. g.nodes[i] = 0
  291. }
  292. for i := range g.allocs {
  293. g.allocs[i] = 0
  294. }
  295. for i := range g.edges {
  296. g.edges[i] = 0
  297. }
  298. g.nodes = g.nodes[:0]
  299. g.edges = g.edges[:0]
  300. g.order, g.size = 0, 0
  301. }
  302. func (g *Graph) Semilattice() string {
  303. var b strings.Builder
  304. for n := 0; n < len(g.nodes); n++ {
  305. b.WriteString(fmt.Sprintf("nodeAddr=%d neighbors=", g.nodes[n]))
  306. for _, e := range g.edges[g.offsets[n]:g.offsets[n+1]] {
  307. if uint32(e) >= g.order {
  308. continue
  309. }
  310. b.WriteString(fmt.Sprintf("%d, ", g.nodes[e]))
  311. }
  312. b.WriteString("\n")
  313. }
  314. return b.String()
  315. }
  316. func (g *Graph) Step() string {
  317. for n := range g.nodes {
  318. if g.allocs[n] == 0 {
  319. g.allocs[n] = 1 // Seed allocator
  320. }
  321. shift := log2(g.nodes[n]) + 1
  322. if log2(g.allocs[n])+shift >= 30 {
  323. log.Printf("Out-of-address-space condition for node %d", n)
  324. continue
  325. }
  326. edges := g.edges[g.offsets[n]:g.offsets[n+1]]
  327. shiftStart := 1
  328. // Shift a node containing exactly one 1
  329. if (n == 0 || g.nodes[n] > 1) && // root or assigned
  330. g.nodes[n]&(g.nodes[n]-1) == 0 { // ...contains one 1
  331. for i := 0; i < min(shiftStart, len(edges)); i++ {
  332. e := edges[i]
  333. if uint32(e) < g.order && g.nodes[n] >= g.nodes[e] {
  334. g.nodes[e] = g.nodes[n] << (i + 1)
  335. }
  336. }
  337. }
  338. // Assign addresses to neighbors
  339. for i := shiftStart; i < len(edges); i++ {
  340. nextAlloc := (g.allocs[n] << shift) | g.nodes[n]
  341. e := edges[i]
  342. if uint32(e) >= g.order ||
  343. (g.nodes[e] != 1 && nextAlloc >= g.nodes[e]) {
  344. continue
  345. }
  346. g.nodes[e] = nextAlloc
  347. g.allocs[n]++
  348. }
  349. }
  350. return fmt.Sprintf("nodes=%v\nedges=%v", g.nodes, g.edges)
  351. }
  352. func (g *Graph) Layout(
  353. gtx layout.Context,
  354. th *material.Theme,
  355. ) D {
  356. squareMax := min(
  357. gtx.Constraints.Max.X,
  358. gtx.Constraints.Max.Y,
  359. )
  360. convFactor := float32(squareMax) / 65535.0
  361. circle := clip.Ellipse{Max: image.Pt(16, 16)}
  362. // Outer margin
  363. defer op.Offset(image.Pt(10, 10)).Push(gtx.Ops).Pop()
  364. var path clip.Path
  365. for n := 0; n < len(g.nodes); n++ {
  366. x1, y1 := idToPt(g.nodes[n], convFactor)
  367. for e := range g.edges[g.offsets[n]:g.offsets[n+1]] {
  368. if uint32(e) >= g.order {
  369. continue
  370. }
  371. if g.nodes[n] == 1 { // Skip orphans and roots
  372. continue
  373. }
  374. color := th.Fg
  375. if distance(g.nodes[n], g.nodes[e]) > 4 {
  376. color = th.ContrastBg
  377. color.A = 0xd0
  378. }
  379. x2, y2 := idToPt(g.nodes[e], convFactor)
  380. path.Begin(gtx.Ops)
  381. path.MoveTo(f32.Pt(float32(x1), float32(y1)))
  382. path.LineTo(f32.Pt(float32(x2), float32(y2)))
  383. paint.FillShape(gtx.Ops, color,
  384. clip.Stroke{
  385. Path: path.End(),
  386. Width: float32(unit.Dp(1)),
  387. }.Op(),
  388. )
  389. }
  390. // Circle size is proportional to address length.
  391. length := log2(g.nodes[n])
  392. shift := int(length >> 3)
  393. scaled := circle
  394. scaled.Max.X = scaled.Max.X >> shift
  395. scaled.Max.Y = scaled.Max.Y >> shift
  396. halfW, halfH := scaled.Max.X>>1, scaled.Max.Y>>1
  397. pos := image.Pt(gtx.Dp(x1)-halfW, gtx.Dp(y1)-halfH)
  398. macro := op.Record(gtx.Ops)
  399. stack := op.Offset(pos).Push(gtx.Ops)
  400. nodeColor := th.Fg
  401. if length >= 24 {
  402. nodeColor = color.NRGBA{R: 0xaa, G: 0, B: 0xaa, A: 0xff}
  403. }
  404. paint.FillShape(gtx.Ops, nodeColor, scaled.Op(gtx.Ops))
  405. stack.Pop()
  406. c := macro.Stop()
  407. op.Defer(gtx.Ops, c)
  408. }
  409. return layout.Dimensions{Size: image.Pt(squareMax, squareMax)}
  410. }
  411. func (g *Graph) LayoutDist(
  412. gtx layout.Context,
  413. th *material.Theme,
  414. ) D {
  415. dims := f32.Pt(400, 100)
  416. width := gtx.Dp(unit.Dp(dims.X))
  417. height := gtx.Dp(unit.Dp(dims.Y))
  418. defer op.Affine( // Flip y-axis and scale
  419. f32.Affine2D{}.
  420. Scale(f32.Pt(dims.X/2, dims.Y/2), f32.Pt(1.0, -1.0)),
  421. ).Push(gtx.Ops).Pop()
  422. barWidth := 0
  423. if g.order > 0 {
  424. pxPerBucket := float32(width) / float32(g.order)
  425. barWidth = gtx.Dp(unit.Dp(pxPerBucket))
  426. }
  427. color := color.NRGBA{R: 0xaa, G: 0, B: 0xaa, A: 0xff}
  428. maxOutDegree := 0
  429. for n := range g.nodes {
  430. d := g.offsets[n+1] - g.offsets[n]
  431. maxOutDegree = max(maxOutDegree, int(d))
  432. }
  433. convFactor := float64(height) / float64(maxOutDegree)
  434. for n := range g.nodes {
  435. cnt := g.offsets[n+1] - g.offsets[n]
  436. y := gtx.Dp(unit.Dp(float64(cnt) * convFactor))
  437. stack := clip.Rect{
  438. Min: image.Pt(n*barWidth, 0),
  439. Max: image.Pt((n+1)*barWidth, y),
  440. }.Push(gtx.Ops)
  441. paint.ColorOp{Color: color}.Add(gtx.Ops)
  442. paint.PaintOp{}.Add(gtx.Ops)
  443. stack.Pop()
  444. }
  445. return layout.Dimensions{Size: image.Pt(width, height)}
  446. }
  447. func distance(a, b uint32) uint32 {
  448. var s, e uint32 = 1, 2
  449. for s < 32 && a&(e-1) == b&(e-1) {
  450. s++
  451. e <<= 1
  452. }
  453. s -= 1
  454. return ones(a>>s) + ones(b>>s)
  455. }
  456. func ones(x uint32) uint32 {
  457. var c uint32
  458. for c = 0; x != 0; c++ {
  459. x &= x - 1 // clear the least significant bit set
  460. }
  461. return c
  462. }
  463. func idToPt(id uint32, convFactor float32) (x, y unit.Dp) {
  464. x0, y0 := demorton(reverse32(id))
  465. x, y = unit.Dp(float32(x0)*convFactor),
  466. unit.Dp(float32(y0)*convFactor)
  467. return
  468. }
  469. func log2(x uint32) uint32 {
  470. b := []uint32{0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000}
  471. s := []uint32{1, 2, 4, 8, 16}
  472. var r uint32 = 0
  473. for i := 4; i >= 0; i-- {
  474. if x&b[i] != 0 {
  475. x >>= s[i]
  476. r |= s[i]
  477. }
  478. }
  479. return r
  480. }
  481. func reverse32(x uint32) uint32 {
  482. var rev uint32
  483. var mask uint32 = 0xff
  484. for i := 0; i < 4; i++ {
  485. rshift := 8 * i
  486. lshift := 8 * (3 - i)
  487. rev |= uint32(reverse((x&mask)>>rshift)) << lshift
  488. mask <<= 8
  489. }
  490. return rev
  491. }
  492. func reverse(b uint32) byte {
  493. b = (b * 0x0802 & 0x22110) | (b * 0x8020 & 0x88440)
  494. b *= 0x10101
  495. b >>= 16
  496. return byte(b)
  497. }
  498. func demorton(z uint32) (x, y uint16) {
  499. res := (uint64(z) | (uint64(z) << 31)) & 0x5555555555555555
  500. res = (res | (res >> 1)) & 0x3333333333333333
  501. res = (res | (res >> 2)) & 0x0f0f0f0f0f0f0f0f
  502. res = (res | (res >> 4)) & 0x00ff00ff00ff00ff
  503. res = res | (res >> 8)
  504. x = uint16(res)
  505. y = uint16(res >> 32)
  506. return
  507. }
  508. func morton(x, y uint16) uint32 {
  509. var res uint64
  510. res = uint64(x) | (uint64(y) << 32)
  511. res = (res | (res << 8)) & 0x00ff00ff00ff00ff
  512. res = (res | (res << 4)) & 0x0f0f0f0f0f0f0f0f
  513. res = (res | (res << 2)) & 0x3333333333333333
  514. res = (res | (res << 1)) & 0x5555555555555555
  515. return uint32(res | (res >> 31))
  516. }