PageSourceSearch

https://chemlambda.github.io/collection-js/myD3Graph.js

js chemlambda.github.io collected 2026-10-03 08:47:15 UTC 10,712 bytes, 481 lines download raw bytes

1// d3 force graphs related, i.e. visualization + physics
2
3
4function myGraph(selector) {
5
6  // Add and remove elements on the graph object
7  this.addNode = function (id, type, x, y) {
8    nodes.push({"id": id, "type": type, x: x, y: y, vx:0, vy:0, links:[]});
9    return nodes[nodes.length-1];
10  };
11
12  this.removeNode = function (id) {
13    var n = findNode(id);
14    while (n.links.length > 0) {
15      removeLinkIndex(links.indexOf(n.links[0]));
16    }
17    nodes.splice(findNodeIndex(id), 1);
18  };
19
20  var removeLinkIndex = function(i) {
21    var slinks = links[i].source.links;
22    slinks.splice(slinks.indexOf(links[i]), 1);
23    var tlinks = links[i].target.links;
24    tlinks.splice(tlinks.indexOf(links[i]), 1);
25    links.splice(i, 1);
26  }
27
28  this.removeLink = function (source, target) {
29    for (var i = 0; i < links.length; i++) {
30      if (links[i].source.id == source && links[i].target.id == target) {
31        removeLinkIndex(i);
32        break;
33      }
34    }
35  };
36
37  this.removeAllLinks = function () {
38    links.splice(0, links.length);
39    update();
40  };
41
42  this.removeAllNodes = function () {
43    nodes.splice(0, nodes.length);
44    links.splice(0, links.length);
45    newNodeIndex = 0;
46    update();
47  };
48
49  this.addLink = function (source, target, value) {
50    var nsource = findNode(source);
51    var ntarget = findNode(target);
52    var newLink = {"source": nsource, "target": ntarget, "value": value};
53    nsource.links.push(newLink);
54    ntarget.links.push(newLink);
55    links.push(newLink);
56  };
57
58  this.findNode = function (id) {
59            for (var i in nodes) {
60                if (nodes[i]["id"] === id) return nodes[i];
61            }
62            ;
63        };
64
65  var findNodeIndex = function (id) {
66    for (var i = 0; i < nodes.length; i++) {
67      if (nodes[i].id == id) {
68        return i;
69      }
70    }
71
72  };
73
74  this.getLinked = function (node) {
75    return node.links.map(function(e) {
76      return (e.source === node ? e.target : e.source);
77    });
78  }
79  
80  this.screenToWorldPoint = function(x,y) {
81    var svgEl = svg.node();
82    var pt = svgEl.createSVGPoint();
83        pt.x = x;
84        pt.y = y;
85        pt =  pt.matrixTransform(vis.node().getCTM().inverse());
86    return pt;
87  }
88
89  // set up the D3 visualisation in the specified element
90
91  var color = d3.scaleOrdinal()
92  .domain(graphNodes)
93  .range(graphNodesColors);
94
95
96  var svg = d3.select(selector)
97    .append("svg:svg")
98    .attr("width", w)
99    .attr("height", h)
100    .attr("id", "svg")
101    .attr("pointer-events", "all")
102    .attr("viewBox", "0 0 " + w + " " + h)
103    .attr("preserveAspectRatio", "xMinYMin meet")  //mod
104    .on("click",backClick)
105
106
107  
108  var vis = svg.append('svg:g');
109
110  var force = d3.forceSimulation();
111
112  this.nodes = force.nodes()
113  this.links = [];
114
115  d3.select("input[id='gravRange']")
116    .on("input", inputted);
117
118//  d3.select("input[id='rewritesRange']")
119//    .on("input", inputted);
120
121  this.update = function () {
122    // Update transform list
123    findAllTransforms();
124    
125    // Update graph
126    var link = vis.selectAll("line")
127    .data(links, function (d) {
128      return d.source.id + "-" + d.target.id;
129    });
130
131    var linkEnter = link.enter()
132      .append("line")
133      .style("stroke-opacity",0).style("fill-opacity",0)
134      .attr("id", function (d) {
135      return d.source.id + "-" + d.target.id;
136    })
137      .attr("stroke-width", function (d) {
138      return d.value / 10;
139    })
140      .transition()
141//      .delay(0.2)
142      .duration(2)
143      .style("stroke-opacity",1).style("fill-opacity",1)
144      .attr("class", "link")
145    //link.append("title")
146    //        .text(function (d) {
147    //            return d.value;
148    //        });
149    link.exit().remove();
150
151    link = link.merge(linkEnter)
152
153    var node = vis.selectAll("g.node")
154    .data(nodes, function (d) {
155      return d.id;
156    });
157
158    var nodeEnter = node.enter().append("g")
159    .attr("class", "node")
160
161    nodeEnter.append("svg:circle")
162      .style("stroke-opacity",0).style("fill-opacity",0)
163      .transition()
164//      .delay(0.2)
165      .duration(1)
166      .style("stroke-opacity",1).style("fill-opacity",1)
167      .attr("r", function(d) {
168      if (d.type == "in" || d.type == "out" || d.type == "middle") {
169        return 4;
170      } else {
171        return 8;
172      }
173    })
174      .attr("id", function (d) {
175      return "Node;" + d.id;
176    })
177      .attr("class", "nodeStrokeClass")
178      .attr("fill", function(d) { return color(d.type); })
179    //nodeEnter.append("svg:text")
180    //        .attr("class", "textClass")
181    //        .attr("x", 14)
182    //        .attr("y", ".31em")
183    //        .text(function (d) {
184    //            return d.type;
185    //        });
186
187    node.exit().remove();
188    
189    node = node.merge(nodeEnter)
190      .on("click",nodeClick)
191      .on("mouseenter",nodeHover)
192      .call(d3.drag()
193        .on("start", dragstarted)
194        .on("drag", dragged)
195        .on("end", dragended)
196      );
197
198    function dragstarted(d) {
199      if (!d3.event.active) force.alphaTarget(0.1).restart();
200      d.fx = d.x;
201      d.fy = d.y;
202    }
203        
204    function dragged(d) {
205      d.fx = d3.event.x;
206      d.fy = d3.event.y;
207    }
208        
209    function dragended(d) {
210      if (!d3.event.active) force.alphaTarget(0);
211      d.fx = null;
212      d.fy = null;
213    }
214
215    function gravForce() {
216      var gravi = (document.getElementById("gravRange").value) / 1000;
217      return gravi;
218    }
219    
220    // Keep node objects on top of edges
221    $(".nodeStrokeClass").each(function( index ) {
222      var gnode = this.parentNode;
223      gnode.parentNode.appendChild(gnode);
224    });
225
226    d3.zoom().on("zoom", function zoom_actions(){
227      vis.attr("transform", d3.event.transform)
228    })(svg)
229    // Restart the force layout.
230    
231    force
232      .alpha(forceAlpha)
233      .alphaDecay(forceAlphaDecay)
234      .velocityDecay(forceVelocityDecay)
235      .force("charge_force", d3.forceManyBody().strength(chargeForceStrength))
236      .force("center_x", d3.forceX(w / 2).strength(gravForce))
237      .force("center_y", d3.forceY(h / 2).strength(gravForce))
238      .force("links", d3.forceLink(links).id(function (d) { return d.id; }).distance(function(d) {
239        if (d.value == 1) {
240          return 2;
241        } else {
242          return 1;
243        }
244      }).strength(forceStrength))
245      .on("tick", function () {
246
247      node.attr("transform", function (d) {
248        return "translate(" + d.x + "," + d.y + ")";
249      });
250
251      link.attr("x1", function (d) {
252        return d.source.x;
253      })
254        .attr("y1", function (d) {
255        return d.source.y;
256      })
257        .attr("x2", function (d) {
258        return d.target.x;
259      })
260        .attr("y2", function (d) {
261        return d.target.y;
262      });
263    })
264    .restart();
265
266// end definition update()
267  };
268
269function inputted() {
270  force.alphaTarget(1);
271  force.restart();
272  update();
273}
274
275// Make it all go
276  update();
277
278// end function myGraph
279}
280
281
282
283function setMode(newMode, newType) {
284  mode = newMode;
285  addType = newType;
286  selection = null;
287}
288
289
290
291function findLinkedOfType(node, type) {
292  var linked = getLinked(node);
293  for (var i=0; i<linked.length; i++) {
294    var o = linked[i];
295    if (o.type == type) {
296      return o;
297    }
298  }
299}
300
301function findLinkedHalfEdge(node) {
302  var linked = getLinked(node);
303  for (var i=0; i<linked.length; i++) {
304    var o = linked[i];
305    if (!isCenter(o)) {
306      return o;
307    }
308  }
309}
310
311function findLinkedCenter(node) {
312  if (isCenter(node)) {
313    return node;
314  }
315  var linked = getLinked(node);
316  for (var i=0; i<linked.length; i++) {
317    var o = linked[i];
318    if (isCenter(o)) {
319      return o;
320    }
321  }
322}
323
324function isCenter(node) {
325  return !(node.type == "in" || node.type == "out" || node.type == "middle");
326}
327
328
329
330
331function removeNodeAndEdges(center) {
332  var linked2 = getLinked(center);
333  for (var i=0; i<linked2.length; i++) {
334    removeNode(linked2[i].id);
335  }
336  removeNode(center.id);
337}
338
339function addNodeAndEdges(type,x,y) {
340  x = x || w/2;
341  y = y || h/2;
342
343  var i = newNodeIndex;
344  
345  var valence = nodeValence[type];
346
347  if (!valence) throw new TypeError("Unknown type" +  type);
348
349  var portTypes = nodePortTypes[valence.length-1];
350  var portList = [i];
351  
352  for (k=0; k<valence.length; k++) {
353    addNode(i+k+1, portTypes[k], x + (Math.random()*20), y + (Math.random()*20))
354    findNode(i+k+1).dir = valence[k];
355    portList.push(i+k+1);
356  }
357  
358  addNode(i, type, x, y);
359  
360  for (k=0; k<valence.length; k++) {
361    addLink(i, i+k+1, 2)
362  }
363  
364  newNodeIndex += valence.length + 1;
365  
366  return portList;
367}
368
369
370  function moveLink1(s1,d2i) {
371    // Connect the other side of s1 to d2
372    var s2 = findLinkedHalfEdge(s1);
373    if (s2 != null) {
374      removeLink(s1.id,s2.id);
375      removeLink(s2.id,s1.id);
376      addLink(s2.id,d2i,2);
377    }
378  }
379
380  function moveLink2(s1,d1) {
381    // Connect the other side of two half-edges to each other
382    var s2 = findLinkedHalfEdge(s1);
383    var d2 = findLinkedHalfEdge(d1);
384    if (s2 != null) {
385      removeLink(s1.id,s2.id);
386      removeLink(s2.id,s1.id);
387    }
388    if (d2 != null) {
389      removeLink(d1.id,d2.id);
390      removeLink(d2.id,d1.id);
391    }
392    if (s2 != null && d2 != null && s2 != d1)
393      addLink(s2.id,d2.id,2);
394  }
395
396function backClick(d,i) {
397  var e = d3.event;
398  var pt = screenToWorldPoint(e.offsetX,e.offsetY)
399      
400  switch (mode) {
401    case "add":
402      addNodeAndEdges(addType,pt.x,pt.y);
403      update();
404      break;
405    case "delete":
406      break;
407    case "transform":
408      break;
409    case "link":
410      selection = null;
411      break;
412  }
413}
414
415function nodeClick(d,i) {
416  var e = d3.event;
417  switch (mode) {
418    case "add":
419      break;
420    case "delete":
421      var center = findLinkedCenter(d);
422
423      removeNodeAndEdges(center);
424      update();
425
426      break;
427    case "transform":
428      var trans = findTransform(d);
429      if (trans) {
430        doTransform(d, trans);
431        update();
432      }
433
434      break;
435    case "link":
436      var linkCount = getLinked(d).length;
437
438      if (isCenter(d)) {
439        return;
440      }
441
442      if (linkCount == 1) {
443        if (selection != null && selection.dir != d.dir) {
444          addLink(selection.id, d.id, 2);
445          update();
446          selection = null;
447        } else {
448          selection = d;
449        }
450      } else {
451        var other = findLinkedHalfEdge(d);
452        removeLink(d.id, other.id);
453        removeLink(other.id, d.id);
454        selection = null;
455      }
456      update();
457      break;
458  }
459
460  e.stopPropagation();
461}
462
463function nodeHover(d,i) {
464  var e = d3.event;
465  exportMolToScreen();
466  switch (mode) {
467    case "transform":
468      var trans = findTransform(d);
469      if (trans) {
470        doTransform(d, trans);
471        update();
472      }
473
474      break;
475  }
476  exportMolToScreenAfter();
477}
478
479
480
481

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.