Skip to content

Commit fb2ae8c

Browse files
committed
wip
1 parent 22211ae commit fb2ae8c

File tree

11 files changed

+876
-89
lines changed

11 files changed

+876
-89
lines changed

packages/viewer/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@
2020
"@pi-base/core": "workspace:*",
2121
"@sentry/browser": "^7.75.1",
2222
"bootstrap": "^4.6.2",
23+
"d3": "^7.8.5",
2324
"debug": "^4.3.4",
2425
"fromnow": "^3.0.1",
2526
"fuse.js": "^6.6.2",
2627
"hast-to-hyperscript": "^10.0.3",
2728
"hyperscript": "^2.0.2",
2829
"jquery": "1.12.4",
30+
"katex": "^0.16.9",
2931
"rehype-katex": "^6.0.3",
3032
"remark-rehype": "^10.1.0",
3133
"unified": "^10.1.2",
@@ -37,6 +39,7 @@
3739
"@sveltejs/kit": "^1.27.1",
3840
"@sveltejs/vite-plugin-svelte": "^2.4.6",
3941
"@tsconfig/svelte": "^3.0.0",
42+
"@types/d3": "^7.4.3",
4043
"@types/debug": "^4.1.10",
4144
"@types/hyperscript": "0.0.6",
4245
"@types/katex": "^0.16.5",

packages/viewer/public/global.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,14 @@
4646
.description {
4747
margin-bottom: 1rem;
4848
}
49+
50+
.links line {
51+
stroke: #999;
52+
stroke-opacity: 0.6;
53+
}
54+
55+
.nodes circle {
56+
stroke: #fff;
57+
stroke-width: 1.5px;
58+
}
59+

packages/viewer/src/app.html

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,25 @@
44
<meta charset="utf-8" />
55
<meta name="viewport" content="width=device-width" />
66

7-
<title>π-Base</title>
7+
<title>π-Base</title>
88

9-
<meta name="author" content="James Dabbs" />
10-
<meta name="description"
11-
content="A community database of topological theorems and spaces, with powerful search and automated proof deduction." />
12-
<meta property="og:type" content="website" />
13-
<meta property="og:site_name" content="π-Base" />
14-
<meta property="og:url" name="twitter:url" content="https://topology.pi-base.org" />
15-
<meta property="og:title" name="twitter:title" content="π-Base" />
16-
<meta property="og:description" name="twitter:description"
17-
content="A community database of topological theorems and spaces, with powerful search and automated proof deduction." />
18-
<meta property="og:image" content="%sveltekit.assets%/pi-base.png" />
19-
<meta name="twitter:site" content="π-Base" />
20-
<meta name="twitter:creator" content="@jamesdabbs" />
9+
<meta name="author" content="James Dabbs" />
10+
<meta name="description"
11+
content="A community database of topological theorems and spaces, with powerful search and automated proof deduction." />
12+
<meta property="og:type" content="website" />
13+
<meta property="og:site_name" content="π-Base" />
14+
<meta property="og:url" name="twitter:url" content="https://topology.pi-base.org" />
15+
<meta property="og:title" name="twitter:title" content="π-Base" />
16+
<meta property="og:description" name="twitter:description"
17+
content="A community database of topological theorems and spaces, with powerful search and automated proof deduction." />
18+
<meta property="og:image" content="%sveltekit.assets%/pi-base.png" />
19+
<meta name="twitter:site" content="π-Base" />
20+
<meta name="twitter:creator" content="@jamesdabbs" />
2121

2222
<link rel="icon" href="%sveltekit.assets%/favicon.ico" />
2323

24-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css" integrity="sha384-Xi8rHCmBmhbuyyhbI88391ZKP2dmfnOl4rT9ZfRI7mLTdk1wblIUnrIq35nqwEvC" crossorigin="anonymous">
25-
<link rel='stylesheet' href='/global.css'>
24+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css" integrity="sha384-Xi8rHCmBmhbuyyhbI88391ZKP2dmfnOl4rT9ZfRI7mLTdk1wblIUnrIq35nqwEvC" crossorigin="anonymous">
25+
<link rel='stylesheet' href='/global.css'>
2626

2727
%sveltekit.head%
2828
</head>

packages/viewer/src/components/Theorems/Table.svelte

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
<script lang="ts">
2-
import type { Theorem } from '@/models'
2+
import type { Property, Theorem } from '@/models'
33
import { Formula, Link } from '../Shared'
4+
import type { Readable } from 'svelte/store'
45
56
export let theorems: Theorem[] = []
7+
export let small = false
8+
export let selected: Readable<Theorem | Property> | undefined = undefined
69
</script>
710

