Skip to content

Commit 9e55de3

Browse files
authored
Force layout (#4)
This adds the force module. Also adds a viridis color interpolator.
1 parent ae1b240 commit 9e55de3

Some content is hidden

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

53 files changed

+89808
-35704
lines changed

docs/Background/iframe.html

Lines changed: 19251 additions & 0 deletions
Large diffs are not rendered by default.

docs/Background/index.html

Lines changed: 359 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Background Example</title>
6+
<style media="screen">
7+
body {
8+
font-family: Helvetica, sans-serif;
9+
display: flex;
10+
}
11+
main {
12+
order: 1;
13+
}
14+
aside {
15+
width: 300px;
16+
}
17+
aside ul {
18+
padding: 0;
19+
}
20+
aside li {
21+
list-style-type: none;
22+
margin: 10px;
23+
}
24+
aside li.active a {
25+
text-decoration: none;
26+
}
27+
iframe {
28+
border: 1px solid black;
29+
}
30+
hr:first-of-type {
31+
display: none;
32+
}
33+
/*
34+
35+
Atom One Light by Daniel Gamage
36+
Original One Light Syntax theme from https://github.com/atom/one-light-syntax
37+
38+
base: #fafafa
39+
mono-1: #383a42
40+
mono-2: #686b77
41+
mono-3: #a0a1a7
42+
hue-1: #0184bb
43+
hue-2: #4078f2
44+
hue-3: #a626a4
45+
hue-4: #50a14f
46+
hue-5: #e45649
47+
hue-5-2: #c91243
48+
hue-6: #986801
49+
hue-6-2: #c18401
50+
51+
*/
52+
53+
.hljs {
54+
display: block;
55+
overflow-x: auto;
56+
padding: 0.5em;
57+
color: #383a42;
58+
background: #fafafa;
59+
}
60+
61+
.hljs-comment,
62+
.hljs-quote {
63+
color: #a0a1a7;
64+
font-style: italic;
65+
}
66+
67+
.hljs-doctag,
68+
.hljs-keyword,
69+
.hljs-formula {
70+
color: #a626a4;
71+
}
72+
73+
.hljs-section,
74+
.hljs-name,
75+
.hljs-selector-tag,
76+
.hljs-deletion,
77+
.hljs-subst {
78+
color: #e45649;
79+
}
80+
81+
.hljs-literal {
82+
color: #0184bb;
83+
}
84+
85+
.hljs-string,
86+
.hljs-regexp,
87+
.hljs-addition,
88+
.hljs-attribute,
89+
.hljs-meta-string {
90+
color: #50a14f;
91+
}
92+
93+
.hljs-built_in,
94+
.hljs-class .hljs-title {
95+
color: #c18401;
96+
}
97+
98+
.hljs-attr,
99+
.hljs-variable,
100+
.hljs-template-variable,
101+
.hljs-type,
102+
.hljs-selector-class,
103+
.hljs-selector-attr,
104+
.hljs-selector-pseudo,
105+
.hljs-number {
106+
color: #986801;
107+
}
108+
109+
.hljs-symbol,
110+
.hljs-bullet,
111+
.hljs-link,
112+
.hljs-meta,
113+
.hljs-selector-id,
114+
.hljs-title {
115+
color: #4078f2;
116+
}
117+
118+
.hljs-emphasis {
119+
font-style: italic;
120+
}
121+
122+
.hljs-strong {
123+
font-weight: bold;
124+
}
125+
126+
.hljs-link {
127+
text-decoration: underline;
128+
}
129+
130+
</style>
131+
</head>
132+
<body>
133+
<main>
134+
<h1>Background</h1>
135+
136+
<iframe src="iframe.html" width="990" height="504" frameborder="1"></iframe>
137+
138+
139+
<hr />
140+
<h3>Background</h3>
141+
<div class="description"><p> Part of a composition used for the background of my Elm Europe talk.</p>
142+
</div>
143+
<pre><code class="elm"><span class="hljs-keyword">module</span> Background <span class="hljs-keyword">exposing</span> (main)
144+
145+
146+
<span class="hljs-keyword">import</span> Color <span class="hljs-keyword">exposing</span> (<span class="hljs-type">Color</span>)
147+
<span class="hljs-keyword">import</span> Color.Convert <span class="hljs-keyword">exposing</span> (colorToCssRgb)
148+
<span class="hljs-keyword">import</span> Graph <span class="hljs-keyword">exposing</span> (<span class="hljs-type">Edge</span>, <span class="hljs-type">Graph</span>, <span class="hljs-type">Node</span>, <span class="hljs-type">NodeId</span>)
149+
<span class="hljs-keyword">import</span> IntDict
150+
<span class="hljs-keyword">import</span> List <span class="hljs-keyword">exposing</span> (range)
151+
<span class="hljs-keyword">import</span> NetworkGraphs <span class="hljs-keyword">exposing</span> (miserablesGraph, pollbooksGraph)
152+
<span class="hljs-keyword">import</span> Svg <span class="hljs-keyword">exposing</span> (..)
153+
<span class="hljs-keyword">import</span> Svg.Attributes <span class="hljs-keyword">as</span> Attr <span class="hljs-keyword">exposing</span> (..)
154+
<span class="hljs-keyword">import</span> Visualization.Force <span class="hljs-keyword">as</span> Force <span class="hljs-keyword">exposing</span> (<span class="hljs-type">State</span>)
155+
<span class="hljs-keyword">import</span> Visualization.Scale <span class="hljs-keyword">as</span> Scale <span class="hljs-keyword">exposing</span> (<span class="hljs-type">SequentialScale</span>)
156+
157+
158+
<span class="hljs-title">screenWidth</span> : <span class="hljs-type">Float</span>
159+
<span class="hljs-title">screenWidth</span> =
160+
<span class="hljs-number">990</span>
161+
162+
163+
<span class="hljs-title">screenHeight</span> : <span class="hljs-type">Float</span>
164+
<span class="hljs-title">screenHeight</span> =
165+
<span class="hljs-number">504</span>
166+
167+
168+
<span class="hljs-title">colorScale</span> : <span class="hljs-type">SequentialScale</span> <span class="hljs-type">Color</span>
169+
<span class="hljs-title">colorScale</span> =
170+
<span class="hljs-type">Scale</span>.sequential ( <span class="hljs-number">200</span>, <span class="hljs-number">700</span> ) <span class="hljs-type">Scale</span>.viridisInterpolator
171+
172+
173+
<span class="hljs-keyword">type</span> <span class="hljs-keyword">alias</span> <span class="hljs-type">CustomNode</span> =
174+
{ rank : <span class="hljs-type">Int</span>, name : <span class="hljs-type">String</span> }
175+
176+
177+
<span class="hljs-keyword">type</span> <span class="hljs-keyword">alias</span> <span class="hljs-type">Entity</span> =
178+
<span class="hljs-type">Force</span>.<span class="hljs-type">Entity</span> <span class="hljs-type">NodeId</span> { value : <span class="hljs-type">CustomNode</span> }
179+
180+
181+
<span class="hljs-title">init</span> : <span class="hljs-type">Graph</span> <span class="hljs-type">Entity</span> ()
182+
<span class="hljs-title">init</span> =
183+
<span class="hljs-keyword">let</span>
184+
graph =
185+
<span class="hljs-type">Graph</span>.mapContexts
186+
(\({ node, incoming, outgoing } <span class="hljs-keyword">as</span> ctx) -&gt;
187+
{ ctx | node = { label = <span class="hljs-type">Force</span>.entity node.id (<span class="hljs-type">CustomNode</span> (<span class="hljs-type">IntDict</span>.size incoming + <span class="hljs-type">IntDict</span>.size outgoing) node.label), id = node.id } }
188+
)
189+
miserablesGraph
190+
191+
links =
192+
graph
193+
|&gt; <span class="hljs-type">Graph</span>.edges
194+
|&gt; <span class="hljs-type">List</span>.map (\{ from, to } -&gt; { source = from, target = to, distance = <span class="hljs-number">30</span>, strength = <span class="hljs-type">Nothing</span> })
195+
196+
forces =
197+
[ <span class="hljs-type">Force</span>.customLinks <span class="hljs-number">1</span> links
198+
, <span class="hljs-type">Force</span>.manyBodyStrength <span class="hljs-number">-30</span> &lt;| <span class="hljs-type">List</span>.map .id &lt;| <span class="hljs-type">Graph</span>.nodes graph
199+
, <span class="hljs-type">Force</span>.center (screenWidth / <span class="hljs-number">2</span>) (screenHeight / <span class="hljs-number">2</span>)
200+
]
201+
<span class="hljs-keyword">in</span>
202+
updateGraphWithList graph (<span class="hljs-type">Force</span>.computeSimulation (<span class="hljs-type">Force</span>.simulation forces) &lt;| <span class="hljs-type">List</span>.map .label &lt;| <span class="hljs-type">Graph</span>.nodes graph)
203+
204+
205+
<span class="hljs-title">updateGraphWithList</span> : <span class="hljs-type">Graph</span> <span class="hljs-type">Entity</span> () -&gt; <span class="hljs-type">List</span> <span class="hljs-type">Entity</span> -&gt; <span class="hljs-type">Graph</span> <span class="hljs-type">Entity</span> ()
206+
<span class="hljs-title">updateGraphWithList</span> =
207+
<span class="hljs-keyword">let</span>
208+
graphUpdater value =
209+
<span class="hljs-type">Maybe</span>.map (\ctx -&gt; updateContextWithValue ctx value)
210+
<span class="hljs-keyword">in</span>
211+
<span class="hljs-type">List</span>.foldr (\node graph -&gt; <span class="hljs-type">Graph</span>.update node.id (graphUpdater node) graph)
212+
213+
214+
<span class="hljs-title">updateContextWithValue</span> nodeCtx value =
215+
<span class="hljs-keyword">let</span>
216+
node =
217+
nodeCtx.node
218+
<span class="hljs-keyword">in</span>
219+
{ nodeCtx | node = { node | label = value } }
220+
221+
222+
<span class="hljs-title">linkElement</span> : <span class="hljs-type">Graph</span> <span class="hljs-type">Entity</span> () -&gt; <span class="hljs-type">Edge</span> () -&gt; <span class="hljs-type">Svg</span> msg
223+
<span class="hljs-title">linkElement</span> graph edge =
224+
<span class="hljs-keyword">let</span>
225+
retrieveEntity =
226+
<span class="hljs-type">Maybe</span>.withDefault (<span class="hljs-type">Force</span>.entity <span class="hljs-number">0</span> (<span class="hljs-type">CustomNode</span> <span class="hljs-number">0</span> <span class="hljs-string">""</span>)) &lt;&lt; <span class="hljs-type">Maybe</span>.map (.node &gt;&gt; .label)
227+
228+
source =
229+
retrieveEntity &lt;| <span class="hljs-type">Graph</span>.get edge.from graph
230+
231+
target =
232+
retrieveEntity &lt;| <span class="hljs-type">Graph</span>.get edge.to graph
233+
<span class="hljs-keyword">in</span>
234+
line
235+
[ strokeWidth <span class="hljs-string">"1"</span>
236+
, stroke &lt;| colorToCssRgb &lt;| <span class="hljs-type">Scale</span>.convert colorScale source.x
237+
, x1 (toString source.x)
238+
, y1 (toString source.y)
239+
, x2 (toString target.x)
240+
, y2 (toString target.y)
241+
]
242+
[]
243+
244+
245+
<span class="hljs-title">hexagon</span> ( x, y ) size attrs =
246+
<span class="hljs-keyword">let</span>
247+
angle =
248+
<span class="hljs-number">2</span> * pi / <span class="hljs-number">6</span>
249+
250+
p =
251+
range <span class="hljs-number">0</span> <span class="hljs-number">6</span>
252+
|&gt; <span class="hljs-type">List</span>.map toFloat
253+
|&gt; <span class="hljs-type">List</span>.map (\a -&gt; ( x + (cos (a * angle)) * size, y + (sin (a * angle)) * size ))
254+
|&gt; <span class="hljs-type">List</span>.map (\( x, y ) -&gt; toString x ++ <span class="hljs-string">","</span> ++ toString y)
255+
|&gt; <span class="hljs-type">String</span>.join <span class="hljs-string">" "</span>
256+
|&gt; points
257+
<span class="hljs-keyword">in</span>
258+
polygon
259+
(p :: attrs)
260+
261+
262+
<span class="hljs-title">nodeSize</span> size node =
263+
hexagon ( node.x, node.y )
264+
size
265+
[ fill &lt;| colorToCssRgb &lt;| <span class="hljs-type">Scale</span>.convert colorScale node.x
266+
]
267+
[ <span class="hljs-type">Svg</span>.title [] [ text node.value.name ] ]
268+
269+
270+
<span class="hljs-title">nodeElement</span> node =
271+
<span class="hljs-keyword">if</span> node.label.value.rank &lt; <span class="hljs-number">5</span> <span class="hljs-keyword">then</span>
272+
nodeSize <span class="hljs-number">4</span> node.label
273+
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> node.label.value.rank &lt; <span class="hljs-number">9</span> <span class="hljs-keyword">then</span>
274+
nodeSize <span class="hljs-number">7</span> node.label
275+
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> node.label.value.rank % <span class="hljs-number">2</span> == <span class="hljs-number">0</span> <span class="hljs-keyword">then</span>
276+
g []
277+
[ nodeSize <span class="hljs-number">9</span> node.label
278+
, circle
279+
[ r <span class="hljs-string">"12"</span>
280+
, cx &lt;| toString node.label.x
281+
, cy &lt;| toString node.label.y
282+
, fill <span class="hljs-string">"none"</span>
283+
, stroke &lt;| colorToCssRgb &lt;| <span class="hljs-type">Scale</span>.convert colorScale node.label.x
284+
]
285+
[]
286+
]
287+
<span class="hljs-keyword">else</span>
288+
nodeSize <span class="hljs-number">10</span> node.label
289+
290+
291+
<span class="hljs-title">view</span> model =
292+
svg [ width (toString screenWidth ++ <span class="hljs-string">"px"</span>), height (toString screenHeight ++ <span class="hljs-string">"px"</span>) ]
293+
[ g [ class <span class="hljs-string">"links"</span> ] &lt;| <span class="hljs-type">List</span>.map (linkElement model) &lt;| <span class="hljs-type">Graph</span>.edges model
294+
, g [ class <span class="hljs-string">"nodes"</span> ] &lt;| <span class="hljs-type">List</span>.map nodeElement &lt;| <span class="hljs-type">Graph</span>.nodes model
295+
]
296+
297+
298+
<span class="hljs-title">main</span> =
299+
init |&gt; view
300+
</code></pre>
301+
302+
<hr />
303+
<h3></h3>
304+
<div class="description"></div>
305+
<pre><code class="elm"></code></pre>
306+
307+
</main>
308+
<aside>
309+
<h2>Examples</h2>
310+
<ul>
311+
312+
<li class="active">
313+
<a href="../Background">Background</a>
314+
</li>
315+
316+
<li >
317+
<a href="../Centroid">Centroid</a>
318+
</li>
319+
320+
<li >
321+
<a href="../CornerRadius">CornerRadius</a>
322+
</li>
323+
324+
<li >
325+
<a href="../Cross">Cross</a>
326+
</li>
327+
328+
<li >
329+
<a href="../Curves">Curves</a>
330+
</li>
331+
332+
<li >
333+
<a href="../CustomPieChart">CustomPieChart</a>
334+
</li>
335+
336+
<li >
337+
<a href="../ForceDirectedGraph">ForceDirectedGraph</a>
338+
</li>
339+
340+
<li >
341+
<a href="../LineChart">LineChart</a>
342+
</li>
343+
344+
<li >
345+
<a href="../PadAngle">PadAngle</a>
346+
</li>
347+
348+
<li >
349+
<a href="../Petals">Petals</a>
350+
</li>
351+
352+
<li >
353+
<a href="../PieChart">PieChart</a>
354+
</li>
355+
356+
</ul>
357+
</aside>
358+
</body>
359+
</html>

docs/Background/preview.png

16.8 KB
Loading

docs/Background/[email protected]

64.3 KB
Loading

docs/Background/[email protected]

95.8 KB
Loading

0 commit comments

Comments
 (0)