Skip to content

Commit c6b3841

Browse files
committed
wip
1 parent 22211ae commit c6b3841

10 files changed

Lines changed: 817 additions & 86 deletions

File tree

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: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
<script lang="ts">
2+
import { onMount } from 'svelte'
3+
import * as d3 from 'd3'
4+
import type { Property, Trait, Theorem } from '@/models'
5+
import { formula } from '@pi-base/core'
6+
import * as katex from 'katex'
7+
import type { Writable } from 'svelte/store'
8+
9+
export let selected: Writable<Property | Theorem>
10+
export let property: Property
11+
export let traits: [Property, Trait][]
12+
export let theorems: Theorem[]
13+
14+
function renderLatex(s: string) {
15+
return s.replaceAll(/\$[^$]+\$/g, p => {
16+
return (katex as any).default.renderToString(p.replaceAll('$', ''))
17+
})
18+
}
19+
20+
type DragEvent = d3.D3DragEvent<HTMLElement, unknown, Node>
21+
22+
type Node = d3.SimulationNodeDatum & {
23+
property: Property
24+
trait?: Trait
25+
degree: number
26+
pinned?: { x?: number; y?: number }
27+
}
28+
29+
type Link = d3.SimulationLinkDatum<Node> & { theorem: Theorem }
30+
31+
let nodes: Node[]
32+
let links: Link[]
33+
34+
let el: SVGSVGElement
35+
36+
const width = 600
37+
const height = 600
38+
const bound = 200
39+
const textWidth = 500
40+
41+
$: {
42+
// Build graph
43+
// - one node per property
44+
// - theorems correspond to edges linking "when" and "then" ()
45+
const inodes: Record<string, Node> = {}
46+
links = []
47+
48+
for (const theorem of theorems) {
49+
for (const a of formula.properties(theorem.when)) {
50+
inodes[a.id] ||= { property: a, degree: 0 }
51+
inodes[a.id].degree++
52+
53+
for (const c of formula.properties(theorem.then)) {
54+
inodes[c.id] ||= { property: c, degree: 0 }
55+
inodes[c.id].degree++
56+
57+
links.push({
58+
source: a.id.toString(),
59+
target: c.id.toString(),
60+
theorem,
61+
})
62+
}
63+
}
64+
}
65+
66+
for (const [property, trait] of traits) {
67+
inodes[property.id] ||= { property, degree: 0 }
68+
inodes[property.id].trait = trait
69+
inodes[property.id].fx = -1 * bound
70+
inodes[property.id].pinned = { x: -1 * bound }
71+
}
72+
73+
inodes[property.id].fx = bound
74+
inodes[property.id].fy = 1
75+
inodes[property.id].pinned = { x: bound, y: 1 }
76+
77+
nodes = Object.values(inodes)
78+
}
79+
80+
onMount(() => {
81+
const color = d3.scaleOrdinal(d3.schemeTableau10)
82+
83+
const simulation = d3
84+
.forceSimulation(nodes)
85+
.force(
86+
'link',
87+
d3.forceLink<Node, Link>(links).id(d => d.property.id.toString()),
88+
)
89+
.force(
90+
'charge',
91+
d3.forceManyBody<Node>().strength(d => -100 * (d.pinned ? 10 : 1)),
92+
)
93+
.force(
94+
'x',
95+
d3.forceX().strength(d => {
96+
const x = d.x || 0
97+
return Math.min((x / bound) * (x / bound), 1)
98+
}),
99+
)
100+
101+
const svg = d3
102+
.select(el)
103+
.attr('width', width)
104+
.attr('height', height)
105+
.attr('viewBox', [-width / 2, -height / 2, width, height])
106+
.attr('style', 'max-width: 100%; width: 100%; height: 100%;')
107+
108+
const text = svg
109+
.append('g')
110+
.attr('class', 'labels')
111+
.selectAll('foreignObject')
112+
.data(nodes)
113+
.enter()
114+
.append('foreignObject')
115+
.attr(
116+
'requiredFeatures',
117+
'http://www.w3.org/TR/SVG11/feature#Extensibility',
118+
)
119+
.attr('width', `${textWidth}px`)
120+
.attr('height', '2em')
121+
122+
const link = svg
123+
.append('g')
124+
.attr('stroke', '#777')
125+
.attr('stroke-opacity', 0.6)
126+
.selectAll('path')
127+
.data(links)
128+
.join('path')
129+
.attr('stroke-width', 2)
130+
.attr('marker-mid', 'url(#arrowhead)')
131+
.on('mouseover', function () {
132+
const datum = d3.select(this).datum() as { theorem: Theorem }
133+
if (datum.theorem) {
134+
$selected = datum.theorem
135+
}
136+
})
137+
138+
text
139+
.append('xhtml:div')
140+
.html(d => (d.degree > 2 || d.pinned ? renderLatex(d.property.name) : ''))
141+
.style('width', '100%')
142+
.style('text-align', 'center')
143+
144+
const node = svg
145+
.append('g')
146+
.selectAll('circle')
147+
.data(nodes)
148+
.enter()
149+
.append('circle')
150+
.attr('stroke', '#888')
151+
.attr('stroke-width', d => (d.pinned ? 3 : 1.5))
152+
.attr('r', d => {
153+
if (d.pinned) {
154+
return 10
155+
} else if (d.degree > 2) {
156+
return 8
157+
} else {
158+
return 5
159+
}
160+
})
161+
.attr('fill', d => (d.pinned || d.degree > 2 ? color('1') : color('2')))
162+
.on('mouseover', function () {
163+
const datum = d3.select(this).datum() as { property: Property }
164+
if (datum.property) {
165+
$selected = datum.property
166+
}
167+
})
168+
169+
node.append('title').text(d => d.property.name)
170+
171+
node.call(
172+
d3
173+
.drag<any, Node, unknown>()
174+
.on('start', dragstarted)
175+
.on('drag', dragged)
176+
.on('end', dragended),
177+
)
178+
179+
simulation.on('tick', () => {
180+
link.attr('d', ({ source, target }: Link) => {
181+
const { x: x1 = 0, y: y1 = 0 } = source as Node
182+
const { x: x2 = 0, y: y2 = 0 } = target as Node
183+
const mx = (x2 - x1) / 2 + x1
184+
const my = (y2 - y1) / 2 + y1
185+
return `M${x1},${y1}L${mx},${my}L${x2},${y2}`
186+
})
187+
188+
node.attr('cx', (d: Node) => d.x!).attr('cy', (d: Node) => d.y!)
189+
190+
text
191+
.attr('x', (d: Node) => d.x! - textWidth / 2)
192+
.attr('y', (d: Node) => d.y! + 8)
193+
})
194+
195+
function dragstarted({ active, subject }: DragEvent) {
196+
if (!active) simulation.alphaTarget(0.3).restart()
197+
198+
subject.fx = subject.pinned?.x || subject.x
199+
subject.fy = subject.pinned?.y || subject.y
200+
}
201+
202+
function dragged({ subject, x, y }: DragEvent) {
203+
subject.fx = subject.pinned?.x || x
204+
subject.fy = subject.pinned?.y || y
205+
}
206+
207+
function dragended({ active, subject }: DragEvent) {
208+
if (!active) simulation.alphaTarget(0)
209+
210+
subject.fx = subject.pinned?.x
211+
subject.fy = subject.pinned?.y
212+
}
213+
})
214+
</script>
215+
216+
<svg xmlns="http://www.w3.org/2000/svg" bind:this={el}>
217+
<defs>
218+
<marker
219+
id="arrowhead"
220+
markerWidth="3"
221+
markerHeight="3"
222+
refX="0"
223+
refY="1.5"
224+
orient="auto"
225+
>
226+
<polygon points="0,0 3,1.5 0,3" fill="#888" />
227+
</marker>
228+
</defs>
229+
</svg>
Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,43 @@
11
<script lang="ts">
2+
import { writable } from 'svelte/store'
23
import { 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'
68
79
export let space: Space
10+
export let property: Property
811
export let theorems: Theorem[]
912
export let traits: [Property, Trait][]
13+
14+
const selected = writable<Property | Theorem>()
1015
</script>
1116

1217
Automatically deduced from the following:
18+
1319
<div class="row">
14-
<div class="col">
15-
<h5>Properties</h5>
16-
<table class="table">
20+
<div class="col-7">
21+
<Graph {property} {traits} {theorems} {selected} />
22+
</div>
23+
<div class="col-5">
24+
<h5>Assumptions</h5>
25+
<table class="table table-sm">
1726
<thead>
1827
<tr>
28+
<th>Id</th>
1929
<th>Property</th>
2030
<th>Value</th>
2131
</tr>
2232
</thead>
2333
<tbody>
2434
{#each traits as [property, trait] (property.id)}
25-
<tr>
35+
<tr class:table-warning={$selected === property}>
36+
<td>
37+
<Link.Property {property}>
38+
{property.id}
39+
</Link.Property>
40+
</td>
2641
<td>
2742
<Link.Property {property} />
2843
</td>
@@ -35,9 +50,8 @@ Automatically deduced from the following:
3550
{/each}
3651
</tbody>
3752
</table>
38-
</div>
39-
<div class="col">
53+
4054
<h5>Theorems</h5>
41-
<Theorems {theorems} />
55+
<Theorems {theorems} small={true} {selected} />
4256
</div>
4357
</div>

0 commit comments

Comments
 (0)