Skip to content

Commit f1a14bb

Browse files
committed
Merge branch 'master' of github.com:jonorthwash/ud-annotatrix
2 parents 605e99d + 9bf2808 commit f1a14bb

File tree

130 files changed

+24239
-1700
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

130 files changed

+24239
-1700
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# UD Annotatrix
22

3+
UD Annotatrix: a simple, sleek, fast editor for Universal Dependencies treebanks.
4+
5+
![UD Annotatrix Screenshot](https://github.com/jonorthwash/ud-annotatrix/raw/master/screenshot.png)
6+
7+
## About
38
UD Annotatrix is a client-side, browser-only, language-independent tool for editing dependency trees according to the guidelines established by the [Universal Dependencies](https://universaldependencies.org) project. UD-Annotatrix supports uploading corpora to the browser to add, remove, and edit dependencies in a wide variety of formats (managed with the [notatrix](https://github.com/keggsmurph21/notatrix) tool), including:
49
- [CoNLL-U](http://universaldependencies.org/format.html)
510
- [VISL CG3](http://beta.visl.sdu.dk/cg3/single/#streamformats)
@@ -9,7 +14,7 @@ UD Annotatrix is a client-side, browser-only, language-independent tool for edit
914

1015
![UD Annotatrix Screenshot](https://github.com/jonorthwash/ud-annotatrix/raw/master/screenshot.png)
1116

12-
Note: [brat](http://brat.nlplab.org) is a similar corpus annotation tool, but we're aiming for a simpler, cleaner, faster interface optimized for Universal Dependencies with an optional server-side component.
17+
Note: [brat](http://brat.nlplab.org) and [CoNLLUEditor](https://github.com/Orange-OpenSource/conllueditor) are similar corpus annotation tools, but UD Annotatrix aims to be a simple(r), clean(er), fast(er) interface optimized for Universal Dependencies with an optional server-side component.
1318

1419
##### Contents
1520
[Features](#features)

client/README.md

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,16 +67,22 @@
6767
string/object. If it can't detect one, it returns null. If it detects one
6868
or more, it follows a simple resolution algorithm to pick one.</p>
6969
</dd>
70+
<dt><a href="#traverseTree">traverseTree(token, heights)</a></dt>
71+
<dd><p>BFS to set the heights of the tree, starting from a root token</p>
72+
</dd>
73+
<dt><a href="#drawMouse">drawMouse(mouse)</a></dt>
74+
<dd><p>Draw mouse on the svg.</p>
75+
</dd>
76+
<dt><a href="#drawNodes">drawNodes(_g, el, heights, spacing)</a> ⇒</dt>
77+
<dd><p>Draws the nodes on the svg.</p>
78+
</dd>
7079
<dt><a href="#bind">bind(eles)</a></dt>
7180
<dd><p>Bind the elements to the internal reference.</p>
7281
</dd>
7382
<dt><a href="#run">run()</a></dt>
7483
<dd><p>Main function that runs all of the
7584
subfunctions needed to generate the graph.</p>
7685
</dd>
77-
<dt><a href="#drawNodes">drawNodes()</a></dt>
78-
<dd><p>Draws the nodes on the svg.</p>
79-
</dd>
8086
<dt><a href="#drawDeprels">drawDeprels()</a></dt>
8187
<dd><p>Draws deprels.</p>
8288
</dd>
@@ -1166,6 +1172,44 @@ Helper function for Corpus. Attempts to detect the format of a given serial
11661172
| --- | --- |
11671173
| serial | <code>String</code> \| <code>Object</code> |
11681174

1175+
<a name="traverseTree"></a>
1176+
1177+
## traverseTree(token, heights)
1178+
BFS to set the heights of the tree, starting from a root token
1179+
1180+
**Kind**: global function
1181+
1182+
| Param | Type | Description |
1183+
| --- | --- | --- |
1184+
| token | <code>Integer</code> | current token |
1185+
| heights | <code>Array</code> | holds the heights of the tokens |
1186+
1187+
<a name="drawMouse"></a>
1188+
1189+
## drawMouse(mouse)
1190+
Draw mouse on the svg.
1191+
1192+
**Kind**: global function
1193+
1194+
| Param | Type |
1195+
| --- | --- |
1196+
| mouse | <code>MouseObject</code> |
1197+
1198+
<a name="drawNodes"></a>
1199+
1200+
## drawNodes(_g, el, heights, spacing) ⇒
1201+
Draws the nodes on the svg.
1202+
1203+
**Kind**: global function
1204+
**Returns**: token number of root
1205+
1206+
| Param | Type | Description |
1207+
| --- | --- | --- |
1208+
| _g | <code>g</code> | <g> element to draw on |
1209+
| el | <code>Array</code> | array of nodes |
1210+
| heights | <code>Array</code> | heights of each node |
1211+
| spacing | <code>Integer</code> | how far nodes are apart |
1212+
11691213
<a name="bind"></a>
11701214

11711215
## bind(eles)
@@ -1183,12 +1227,6 @@ Bind the elements to the internal reference.
11831227
Main function that runs all of the
11841228
subfunctions needed to generate the graph.
11851229

1186-
**Kind**: global function
1187-
<a name="drawNodes"></a>
1188-
1189-
## drawNodes()
1190-
Draws the nodes on the svg.
1191-
11921230
**Kind**: global function
11931231
<a name="drawDeprels"></a>
11941232

client/graph/index.js

Lines changed: 76 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const config = require("./config");
66
const nx = require("notatrix");
77
const utils = require("../utils");
88
const v = require("./visualiser.js");
9+
const tree = require("./tree.js");
910

1011
/**
1112
* Abstraction over the graph editor. Handles interaction between the graph
@@ -21,7 +22,13 @@ class Graph {
2122
this.app = app;
2223
this.config = config;
2324

24-
this.v = v;
25+
if (this.app.corpus.is_vertical) {
26+
this.grapher = tree;
27+
}
28+
else {
29+
this.grapher = v;
30+
}
31+
2532

2633
// keep track for our progress bar
2734
this.progress = {
@@ -63,6 +70,17 @@ class Graph {
6370
// Basically so empty nodes are easier to deal with
6471
this.presentationId = {};
6572

73+
// We want to block the tree view if there is no root
74+
// or if there exists a cycle.
75+
this.treeBlocked = false;
76+
77+
// Holds all connections between nodes, making it easier
78+
// to traverse the tree.
79+
this.connections = {};
80+
81+
// Total number of forms (not counting supertokens)
82+
this.numTokens = 0;
83+
6684
// load configuration prefs
6785
this.load();
6886
}
@@ -77,7 +95,11 @@ class Graph {
7795
* @return {Array} [Object]
7896
*/
7997
get eles() {
98+
// reset variables
8099
this.presentationId = {};
100+
this.connections = {};
101+
this.numTokens = 0;
102+
81103
// helper function to get subscripted index numbers for superTokens
82104
function toSubscript(str) {
83105
const subscripts = {
@@ -112,6 +134,9 @@ class Graph {
112134
this.progress.done = 0;
113135
this.progress.total = 0;
114136

137+
// reset tree blocked
138+
this.treeBlocked = false;
139+
115140
// cache these
116141
const sent = this.app.corpus.current, format = this.app.corpus.format;
117142

@@ -124,6 +149,8 @@ class Graph {
124149
// Counts just supertokens
125150
let mwTokenNum = 0;
126151

152+
let rootFound = false;
153+
127154
// walk over all the tokens
128155
sent.index().iterate(token => {
129156
// don't draw other analyses
@@ -136,6 +163,10 @@ class Graph {
136163
let pos = format === "CG3" ? token.xpostag || token.upostag : token.upostag || token.xpostag;
137164
let isRoot = sent.root.dependents.has(token);
138165

166+
if(isRoot) {
167+
rootFound = true;
168+
}
169+
139170
// after iteration, this will just be the max
140171
this.clumps = clump;
141172

@@ -169,7 +200,7 @@ class Graph {
169200

170201
this.tokens[tokenNum] = token;
171202

172-
this.presentationId[id] = tokenNum;
203+
this.presentationId[id] = tokenNum;
173204

174205
eles.push(
175206
{ // "form" node, including pos data
@@ -193,16 +224,22 @@ class Graph {
193224
},
194225
);
195226
tokenNum++;
227+
this.numTokens++;
196228
}
197229

198230
});
199231

232+
if(!rootFound) {
233+
this.treeBlocked = true;
234+
}
235+
200236
sent.index().iterate(token => {
201237
// iterate over the token's heads to get edges
202238
token.mapHeads((head, i) => {
203239

204-
// if not enhanced, only draw the first dependency
205-
if (i && !sent.options.enhanced)
240+
// if not enhanced or is_vertical
241+
// only draw the first dependency
242+
if (i && (!sent.options.enhanced || this.app.corpus.is_vertical))
206243
return;
207244

208245
this.progress.total += 1;
@@ -227,7 +264,18 @@ class Graph {
227264

228265
const presentId = this.presentationId[id];
229266
const presentHeadId = this.presentationId[headId];
230-
267+
let depClasses = utils.validate.depEdgeClasses(sent, token, head);
268+
if (!(presentHeadId in this.connections)) {
269+
this.connections[presentHeadId] = [];
270+
}
271+
this.connections[presentHeadId].push(presentId);
272+
273+
if(depClasses.includes("cycle")) {
274+
this.treeBlocked = true;
275+
}
276+
if(String(id).includes('.') || String(headId).includes('.')) {
277+
depClasses += " dotted";
278+
}
231279
eles.push({
232280
id: `dep_${presentId}_${presentHeadId}`,
233281
name: `dependency`,
@@ -242,7 +290,7 @@ class Graph {
242290
targetToken: token,
243291
label: label,
244292
enhanced: i ? true: false,
245-
classes: utils.validate.depEdgeClasses(sent, token, head),
293+
classes: depClasses,
246294
});
247295
});
248296
});
@@ -258,10 +306,23 @@ class Graph {
258306
* @return {Graph} (chaining)
259307
*/
260308
draw() {
261-
// cache a ref
309+
if (this.app.corpus.is_vertical) {
310+
this.grapher = tree;
311+
}
312+
else {
313+
this.grapher = v;
314+
}
262315

263-
this.v.bind(this);
264-
this.v.run();
316+
if (!this.app.corpus.is_vertical || !this.treeBlocked) {
317+
this.grapher.bind(this);
318+
this.grapher.run();
319+
}
320+
else {
321+
this.grapher.displayError();
322+
console.log("Graph contains a cycle or needs a root.");
323+
$("#vertical").click();
324+
}
325+
265326

266327
// add the mice and locks from `collab`
267328
this.drawMice();
@@ -718,7 +779,7 @@ class Graph {
718779

719780
let id = ele.attr("id");
720781
let sourceNum = parseInt(id.split("_")[2]);
721-
let targetNum = parseInt(id.split("_")[1]);
782+
let targetNum = parseInt(id.split("_")[1]);
722783
let src = this.tokens[sourceNum];
723784
let tar = this.tokens[targetNum];
724785
tar.modifyHead(src, deprel);
@@ -751,7 +812,7 @@ class Graph {
751812
try {
752813
let id = ele.attr("id");
753814
let sourceNum = parseInt(id.split("_")[2]);
754-
let targetNum = parseInt(id.split("_")[1]);
815+
let targetNum = parseInt(id.split("_")[1]);
755816
let src = this.tokens[sourceNum];
756817
let tar = this.tokens[targetNum];
757818
tar.removeHead(src);
@@ -1106,6 +1167,9 @@ class Graph {
11061167
target.addClass("input");
11071168
let textElement = $('#text-' + target.attr('id'));
11081169
let textLabel = textElement.text().replace(/[]/, '');
1170+
if(textElement.is("textPath")) {
1171+
textElement = $('#textContainer-' + target.attr('id'));
1172+
}
11091173
let textBCR;
11101174
if(target.attr('id').includes('dep')) {
11111175
textBCR = textElement[0].getBoundingClientRect();
@@ -1163,7 +1227,7 @@ class Graph {
11631227
*/
11641228
drawMice() {
11651229
this.app.collab.getMouseNodes().forEach(mouse => {
1166-
this.v.drawMouse(mouse);
1230+
this.grapher.drawMouse(mouse);
11671231
});
11681232
}
11691233

0 commit comments

Comments
 (0)