Skip to content

Commit 55772c9

Browse files
committed
wip drawing tableaus with svg
1 parent 199216f commit 55772c9

6 files changed

Lines changed: 233 additions & 5 deletions

File tree

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
<ul>
2-
<li>Tableau / proof</li>
3-
<li>Final judgement: Entailment/Contradiction/Neutral</li>
4-
</ul>
1+
<section>
2+
<la-tableau-svg></la-tableau-svg>
3+
</section>

frontend/src/app/annotate/annotation-tableau/annotation-tableau.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { Component } from "@angular/core";
2+
import { TableauSVG } from "../tableau-svg/tableau-svg.component";
23

34
@Component({
45
selector: "la-annotation-tableau",
56
standalone: true,
6-
imports: [],
7+
imports: [TableauSVG],
78
templateUrl: "./annotation-tableau.component.html",
89
styleUrl: "./annotation-tableau.component.scss",
910
})
Lines changed: 12 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
svg {
2+
font-family: monospace;
3+
}
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
import { Component, ElementRef, Input, Output, ViewChild, EventEmitter } from "@angular/core";
2+
3+
interface Dimensions {
4+
width?: number;
5+
height?: number;
6+
}
7+
8+
@Component({
9+
selector: "[term]",
10+
standalone: true,
11+
imports: [],
12+
templateUrl: "./tableau-svg-term.svg",
13+
})
14+
export class TableauTerm {
15+
@Input()
16+
public idx?: number;
17+
18+
@Input()
19+
public label?: String;
20+
21+
@Input()
22+
public term: String = '';
23+
24+
@Input()
25+
public rule?: String;
26+
27+
@ViewChild('idxText')
28+
idxText?: ElementRef<SVGTextElement>;
29+
30+
@ViewChild('termText')
31+
termText!: ElementRef<SVGTextElement>;
32+
33+
@ViewChild('labelText')
34+
labelText?: ElementRef<SVGTextElement>;
35+
36+
@Output()
37+
public onSize = new EventEmitter<Dimensions>();
38+
39+
padding = 10;
40+
termX: number = 0;
41+
labelX: number = 0;
42+
idxW: number = 0;
43+
labelW: number = 0;
44+
totalW: number = 0;
45+
46+
ngAfterViewChecked() {
47+
this.idxW = this.idxText ? this.idxText.nativeElement.getComputedTextLength() + this.padding : 0;
48+
this.labelW = this.labelText ? this.labelText.nativeElement.getComputedTextLength() + this.padding : 0;
49+
this.termX = this.idxW;
50+
this.labelX = this.termX + this.termText.nativeElement.getComputedTextLength() + this.padding;
51+
this.totalW = this.labelW + this.idxW + this.termText.nativeElement.getComputedTextLength();
52+
53+
this.onSize.emit({width: this.totalW});
54+
}
55+
56+
}
57+
58+
@Component({
59+
selector: "[tableau-tree]",
60+
standalone: true,
61+
imports: [TableauTerm],
62+
template: `
63+
<svg:g>
64+
@for (node of tree.nodes; track tree.nodes; let i = $index) {
65+
<svg:g attr.transform="translate(0, {{ nodeY(i) }})">
66+
<svg:g term [idx]="node.idx" [term]="node.term" [label]="node.label" (onSize)="updateDimensions($event)" [rule]="node.rule"></svg:g>
67+
@if (i < tree.nodes.length - 1) {
68+
<svg:path attr.d="M 0 {{ nodeHeight(node) - 15 }} l 0 15" stroke="blue" fill="none"/>
69+
}
70+
</svg:g>
71+
}
72+
73+
@if (tree.subtrees) {
74+
@for (subtree of tree.subtrees; track tree.subtrees; let i = $index) {
75+
<svg:path attr.d="
76+
M 0 {{ totalNodeHeight() - 15 }}
77+
q 0 {{levelHeight/2}} {{subtreePosition(i)/2 }} {{levelHeight/2}}
78+
q {{subtreePosition(i)/2}} 0 {{subtreePosition(i)/2}} {{levelHeight/2}}
79+
" stroke="blue" fill="none"/>
80+
}
81+
}
82+
83+
<svg:g attr.transform="translate(0, {{ totalNodeHeight() + levelHeight }})">
84+
@for (subtree of tree.subtrees; track tree.subtrees; let i = $index) {
85+
<svg:g tableau-tree [tree]="subtree" attr.transform="translate({{subtreePosition(i)}}, 0)"
86+
(onSize)="updateSubDimensions($event)"></svg:g>
87+
}
88+
</svg:g>
89+
90+
</svg:g>
91+
`
92+
})
93+
export class TableauTree {
94+
@Input()
95+
tree: any;
96+
97+
levelHeight = 40;
98+
99+
width: number = 0;
100+
subWidth: number = 0;
101+
subHeight: number = 0;
102+
103+
@Output()
104+
public onSize = new EventEmitter<Dimensions>();
105+
106+
updateDimensions(size: Dimensions) {
107+
this.width = Math.max(this.width, size.width!);
108+
this.emitSize();
109+
}
110+
111+
updateSubDimensions(size: Dimensions) {
112+
this.subWidth = Math.max(this.subWidth, size.width!);
113+
this.subHeight = Math.max(this.subHeight, size.height!);
114+
this.emitSize();
115+
}
116+
117+
emitSize() {
118+
this.onSize.emit({
119+
width: Math.max(this.subWidth * (this.tree.subtrees?.length ?? 0), this.width),
120+
height: this.subHeight + 70 * this.tree.nodes.length,
121+
});
122+
}
123+
124+
subtreePosition(idx: number) {
125+
let widthWithPadding = 1.15 * this.subWidth;
126+
return widthWithPadding * idx - (widthWithPadding / 2) * (this.tree.subtrees.length - 1);
127+
}
128+
129+
nodeHeight(node: any) {
130+
return node.rule ? 60 : 40;
131+
}
132+
133+
totalNodeHeight() {
134+
return this.tree.nodes.map(this.nodeHeight).reduce((sum: number, h: number) => sum + h, 0);
135+
}
136+
137+
nodeY(idx: number) {
138+
return this.tree.nodes.slice(0, idx).map(this.nodeHeight).reduce((sum: number, h: number) => sum + h, 0);
139+
}
140+
}
141+
142+
@Component({
143+
selector: "la-tableau-svg",
144+
standalone: true,
145+
imports: [TableauTree],
146+
templateUrl: "./tableau-svg.component.svg",
147+
styleUrl: "./tableau-svg.component.scss",
148+
})
149+
export class TableauSVG {
150+
151+
treeDimensions: Dimensions = {width: 0, height: 0};
152+
153+
onTreeSize(size: Dimensions) {
154+
this.treeDimensions = size;
155+
}
156+
157+
tree: any = {
158+
nodes: [
159+
{idx: 1, term: 'every@man@(be@work)'},
160+
{idx: 2, term: 'every@(who@(be@work)@person)@(λ1,a@(expensive@car)@(λ2,have@2@1))'},
161+
{idx: 3, term: 'every@man@(λ3,a@car@(λ2,own@2@3))'},
162+
{idx: 4, term: 'be@work', label: 'c1', rule: 'up_mon_fun_some[1,3]'},
163+
{idx: 5, term: '(λ3,a@car@(λ2,own@2@3))', label: 'c1', rule: 'up_mon_fun_some[1,3]'},
164+
{idx: 6, term: 'man', label: 'c1', rule: 'up_mon_fun_some[1,3]'},
165+
{idx: 7, term: 'a@car@(λ2,own@2@c1)', rule: 'pull_arg[5]'},
166+
{idx: 8, term: 'work', label: 'c1', rule: 'aux_verb[4]'},
167+
{idx: 9, term: 'be@work', label: 'c1', rule: 'tr_every_c[1,6], [c1]'},
168+
{idx: 8, term: 'work', label: 'c1', rule: 'aux_verb[9]'},
169+
],
170+
subtrees: [
171+
{
172+
nodes: [
173+
{idx: 10, term: 'who@(be@work)@person', label: 'c1', rule: 'tr_every[2],[c1]'},
174+
],
175+
subtrees: [
176+
{
177+
nodes: [
178+
{idx: 13, term: 'be@work', label: 'c1', rule: 'fl_conj_who[10]'},
179+
{idx: 16, term: 'work', label: 'c1', rule: 'aux_verb[13]'},
180+
{term: 'X', rule: 'cl_subcat[16,8]'}
181+
],
182+
},
183+
{
184+
nodes: [
185+
{idx: 14, term: 'person', label: 'c1', rule: 'fl_conj_who[10]'},
186+
{term: 'X', rule: 'cl_subsumption[14,6]'}
187+
]
188+
}
189+
]
190+
},
191+
{
192+
nodes: [
193+
{idx: 11, term: 'who@(be@work)@person', label: 'c1', rule: 'tr_every[2],[c1]'},
194+
{idx: 12, term: '(λ1,a@(expensive@car)@(λ2,have@2@1))', label: 'c1', rule: 'tr_every[2],[c1]'},
195+
{idx: 15, term: 'a@(expensive@car)@(λ2,have@2@c1)', label: 'c1', rule: 'pull_arg[12]'},
196+
{idx: 17, term: 'be@work', label: 'c1', rule: 'tr_conj_who[11]'},
197+
{idx: 18, term: 'person', label: 'c1', rule: 'tr_conj_who[11]' },
198+
{idx: 8, term: 'work', label: 'c1', rule: 'aux_verb[17]'},
199+
{idx: 19, term: 'a@(expensive@car)', label: '(λ2,have@2@c1)', rule: 'same_args[15,7]'},
200+
{idx: 20, term: 'a@car', label: '(λ2,have@2@c1)', rule: 'same_args[15,7]'},
201+
{idx: 21, term: 'expensive@car', label: 'c2', rule: 'up_mon_fun[19,20]'},
202+
{idx: 22, term: 'car', label: 'c2', rule: 'up_mon_fun[19,20]'},
203+
{idx: 23, term: 'car', label: 'c2', rule:'int_mod_tr[21]'},
204+
{idx: 24, term: 'expensive', label: 'c2', rule:'int_mod_tr[21]'},
205+
{term: 'X', rule: 'cl_subsmption[23,22]'}
206+
]
207+
}
208+
]
209+
}
210+
}

0 commit comments

Comments
 (0)