graph: zooming and dragging, changed default repulsion force
This commit is contained in:
		
							parent
							
								
									2006216a99
								
							
						
					
					
						commit
						b1dcf45db6
					
				
					 4 changed files with 44 additions and 20 deletions
				
			
		|  | @ -8,10 +8,16 @@ | ||||||
| 
 | 
 | ||||||
|   let selectedTask: string | null = null; |   let selectedTask: string | null = null; | ||||||
|   let clickedTask: string | null = null; |   let clickedTask: string | null = null; | ||||||
| 
 |   let repulsionForce: number = -600; | ||||||
|   let clicked: string[] = []; |   let clicked: string[] = []; | ||||||
| 
 | 
 | ||||||
|   function clickTask(e: CustomEvent<TaskDescriptor>) { |   function clickTask(e: CustomEvent<TaskDescriptor>) { | ||||||
|  |     // sanity check | ||||||
|  |     if (selectedTask == null) { | ||||||
|  |       alert("tohle je divny event"); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     // ukladani seznamu poslednich kliknuti |     // ukladani seznamu poslednich kliknuti | ||||||
|     clicked.push(selectedTask); |     clicked.push(selectedTask); | ||||||
|     if (clicked.length > 3) |     if (clicked.length > 3) | ||||||
|  | @ -41,6 +47,9 @@ | ||||||
|         } |         } | ||||||
|       }); |       }); | ||||||
|       tasks = tasks; |       tasks = tasks; | ||||||
|  | 
 | ||||||
|  |       // run simulation | ||||||
|  |       toggleDivnaPromena(); | ||||||
|     } else { |     } else { | ||||||
|       alert("Nope, prvni musis nekam klikat..."); |       alert("Nope, prvni musis nekam klikat..."); | ||||||
|     } |     } | ||||||
|  | @ -48,7 +57,7 @@ | ||||||
| 
 | 
 | ||||||
|   let hovnoDivnaPromenaKteraJeFaktFuj = true; |   let hovnoDivnaPromenaKteraJeFaktFuj = true; | ||||||
|   function toggleDivnaPromena() { |   function toggleDivnaPromena() { | ||||||
|       hovnoDivnaPromenaKteraJeFaktFuj = ! hovnoDivnaPromenaKteraJeFaktFuj; |     hovnoDivnaPromenaKteraJeFaktFuj = !hovnoDivnaPromenaKteraJeFaktFuj; | ||||||
|   } |   } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  | @ -62,7 +71,7 @@ | ||||||
|     display: flex; |     display: flex; | ||||||
|     flex-direction: row; |     flex-direction: row; | ||||||
|     margin: 0; |     margin: 0; | ||||||
|     height: 100vh; |     height: 99vh; | ||||||
|     width: 100%; |     width: 100%; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -106,7 +115,12 @@ | ||||||
|   <div class="left"> |   <div class="left"> | ||||||
|     <div class="lastClicked">Last clicked: <b>{clicked.join(' | ')}</b></div> |     <div class="lastClicked">Last clicked: <b>{clicked.join(' | ')}</b></div> | ||||||
|     <div class="graph"> |     <div class="graph"> | ||||||
|       <Graph {tasks} bind:selectedTask on:selectTask={clickTask} runSimulationWeirdHack={hovnoDivnaPromenaKteraJeFaktFuj} /> |       <Graph | ||||||
|  |         {tasks} | ||||||
|  |         {repulsionForce} | ||||||
|  |         bind:selectedTask | ||||||
|  |         on:selectTask={clickTask} | ||||||
|  |         runSimulationWeirdHack={hovnoDivnaPromenaKteraJeFaktFuj} /> | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
|   <div class="right"> |   <div class="right"> | ||||||
|  | @ -115,12 +129,14 @@ | ||||||
|       <div> |       <div> | ||||||
|         <button on:click={addEdge}>Pridat hranu - posledni vyzaduje predposledni</button> |         <button on:click={addEdge}>Pridat hranu - posledni vyzaduje predposledni</button> | ||||||
|       </div> |       </div> | ||||||
|  |       <div><button on:click={toggleDivnaPromena}>Spustit simulaci</button></div> | ||||||
|       <div> |       <div> | ||||||
|         <button on:click={toggleDivnaPromena}>Spustit simulaci</button> |         Repulsion force: <input type="number" bind:value={repulsionForce} name="repulsionForceInput" max="1000" min="-10000" /> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|     <div class="taskDetails"> |     <div class="taskDetails"> | ||||||
|       {#if currentTask != null} |       {#if currentTask != null} | ||||||
|  |         start | ||||||
|         <h3>{currentTask}</h3> |         <h3>{currentTask}</h3> | ||||||
|         <span>{taskMap.get(currentTask).comment}</span> |         <span>{taskMap.get(currentTask).comment}</span> | ||||||
|         <ul> |         <ul> | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ | ||||||
| 
 | 
 | ||||||
|   export let tasks: TasksFile; |   export let tasks: TasksFile; | ||||||
|   export let selectedTask: null | string = null; |   export let selectedTask: null | string = null; | ||||||
|  |   export let repulsionForce: number = -600; | ||||||
| 
 | 
 | ||||||
|   $: nodes = tasks.tasks; |   $: nodes = tasks.tasks; | ||||||
|   $: edges = createLinksFromTaskMap(tasks); |   $: edges = createLinksFromTaskMap(tasks); | ||||||
|  | @ -45,7 +46,7 @@ | ||||||
|           }) // This provide  the id of a node |           }) // This provide  the id of a node | ||||||
|           .links(edges) // and this the list of links |           .links(edges) // and this the list of links | ||||||
|       ) |       ) | ||||||
|       .force("charge", d3.forceManyBody().strength(-400)) // This adds repulsion between nodes. Play with the -400 for the repulsion strength |       .force("charge", d3.forceManyBody().strength(repulsionForce)) // This adds repulsion between nodes. Play with the -400 for the repulsion strength | ||||||
|       .force("x", d3.forceX()) // attracts elements to the zero X coord |       .force("x", d3.forceX()) // attracts elements to the zero X coord | ||||||
|       .force("y", d3.forceY()) // attracts elements to the zero Y coord |       .force("y", d3.forceY()) // attracts elements to the zero Y coord | ||||||
|       .on("tick", ticked) |       .on("tick", ticked) | ||||||
|  | @ -61,19 +62,29 @@ | ||||||
|   // run on create |   // run on create | ||||||
|   onMount(() => { |   onMount(() => { | ||||||
|     // set the dimensions and margins of the graph |     // set the dimensions and margins of the graph | ||||||
|     var margin = { top: 10, right: 30, bottom: 30, left: 40 }, |       const width = container.clientWidth; | ||||||
|       width = container.clientWidth - margin.left - margin.right, |       const height = container.clientHeight; | ||||||
|       height = container.clientHeight - margin.top - margin.bottom; |  | ||||||
| 
 | 
 | ||||||
|     // resize the svg object |     // resize the svg object | ||||||
|     var svg = d3 |     var svg = d3 | ||||||
|       .select(container) |       .select(container) | ||||||
|       .select("svg") |       .select("svg") | ||||||
|       .attr("width", width + margin.left + margin.right) |       .attr("width", width) | ||||||
|       .attr("height", height + margin.top + margin.bottom) |       .attr("height", height) | ||||||
|       .attr("viewBox", [-width / 2, -height / 2, width, height]) |       .attr("viewBox", [-width / 2, -height / 2, width, height]) | ||||||
|       .select("g") |       .select("g"); | ||||||
|       .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | 
 | ||||||
|  |     function zoomed(e) { | ||||||
|  |       let { transform } = e; | ||||||
|  |       svg.attr("transform", transform); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     d3.select(container).call( | ||||||
|  |       d3 | ||||||
|  |         .zoom() | ||||||
|  |         .scaleExtent([0.1, 2]) | ||||||
|  |         .on("zoom", zoomed) | ||||||
|  |     ); | ||||||
| 
 | 
 | ||||||
|     runSimulation(); |     runSimulation(); | ||||||
|   }); |   }); | ||||||
|  | @ -85,7 +96,6 @@ | ||||||
|     runSimulation(); |     runSimulation(); | ||||||
|   } |   } | ||||||
|   // now it's safe to stop vomitting 🤮 |   // now it's safe to stop vomitting 🤮 | ||||||
| 
 |  | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <style> | <style> | ||||||
