Skip to content

Commit 4b78561

Browse files
committed
move most global constants to config.js
1 parent e235d54 commit 4b78561

File tree

8 files changed

+209
-774
lines changed

8 files changed

+209
-774
lines changed

index.html

Lines changed: 13 additions & 422 deletions
Large diffs are not rendered by default.

js/GraphLayout.js

Lines changed: 26 additions & 222 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class GraphLayout {
2525

2626
// selected words to generate graph around
2727
this.words = [];
28-
this.distanceFromRoot = 30; // default value for max dist from root
28+
this.maxDepth = 30; // default value for max dist from root
2929
this.data = {
3030
flat: {},
3131
anchors: [],
@@ -34,14 +34,6 @@ class GraphLayout {
3434

3535
// force simulation
3636
this.simulation = d3.forceSimulation()
37-
.force('link', d3.forceLink().id(d => d.id))
38-
.force('collision', d3.forceCollide(d => {
39-
return d.role === "link-anchor" ? 0 : 20;
40-
}))
41-
.force('charge', d3.forceManyBody()
42-
.strength(d => d.role === "link-anchor" ? 0 : -30)
43-
.distanceMax(100)
44-
)
4537
.force('center', d3.forceCenter( 0,0 ));
4638
}
4739

@@ -75,10 +67,11 @@ class GraphLayout {
7567
if (this.words.length === words.length &&
7668
this.words.every((w,i) => words[i] === w)) { return; }
7769
else { this.words = words; }
78-
7970
this.generateData();
8071
console.log('data', this.data);
8172

73+
return;
74+
8275
// draw nodes
8376
this.drawNodes();
8477

@@ -90,102 +83,57 @@ class GraphLayout {
9083
}
9184

9285
generateData() {
93-
// flatten nodes/links within a given distance of selected words
94-
var d = this.data.flat = {};
86+
console.log(this.words);
87+
const d = this.data.flat = {};
88+
const maxDepth = this.maxDepth;
9589
this.words.forEach(root => {
96-
var maxDepth = this.distanceFromRoot;
9790
function addToDataset(node,depth) {
9891
if (depth > maxDepth) { return; } // done
9992
if (d[node.id] && d[node.id].depth <= depth) { // skip
10093
return;
10194
}
10295

103-
if (node.type === "WORD") {
96+
if (node instanceof Word) {
10497
d[node.id] = {
10598
id: node.id,
10699
depth: depth,
107100
data: node
108101
}
109102
}
110-
else if (node.type === "LINK") {
103+
else if (node instanceof Link) {
111104
d[node.id] = {
112105
id: node.id,
113106
depth: depth,
114107
data: node
115108
}
116109
// recurse to start/endpoint
117-
if (node.s) { addToDataset(node.s, depth + 1); }
118-
if (node.e) { addToDataset(node.e, depth + 1); }
110+
if (node.words) {
111+
node.words.forEach(anchor => addToDataset(anchor, depth + 1));
112+
}
119113
}
120114
// recurse to adjacent links
121-
var links = [].concat( node.parentsL, node.parentsR );
115+
var links = [].concat( node.parentsL, node.parentsC, node.parentsR );
122116
links.forEach(l => addToDataset(l, depth + 1));
123117
}
124118
addToDataset(root, 0);
125119
});
126120

127121
// sort flat data into nodes and links
128-
var a = this.data.anchors = [];
129-
var l = this.data.links = [];
122+
const a = this.data.anchors = [];
123+
const l = this.data.links = [];
130124

131-
for (var i in d) {
132-
if (d[i].data.type === "WORD") {
133-
d[i].role = "word";
134-
a.push(d[i]);
125+
for (var datum in d) {
126+
if (d[datum].data instanceof Word) {
127+
a.push(d[datum]);
135128
}
136129
else {
137-
d[i].stops = [];
138-
d[i].role = "link";
139-
l.push(d[i]);
130+
l.push(d[datum]);
140131
}
141132
}
142133

143-
// identify anchors (endpoints of links): can be words or other links
144-
function getAnchorPoint(node) {
145-
if (d[node.id]) {
146-
if (d[node.id].role === "word") {
147-
return d[node.id];
148-
}
149-
else {
150-
// create anchor point along link
151-
var linkAnchor = {
152-
id: node.id,
153-
data: node,
154-
role: "link-anchor",
155-
link: d[node.id]
156-
};
157-
linkAnchor.link.stops.push(linkAnchor); // circular ref
158-
a.push(linkAnchor);
159-
return linkAnchor;
160-
}
161-
}
162-
else {
163-
// endpoint not in range of data
164-
var emptyNode = {
165-
id: node.id,
166-
data:node,
167-
role: "nil"
168-
};
169-
a.push(emptyNode);
170-
return emptyNode;
171-
}
172-
}
134+
console.log(a, l);
173135

174-
l.forEach(link => {
175-
var s = link.data.s,
176-
e = link.data.e;
177-
178-
link.source = getAnchorPoint(s);
179-
link.target = getAnchorPoint(e);
180-
});
181136

182-
// evenly space stops on initialization
183-
l.forEach(link => {
184-
var tmax = link.stops.length + 1;
185-
link.stops.forEach((stop,i) => {
186-
stop.t = (i + 1)/tmax;
187-
})
188-
})
189137
}//end generateData()
190138

191139
drawNodes() {
@@ -202,126 +150,18 @@ class GraphLayout {
202150
d.fy = d.y;
203151
})
204152
.on('drag', function(d) {
205-
if (d.role !== "link-anchor") {
206-
d.fx += d3.event.dx,
207-
d.fy += d3.event.dy;
208-
}
209-
else {
210-
// get distance to source/target
211-
var nsDx = d.link.source.x - d.x,
212-
nsDy = d.link.source.y - d.y,
213-
ntDx = d.link.target.x - d.x,
214-
ntDy = d.link.target.y - d.y,
215-
216-
esDx = d.link.source.x - d3.event.x,
217-
esDy = d.link.source.y - d3.event.y,
218-
etDx = d.link.target.x - d3.event.x,
219-
etDy = d.link.target.y - d3.event.y;
220-
221-
var nodeDistanceToSource = nsDx*nsDx + nsDy*nsDy,
222-
nodeDistanceToTarget = ntDx*ntDx + ntDy*ntDy,
223-
dragDistanceToSource = esDx*esDx + esDy*esDy,
224-
dragDistanceToTarget = etDx*etDx + etDy*etDy;
225-
226-
var direction = 0;
227-
if (dragDistanceToSource < nodeDistanceToSource) {
228-
direction = -0.01;
229-
}
230-
else if (dragDistanceToTarget < nodeDistanceToTarget) {
231-
direction = 0.01;
232-
}
233-
else {
234-
direction = dragDistanceToSource<dragDistanceToTarget ?
235-
-0.01 : 0.01;
236-
}
237-
d.t = Math.max(Math.min(d.t + direction, 0.9), 0.1);
238-
}
153+
d.fx += d3.event.dx,
154+
d.fy += d3.event.dy;
239155
})
240156
.on('end', (d) => {
241157
if (!d3.event.active) {
242158
sim.alphaTarget(0);
243159
}
244-
if (d.role !== "link-anchor") {
245-
d.fx = d.fy = null;
246-
}
247160
});
248161

249162
// data entry/merge
250-
var nodeGroup = this.nodes.selectAll('.node-group')
251-
.data(this.data.anchors);
252-
253-
nodeGroup.exit().remove();
254-
var nodeEnter = nodeGroup.enter().append('g')
255-
.attr('class','node-group')
256-
.attr("transform", () => {
257-
return 'translate(' + this.bounds.width/2 + ',' + this.bounds.height/2 + ')';
258-
});
259-
260-
nodeEnter.append('circle')
261-
.attr('class','node');
262-
var label = nodeEnter.append('g')
263-
.attr('class','node-label')
264-
.attr('pointer-events','none')
265-
.attr('transform','translate(10,-5)');
266-
label.append('text')
267-
.style('font-size','0.8em')
268-
.attr('text-anchor','start');
269-
label.append('rect')
270-
.attr('rx',1)
271-
.attr('ry',1)
272-
.attr('fill', '#fafaea')
273-
.attr('stroke','#cacabc');
274-
275-
nodeGroup = nodeGroup.merge(nodeEnter);
276-
nodeGroup
277-
.classed('node-word', d => d.role === 'word')
278-
.on('mouseover', (d) => {
279-
function mouseoverWord(word) {
280-
// TODO: link back to word in "drawing" svg
281-
282-
}
283-
if (d.role === "word") { mouseoverWord( d.data ); }
284-
console.log('moused over',d)
285-
})
286-
.on('mouseout', (d) => {
287-
function mouseoutWord(word) {
288-
289-
}
290-
})
291-
.call(drag);
292-
293-
// draw circle
294-
var node = nodeGroup.selectAll('.node')
295-
.data(d => [d])
296-
.attr('r',(d) => d.role === 'word' ? 7 : 4)
297-
.attr('stroke', 'rgba(0,0,0,0.2)')
298-
.attr('fill',(d) => {
299-
if (d.role !== 'word') {
300-
return 'transparent';
301-
}
302-
else {
303-
return colors((d.depth+2)/10);
304-
}
305-
});
306-
307-
// draw text label
308-
label = nodeGroup.selectAll('.node-label')
309-
.raise()
310-
.data(d => [d]);
311-
312-
label.select('text')
313-
.text(d => d.role === "word" ? d.data.val : '')
314-
.attr('x',5);
315-
label.select('rect')
316-
.style('display', d => d.role === "word" ? "block" : "none")
317-
.attr('width', function() {
318-
return this.parentNode.getElementsByTagName('text')[0].getBBox().width + 10;
319-
})
320-
.attr('height','1.5em')
321-
.attr('y','-1em')
322-
.lower();
323-
324-
this.nodes.selectAll('.node-word').raise();
163+
// var nodeGroup = this.nodes.selectAll('.node-group')
164+
// .data(this.data.anchors);
325165
}
326166

327167
drawLinks() {
@@ -354,38 +194,9 @@ class GraphLayout {
354194

355195
function tick() {
356196
node
357-
.datum(d => {
358-
if (d.role === "link-anchor") {
359-
// get position of link-anchor on line
360-
var target = d.link.target,
361-
source = d.link.source;
362-
363-
var dx = target.x - source.x,
364-
dy = target.y - source.y,
365-
dr = Math.sqrt( dx * dx + dy * dy);
366-
367-
if (dr === 0) {
368-
d.fx = target.x,
369-
d.fy = target.y;
370-
return d;
371-
}
372-
373-
var sin60 = Math.sqrt(3)/2;
374-
var cx = source.x + dx * 0.5 - dy * sin60,
375-
cy = source.y + dy * 0.5 + dx * sin60;
376-
377-
var acos = Math.acos( (source.x - cx)/dr ),
378-
asin = Math.asin( (source.y - cy)/dr );
379-
380-
var theta = (asin < 0) ? -acos : acos;
381-
382-
d.fx = cx + dr*Math.cos(theta + Math.PI/3 * d.t),
383-
d.fy = cy + dr*Math.sin(theta + Math.PI/3 * d.t);
384-
}
385-
else {
386-
d.x = clampX(d.x);
387-
d.y = clampY(d.y);
388-
}
197+
.datum(d => {
198+
d.x = clampX(d.x);
199+
d.y = clampY(d.y);
389200
return d;
390201
})
391202
.attr("transform", (d) => {
@@ -395,7 +206,6 @@ class GraphLayout {
395206
link
396207
.attr('d', arrowPath);
397208

398-
399209
function arrowPath(d,i) {
400210
var target = d.target,
401211
source = d.source;
@@ -404,10 +214,6 @@ class GraphLayout {
404214
dy = target.y - source.y,
405215
dr = Math.sqrt( dx * dx + dy * dy);
406216

407-
// if (target.role === "link-anchor" || source.role === "link-anchor") {
408-
// dr /= 2;
409-
// }
410-
411217
return "M" + source.x + "," + source.y +
412218
"A" + dr + "," + dr + " 0 0,1 " +
413219
target.x + "," + target.y;
@@ -418,8 +224,6 @@ class GraphLayout {
418224
.nodes(this.data.anchors)
419225
.on('tick', tick);
420226

421-
// this.simulation.force('link').links(this.data.links);
422-
423227
if (this.simulation.alpha() < 0.1) {
424228
this.simulation.alpha(0.3).restart();
425229
}

0 commit comments

Comments
 (0)