You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

mod_e.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. // https://observablehq.com/@jinnace/sticky-force-layout@137
  2. export default function define(runtime, observer) {
  3. const main = runtime.module();
  4. yglob.m = main
  5. main.variable(observer()).define(["md"], function(md){return(
  6. md`# Sticky Force Layout
  7. In a [force simulation](https://github.com/d3/d3-force#simulation_nodes), we set *d.*fx = x and *d.*fy = y while dragging, to fix the nodes in the ⟨x,y⟩ position after they have been repositioned by the user.
  8. Click on a node to release it from its fixed position. Note that the force layout resumes automatically on drag. This ensures that other nodes in the graph respond naturally to the dragged node’s movement.
  9. `
  10. )});
  11. // main.variable(observer()).define(["d3","width","height","graph","clamp"], function*(d3,width,height,graph,clamp)
  12. // main.variable(observer("chart")).define("chart", ["data","d3","width","height","types","color","location","drag","linkArc","invalidation"], function(data,d3,width,height,types,color,location,drag,linkArc,invalidation)
  13. // main.variable(observer()).define( ["d3","width","height","graph","clamp"], function(d3,width,height,graph,clamp)
  14. main.variable(observer()).define(["d3","width","height","graph","clamp"], function*(d3,width,height,graph,clamp)
  15. {
  16. clog("vbox")
  17. var cwidth,cheight,cx,cy
  18. var cwidth_,cheight_,cx_,cy_
  19. if (1 || yglob.svg_m > 1){
  20. cwidth_ = width * (yglob.svg_m - 1)/2
  21. cheight_ = height * (yglob.svg_m - 1)/2
  22. cwidth = width*yglob.svg_m - cwidth_
  23. cheight = height*yglob.svg_m - cheight_
  24. cx = 0 - cwidth_
  25. cy = 0 - cheight_
  26. } else {
  27. cwidth = width*yglob.svg_m
  28. cheight = height*yglob.svg_m
  29. cx =0
  30. cy = 0
  31. }
  32. clog("vbox",{cwidth,cheight,cx,cy,cwidth_,cheight_})
  33. // const svg = d3.create("svg").attr("viewBox", [0, 0, width*yglob.svg_m, height*yglob.svg_m]),
  34. const svg = d3.create("svg").attr("viewBox", [cx, cy, cwidth, cheight]),
  35. link = svg
  36. .selectAll(".link")
  37. .data(graph.links)
  38. .join("line")
  39. .classed("link", true)
  40. /*
  41. node = svg
  42. .selectAll(".node")
  43. .data(graph.nodes)
  44. .join("circle")
  45. .attr("r", 12)
  46. .classed("node", true)
  47. .classed("fixed", d => d.fx !== undefined);
  48. // .join("g")
  49. // .attr("r", 12)
  50. // .classed("node", true)
  51. // .classed("fixed", d => d.fx !== undefined);
  52. //
  53. */
  54. /*
  55. const node = svg
  56. .selectAll(".node")
  57. .data(graph.nodes)
  58. .join("circle")
  59. .attr("r", 12)
  60. .classed("node", true)
  61. .classed("fixed", d => d.fx !== undefined);
  62. */
  63. // svg.append("g").attr("id","tx").classed("tnodex", true).text("Q")
  64. // /*
  65. const node = svg
  66. .selectAll(".node")
  67. .data(graph.nodes)
  68. .join("g")
  69. .attr("r", 12)
  70. .classed("node", true)
  71. .classed("fixed", d => d.fx !== undefined)
  72. svg.append("text").attr("id","tx").classed("tnodex", true).text("Q")
  73. // */
  74. const node_l = svg.append("g").classed("n2",1)
  75. .selectAll("g")
  76. // .selectAll(".node")
  77. .data(graph.nodes)
  78. // .join("circle")
  79. .join("g")
  80. // .attr("x", "16px")
  81. // .attr("transform", "translate(40,2)")
  82. .attr("fill", "#0ff")
  83. .classed("node", true)
  84. .classed("node_l", true)
  85. .classed("fixed", (d) => {clog("zx~",d);return d.fx !== undefined})
  86. .classed("fx", glob_dev_fns.cls_picker || nop)
  87. // const node = node_l
  88. //
  89. node_l.append("circle")
  90. .attr("r", 6)
  91. .attr("transform", "translate(1,-18)")
  92. // svg.selectAll("#tx").after(node_l)
  93. // svg.selectAll
  94. /*
  95. .join("circle")
  96. .attr("r", 12)
  97. .classed("node", true)
  98. .classed("fixed", d => d.fx !== undefined);
  99. /*
  100. node.append("circle")
  101. .attr("r", 12)
  102. .classed("node", true)
  103. .classed("fixed", d => {
  104. clog("C")
  105. return d.fx !== undefined || 11
  106. }).clone(true)
  107. */
  108. // .classed("fixed", d => d.fx !== undefined || 11).clone(true)
  109. console.log("z61",graph,graph.nodes)
  110. console.log("z62",graph.nodes,length)
  111. var kx,vx
  112. for (vx of graph.nodes){
  113. clog("z~",vx)
  114. }
  115. console.log("NODE.",svg)
  116. // var z = node.append("use").attr("href","#tx")
  117. var z = node.append("text")
  118. .attr("x", 10)
  119. // .attr("y", "0.3em")
  120. .attr("y", "1em")
  121. // .attr("fill","#000")
  122. /*
  123. .style("border-bottom-color","#000")
  124. .attr("border-bottom-color","#000")
  125. .attr("text-decoration","underline")
  126. .attr("text-decoration-color","#00f")
  127. .attr("text-decoration-style","wavy")
  128. .style("text-decoration","underline")
  129. .style("text-decoration-color","#00f")
  130. .style("text-decoration-style","wavy")
  131. .style("text-decoration-color","#00f")
  132. .attr("underline-thickness",3)
  133. .attr("stroke", "#004")
  134. .style("stroke", "#004")
  135. .attr("stroke-width", 0)
  136. */
  137. .text(d => {clog(d.id,d,this); return d.index + " Z"})
  138. // .text(d => {clog(d.id,d,this); return d.index + " Z"}).append("text").text("???")
  139. node.append("circle").attr("r", 12)
  140. // node.style("border-bottom-color","#000")
  141. // node.attr("border-bottom-color","#000")
  142. // border-bottom-color: blue;
  143. // .attr("fill","#0ff")
  144. // .style("background","#0ff")
  145. // .style(`background:#0ff;`)
  146. // .style({background:"#0ff"})
  147. // .css({background:"#0ff"})
  148. console.log("NODE",svg,node,[z],z.style,[svg.node])
  149. /*
  150. let transform;
  151. const zoom = d3.zoom().on("zoom", e => {
  152. node.attr("transform", (transform = e.transform));
  153. node.style("stroke-width", 3 / Math.sqrt(transform.k));
  154. // points.attr("r", 3 / Math.sqrt(transform.k));
  155. });
  156. svg.call(zoom)
  157. .call(zoom.transform, d3.zoomIdentity)
  158. */
  159. yield svg.node();
  160. /*
  161. clog("z?",graph)
  162. for (vx of graph.nodes){
  163. clog("z~",vx)
  164. }
  165. */
  166. // dev_dec_gen(glob_dev_fns.forceManyBody,"forceManyBody_",[])
  167. // dev_dec_gen(glob_dev_fns.forceCenter,"forceCenter_",[width / 2, height / 2])
  168. // dev_dec_gen(glob_dev_fns.forceLink,"forceLink_",[graph.links])
  169. // glob_dev_fns.forceManyBody_
  170. // glob_dev_fns.forceCenter_
  171. // glob_dev_fns.forceLink_
  172. // clog()
  173. clog("d3_",d3,d3.evl,d3.evl(glob_dev_fns.forceManyBody + ""))
  174. Object.assign(window.yglob,{d3})
  175. const simulation = d3
  176. .forceSimulation()
  177. .nodes(graph.nodes)
  178. .force("charge",dev_dec_gen(glob_dev_fns.forceManyBody,"forceManyBody_",[]))
  179. .force("center",dev_dec_gen(glob_dev_fns.forceCenter,"forceCenter_",[width / 2, height / 2]))
  180. .force("link", dev_dec_gen(glob_dev_fns.forceLink,"forceLink_",[graph.links]))
  181. // .force("charge", d3.forceManyBody())
  182. // .force("center", d3.forceCenter(width / 2, height / 2))
  183. // .force("link", d3.forceLink(graph.links))
  184. .on("tick", tick);
  185. /*
  186. .force("charge", d3.evl(`clog("d3_~");ret= `+glob_dev_fns.forceManyBody)())
  187. .force("center", d3.evl(`clog("d3_~");ret= `+glob_dev_fns.forceCenter)(width / 2, height / 2))
  188. .force("link", d3.evl(`clog("d3_~");ret= `+glob_dev_fns.forceLink)(graph.links))
  189. .force("charge", glob_dev_fns.forceManyBody())
  190. .force("center", glob_dev_fns.forceCenter(width / 2, height / 2))
  191. .force("link", glob_dev_fns.forceLink(graph.links))
  192. .force("charge", d3.forceManyBody())
  193. .force("center", d3.forceCenter(width / 2, height / 2))
  194. .force("link", d3.forceLink(graph.links))
  195. .force("charge", d3.evl(`
  196. clog("d3_C")
  197. ret.fn=` +glob_dev_fns.forceManyBody + "").fn())
  198. .force("center", d3.evl(`
  199. clog("d3_C")
  200. ret.fn=` +glob_dev_fns.forceCenter + "").fn(width / 2, height / 2))
  201. .force("link", d3.evl(`
  202. clog("d3_C")
  203. ret.fn=` +glob_dev_fns.forceLink + "").fn(graph.links))
  204. */
  205. const drag = d3
  206. .drag()
  207. .on("start", dragstart)
  208. .on("drag", dragged);
  209. node.call(drag).on("click", click);
  210. node_l.call(drag).on("click", click);
  211. function tick() {
  212. return glob_dev_fns["tick"].apply(this,arguments)
  213. }
  214. function click(event, d) {
  215. return glob_dev_fns["click"].apply(this,arguments)
  216. }
  217. function dragstart() {
  218. return glob_dev_fns["dragstart"].apply(this,arguments)
  219. }
  220. function dragged(event, d) {
  221. return glob_dev_fns["dragged"].apply(this,arguments)
  222. }
  223. // function tick() {}
  224. function tick_() {
  225. link
  226. .attr("x1", d => d.source.x)
  227. .attr("y1", d => d.source.y)
  228. .attr("x2", d => d.target.x)
  229. .attr("y2", d => d.target.y);
  230. node.attr("transform", d => `translate(${d.x},${d.y})`);
  231. node_l.attr("transform", d => `translate(${d.x},${d.y})`);
  232. // node.find("text").text(d => `${d.index} z?`)
  233. // node.text(d => `${d.index} z?`)
  234. // node.text(d => { // clog("Ntxt") return`${d.index} z?`})
  235. z.text(d => {
  236. // clog("Ntxt")
  237. return`${d.index} ${d.i2 != undefined ? d.i2 : "_"}`
  238. return`${d.index} ${d.i2 || "_"}`
  239. return`${d.index} ${d.x.toFixed(2)},${d.y.toFixed(2)}`
  240. })
  241. if (!yglob.n1){
  242. clog("vbx",node,z)
  243. yglob.n1 =1
  244. }
  245. // node
  246. // clog("t`",this,arguments)
  247. // node_l.attr("transform", d => `translate(${d.x},${d.y})`);
  248. // node_l.attr("transform", d => `translate(${d.x},${d.y})`);
  249. /*
  250. node
  251. .attr("cx", d => d.x)
  252. .attr("cy", d => d.y);
  253. */
  254. }
  255. function click_(event, d) {
  256. delete d.fx;
  257. delete d.fy;
  258. clog("click")
  259. d3.select(this).classed("fixed", false);
  260. simulation.alpha(1).restart();
  261. }
  262. function dragstart_() {
  263. clog("cli drag")
  264. d3.select(this).classed("fixed", true);
  265. }
  266. function dragged_(event, d) {
  267. d.fx = clamp(event.x, 0, width);
  268. d.fy = clamp(event.y, 0, height);
  269. // clog("DRAGGED2")
  270. // simulation.alpha(0).restart();
  271. simulation.alpha(1).restart();
  272. }
  273. glob_dev_fns["tick"] = tick_
  274. glob_dev_fns["click"] = click_
  275. glob_dev_fns["dragstart"] = dragstart_
  276. glob_dev_fns["dragged"] = dragged_
  277. glob_dev_fns["tick_"] = tick_
  278. glob_dev_fns["click_"] = click_
  279. glob_dev_fns["dragstart_"] = dragstart_
  280. glob_dev_fns["dragged_"] = dragged_
  281. clog(":vbox")
  282. window.yglob = window.yglob || {}
  283. Object.assign(window.yglob,{simulation,d3})
  284. // window.yglob = {simulation}
  285. }
  286. );
  287. main.variable(observer("d3")).define("d3", ["require"], function(require){return(
  288. // clog("D3 .0")
  289. require("/ign/libs/d3_.js")
  290. // require("d3@6")
  291. // clog("D3 .1")
  292. )});
  293. main.variable(observer("height")).define("height", ["width"], function(width){return(
  294. Math.min(500, width * 0.6)
  295. )});
  296. main.variable(observer("clamp")).define("clamp", function(){return(
  297. function clamp(x, lo, hi) {
  298. return x < lo ? lo : x > hi ? hi : x;
  299. }
  300. )});
  301. /*
  302. main.variable(observer()).define(["html"], function(html){return(
  303. html`<style>
  304. .link {
  305. stroke: #000;
  306. stroke-width: 1.5px;
  307. }
  308. .node {
  309. cursor: move;
  310. fill: #ccc;
  311. stroke: #000;
  312. stroke-width: 1.5px;
  313. }
  314. .node.fixed {
  315. fill: #f00;
  316. }
  317. </style>`
  318. )});
  319. */
  320. main.variable(observer("graph")).define("graph", function(){
  321. clog("d3_mg")
  322. // return(
  323. const ret = (
  324. {
  325. // nodes: Array.from({length:10}, () => ({})),
  326. nodes: Array.from({length:16}, () => ({})),
  327. // nodes: Array.from({length:13}, () => ({})),
  328. links: [
  329. { source: 0, target: 1 },
  330. { source: 1, target: 2 },
  331. { source: 2, target: 0 },
  332. { source: 1, target: 3 },
  333. { source: 3, target: 2 },
  334. { source: 3, target: 4 },
  335. { source: 4, target: 5 },
  336. { source: 5, target: 6 },
  337. { source: 5, target: 7 },
  338. { source: 6, target: 7 },
  339. { source: 6, target: 8 },
  340. { source: 7, target: 8 },
  341. { source: 9, target: 4 },
  342. { source: 9, target: 11 },
  343. { source: 9, target: 10 },
  344. { source: 10, target: 11 },
  345. { source: 11, target: 12 },
  346. { source: 12, target: 10 },
  347. { source: 13, target: 14 },
  348. ]
  349. }
  350. )
  351. window.yglob.main = ret
  352. return ret
  353. });
  354. return main;
  355. }
  356. // window.yglob.dbg_flags
  357. // window.yglob.dbg_flags.
  358. window.yglob = window.yglob || {
  359. svg_m:1,
  360. dbg_flags:{
  361. center:1,
  362. center_f:1,
  363. },
  364. }
  365. // clog("vbox..0")