8-
<table class="table">
11+
<table class="table" class:table-sm={small}>
912
<thead>
1013
<tr>
1114
<th>Id</th>
@@ -15,7 +18,7 @@
1518
</thead>
1619
<tbody>
1720
{#each theorems as theorem (theorem.id)}
18-
<tr>
21+
<tr class:table-warning={$selected === theorem}>
1922
<td>
2023
<Link.Theorem {theorem} />
2124
</td>
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
<script lang="ts">
2+
import { onMount } from 'svelte'
3+
import * as d3 from 'd3'
4+
import type { Property, Theorem } from '@/models'
5+
import type { Writable } from 'svelte/store'
6+
7+
import {
8+
type Node,
9+
type Link,
10+
type Graph,
11+
drawLinkWithMidpoint,
12+
renderLatex,
13+
pin,
14+
} from './graph'
15+
16+
export let selected: Writable<Property | Theorem>
17+
export let graph: Graph
18+
19+
let el: SVGSVGElement
20+
21+
const width = 600
22+
const height = 600
23+
24+
// Assumptions will be snapped to x = -bound, while the derived trait
25+
// will be located at x = +bound
26+
const bound = 200
27+
28+
// Maximum width of node label text
29+
const textWidth = 500
30+
31+
$: pin(graph, bound)
32+
33+
onMount(() => {
34+
const color = d3.scaleOrdinal(d3.schemeTableau10)
35+
36+
const simulation = d3
37+
.forceSimulation(graph.nodes)
38+
// Space out properties
39+
.force(
40+
'charge',
41+
d3.forceManyBody<Node>().strength(d => -100 * (d.pinned ? 10 : 3)),
42+
)
43+
// Keep linked properties close together
44+
.force(
45+
'link',
46+
d3.forceLink<Node, Link>(graph.links).id(d => d.property.id.toString()),
47+
)
48+
49+
// Setup the SVG palette and primary shape types
50+
const svg = d3
51+
.select(el)
52+
.attr('width', width)
53+
.attr('height', height)
54+
.attr('viewBox', [-width / 2, -height / 2, width, height])
55+
.attr('style', 'max-width: 100%; width: 100%; height: 100%;')
56+
57+
// Labeling property nodes is tricky, for a few reasons:
58+
//
59+
// While we'd _like_ labels to be middle-anchored text nodes, if we want
60+
// them to contain katex-rendered math, we need to attach foreignObjects to
61+
// hold HTML nodes.
62+
//
63+
// Because we can't middle-anchor those nodes, we kludge around it by
64+
// providing the foreignObjects with a generous bounding box and
65+
// text-aligning inside them.
66+
//
67+
// We _don't_ want these bounding boxes to intercept clicks or mouseovers on
68+
// the graph, so we have to keep them first in SVG document order.
69+
const text = svg
70+
.append('g')
71+
.attr('class', 'labels')
72+
.selectAll('foreignObject')
73+
.data(graph.nodes)
74+
.enter()
75+
.append('foreignObject')
76+
.attr(
77+
'requiredFeatures',
78+
'http://www.w3.org/TR/SVG11/feature#Extensibility',
79+
)
80+
.attr('width', `${textWidth}px`)
81+
.attr('height', '2em')
82+
83+
text
84+
.append('xhtml:div')
85+
.html(d => (d.degree > 2 || d.pinned ? renderLatex(d.property.name) : ''))
86+
.style('width', '100%')
87+
.style('text-align', 'center')
88+
89+
// Draw edges connecting properties which are related by a theorem
90+
const link = svg
91+
.append('g')
92+
.attr('stroke', '#777')
93+
.attr('stroke-opacity', 0.6)
94+
.selectAll('path')
95+
.data(graph.links)
96+
.join('path')
97+
.attr('stroke-width', 2)
98+
.attr('marker-mid', 'url(#arrowhead)')
99+
100+
link.append('title').text(d => d.theorem.name)
101+
102+
// Draw nodes for each property
103+
const node = svg
104+
.append('g')
105+
.selectAll('circle')
106+
.data(graph.nodes)
107+
.enter()
108+
.append('circle')
109+
.attr('stroke', '#888')
110+
.attr('stroke-width', d => (d.pinned ? 3 : 1.5))
111+
.attr('r', d => {
112+
if (d.pinned) {
113+
return 10
114+
} else if (d.degree > 2) {
115+
return 8
116+
} else {
117+
return 5
118+
}
119+
})
120+
.attr('fill', d => (d.pinned || d.degree > 2 ? color('1') : color('2')))
121+
122+
node.append('title').text(d => d.property.name)
123+
124+
// Run the simulation and update elements
125+
simulation.on('tick', () => {
126+
link.attr('d', drawLinkWithMidpoint)
127+
128+
node.attr('cx', ({ x }) => x ?? null).attr('cy', ({ y }) => y ?? null)
129+
130+
// Position labels just under their corresponding nodes, and shifted so
131+
// that centered internal text will appear middle-anchored
132+
text
133+
.attr('x', ({ x }) => (x ? x - textWidth / 2 : null))
134+
.attr('y', ({ y }) => (y ? y + 8 : null))
135+
})
136+
137+
// Allow hover-over
138+
node.on('mouseover', function () {
139+
const datum = d3.select(this).datum() as { property: Property }
140+
if (datum.property) {
141+
$selected = datum.property
142+
}
143+
})
144+
145+
link.on('mouseover', function () {
146+
const datum = d3.select(this).datum() as Link
147+
if (datum.theorem) {
148+
$selected = datum.theorem
149+
}
150+
})
151+
152+
// Allow dragging non-pinned nodes
153+
154+
type DragEvent = d3.D3DragEvent<HTMLElement, unknown, Node>
155+
156+
node.call(
157+
d3
158+
.drag<any, Node, unknown>()
159+
.on('start', dragstarted)
160+
.on('drag', dragged)
161+
.on('end', dragended),
162+
)
163+
164+
function dragstarted({ active, subject }: DragEvent) {
165+
if (!active) {
166+
simulation.alphaTarget(0.3).restart()
167+
}
168+
169+
subject.fx = subject.pinned?.x || subject.x
170+
subject.fy = subject.pinned?.y || subject.y
171+
}
172+
173+
function dragged({ subject, x, y }: DragEvent) {
174+
subject.fx = subject.pinned?.x || x
175+
subject.fy = subject.pinned?.y || y
176+
}
177+
178+
function dragended({ active, subject }: DragEvent) {
179+
if (!active) {
180+
simulation.alphaTarget(0)
181+
}
182+
183+
subject.fx = subject.pinned?.x
184+
subject.fy = subject.pinned?.y
185+
}
186+
})
187+
</script>
188+
189+
<svg xmlns="http://www.w3.org/2000/svg" bind:this={el}>
190+
<defs>
191+
<marker
192+
id="arrowhead"
193+
markerWidth="3"
194+
markerHeight="3"
195+
refX="0"
196+
refY="1.5"
197+
orient="auto"
198+
>
199+
<polygon points="0,0 3,1.5 0,3" fill="#888" />
200+
</marker>
201+
</defs>
202+
</svg>
Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,32 @@
11
<script lang="ts">
2-
import { Link } from '../Shared'
2+
import { writable } from 'svelte/store'
3+
import { Icons, Link } from '../Shared'
34
import { Table as Theorems } from '../Theorems'
5+
import Graph from './Graph.svelte'
46
import Value from './Value.svelte'
57
import type { Property, Space, Theorem, Trait } from '@/models'
8+
import { toGraph } from './graph'
69
710
export let space: Space
11+
export let property: Property
812
export let theorems: Theorem[]
913
export let traits: [Property, Trait][]
14+
15+
const selected = writable<Property | Theorem>()
16+
17+
$: graph = toGraph(property, traits, theorems)
1018
</script>
1119

20+
<Icons.Robot />
21+
1222
Automatically deduced from the following:
23+
1324
<div class="row">
14-
<div class="col">
15-
<h5>Properties</h5>
16-
<table class="table">
25+
<div class="col-6">
26+
<Graph {graph} {selected} />
27+
</div>
28+
<div class="col-6">
29+
<table class="table table-sm">
1730
<thead>
1831
<tr>
1932
<th>Property</th>
@@ -22,7 +35,7 @@ Automatically deduced from the following:
2235
</thead>
2336
<tbody>
2437
{#each traits as [property, trait] (property.id)}
25-
<tr>
38+
<tr class:table-warning={$selected === property}>
2639
<td>
2740
<Link.Property {property} />
2841
</td>
@@ -35,9 +48,7 @@ Automatically deduced from the following:
3548
{/each}
3649
</tbody>
3750
</table>
38-
</div>
39-
<div class="col">
40-
<h5>Theorems</h5>
41-
<Theorems {theorems} />
51+
52+
<Theorems {theorems} small={true} {selected} />
4253
</div>
4354
</div>

0 commit comments

Comments
 (0)