|  | @ -104,6 +114,7 @@ | ||||||
|       {#each nodes as task} |       {#each nodes as task} | ||||||
|         <GraphNode |         <GraphNode | ||||||
|           {task} |           {task} | ||||||
|  |           on:taskClick | ||||||
|           on:click={nodeClick(task)} |           on:click={nodeClick(task)} | ||||||
|           on:hoveringChange={nodeHover(task)} /> |           on:hoveringChange={nodeHover(task)} /> | ||||||
|       {/each} |       {/each} | ||||||
|  |  | ||||||
|  | @ -1,8 +1,6 @@ | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
|     import type { SimulationLinkDatum } from "d3"; |     import type { SimulationLinkDatum } from "d3"; | ||||||
|    |    | ||||||
|     import { onMount } from "svelte"; |  | ||||||
|    |  | ||||||
|     import type { TaskDescriptor } from "./task-loader"; |     import type { TaskDescriptor } from "./task-loader"; | ||||||
|    |    | ||||||
|     export let edge: SimulationLinkDatum<TaskDescriptor>; |     export let edge: SimulationLinkDatum<TaskDescriptor>; | ||||||
|  |  | ||||||
|  | @ -1,17 +1,15 @@ | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
|   import { createEventDispatcher, onMount } from "svelte"; |   import { createEventDispatcher, onMount } from "svelte"; | ||||||
| 
 |  | ||||||
|   import type { TaskDescriptor } from "./task-loader"; |   import type { TaskDescriptor } from "./task-loader"; | ||||||
| 
 | 
 | ||||||
|   export let task: TaskDescriptor; |   export let task: TaskDescriptor; | ||||||
|   let hovering: boolean = false; |   let hovering: boolean = false; | ||||||
|   let text_element: SVGTextElement; |   let text_element: SVGTextElement; | ||||||
| 
 | 
 | ||||||
|   const eventDispatcher = createEventDispatcher() |  | ||||||
| 
 |  | ||||||
|   $: cx = task === undefined || task.x === undefined ? 0 : task.x; |   $: cx = task === undefined || task.x === undefined ? 0 : task.x; | ||||||
|   $: cy = task === undefined || task.y === undefined ? 0 : task.y; |   $: cy = task === undefined || task.y === undefined ? 0 : task.y; | ||||||
|    |    | ||||||
|  |   const eventDispatcher = createEventDispatcher() | ||||||
|   function enter() { |   function enter() { | ||||||
|     hovering = true; |     hovering = true; | ||||||
|     eventDispatcher("hoveringChange", hovering) |     eventDispatcher("hoveringChange", hovering) | ||||||
|  | @ -26,6 +24,7 @@ | ||||||
|     eventDispatcher("click", e) |     eventDispatcher("click", e) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   // automatically size the bubbles to fit the text | ||||||
|   let ellipse_rx = 20; |   let ellipse_rx = 20; | ||||||
|   let ellipse_ry = 20; |   let ellipse_ry = 20; | ||||||
|   onMount(() => { |   onMount(() => { | ||||||
|  |  | ||||||
		Reference in a new issue