D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
sonbe
Full window
Github gist
test_narrative_chart_03
Built with
blockbuilder.org
<!DOCTYPE html> <html> <head> <title>Narrative Charts</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <ul class="sections"> <li id="section-1"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-1">¶</a> </div> <h1 id="narrative-charts">Narrative Charts</h1> </div> </li> <li id="section-2"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-2">¶</a> </div> <p><code>d3.layout.narrative()</code></p> <p>The constructor takes no arguements. All relevant object properties should be set using the setter functions.</p> </div> <div class="content"><div class='highlight'><pre>d3.layout.narrative = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{</pre></div></div> </li> <li id="section-3"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-3">¶</a> </div> <h2 id="import-jlouvian">Import jLouvian</h2> </div> </li> <li id="section-4"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-4">¶</a> </div> <p><a href="https://github.com/upphiminn/jLouvain">jLouvian</a> is a open source (MIT) javascript implementation of the Louvian method of <a href="https://www.wikiwand.com/en/Louvain_Modularity">community detection</a>.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-keyword">import</span> <span class="hljs-string">'vendor/jLouvian/jLouvian.js'</span>;</pre></div></div> </li> <li id="section-5"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-5">¶</a> </div> <p>Define all the variables.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-keyword">var</span> narrative, scenes, characters, introductions, links, size, orientation, pathSpace, scale, labelSize, labelPosition, groupMargin, scenePadding, groups;</pre></div></div> </li> <li id="section-6"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-6">¶</a> </div> <p>Set some defaults.</p> </div> <div class="content"><div class='highlight'><pre>size = [<span class="hljs-number">1</span>,<span class="hljs-number">1</span>]; scale = <span class="hljs-number">1</span>; pathSpace = <span class="hljs-number">10</span>; labelSize = [<span class="hljs-number">100</span>,<span class="hljs-number">15</span>]; labelPosition = <span class="hljs-string">'right'</span>; scenePadding = [<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>]; groupMargin = <span class="hljs-number">0</span>; orientation = <span class="hljs-string">'horizontal'</span>;</pre></div></div> </li> <li id="section-7"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-7">¶</a> </div> <h1 id="public-functions-the-api-">Public functions (the API)</h1> </div> </li> <li id="section-8"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-8">¶</a> </div> <p>The narrative object which is returned and exposes the public API.</p> </div> <div class="content"><div class='highlight'><pre>narrative = {};</pre></div></div> </li> <li id="section-9"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-9">¶</a> </div> <h2 id="scenes">Scenes</h2> </div> </li> <li id="section-10"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-10">¶</a> </div> <p><code>narrative.scenes([array])</code></p> <p>Set or get the scenes array. If an array is passed, sets the narrative’s scenes to the passed array, else returns the scenes array.</p> </div> <div class="content"><div class='highlight'><pre>narrative.scenes = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_</span>) </span>{ <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">arguments</span>.length) { <span class="hljs-keyword">return</span> scenes; } scenes = _; <span class="hljs-keyword">return</span> narrative; };</pre></div></div> </li> <li id="section-11"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-11">¶</a> </div> <h2 id="characters">Characters</h2> </div> </li> <li id="section-12"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-12">¶</a> </div> <p><code>narrative.characters([array])</code></p> <p>Set or get the characters array. If an array is passed, sets the narrative’s characters array, otherwise returns the characters array.</p> </div> <div class="content"><div class='highlight'><pre>narrative.characters = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_</span>) </span>{ <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">arguments</span>.length) { <span class="hljs-keyword">return</span> characters; } characters = _; <span class="hljs-keyword">return</span> narrative; };</pre></div></div> </li> <li id="section-13"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-13">¶</a> </div> <h2 id="size">Size</h2> </div> </li> <li id="section-14"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-14">¶</a> </div> <p><code>narrative.size([array])</code></p> <p>Set or get the size of the layout. A two element array <code>[width,height]</code>. Note that this is considered a guide for the layout algorithm. See <code>narrative.extent()</code> for getting the final size of the layout.</p> </div> <div class="content"><div class='highlight'><pre>narrative.size = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_</span>) </span>{ <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">arguments</span>.length) { <span class="hljs-keyword">return</span> size; } size = _; <span class="hljs-keyword">return</span> narrative; };</pre></div></div> </li> <li id="section-15"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-15">¶</a> </div> <h2 id="orientation">Orientation</h2> </div> </li> <li id="section-16"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-16">¶</a> </div> <p><code>narrative.orientation([orientation])</code></p> <p><em>Incomplete:</em> Only the default (horizontal) option is fully supported.</p> <p>Set the orientation to use for the layout. The choices are <code>'horizontal'</code> (default) or <code>'vertical'</code>. In a horizontal orientation ‘time’ runs from left to right and in vertical, top to bottom.</p> </div> <div class="content"><div class='highlight'><pre>narrative.orientation = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_</span>) </span>{ <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">arguments</span>.length) { <span class="hljs-keyword">return</span> orientation; } orientation = _; <span class="hljs-keyword">return</span> narrative; };</pre></div></div> </li> <li id="section-17"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-17">¶</a> </div> <h2 id="extent">Extent</h2> </div> </li> <li id="section-18"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-18">¶</a> </div> <p><code>narrative.extent()</code></p> <p>Get the extent of the space used by the layout. This is useful for adjusting the size of the containing element after the layout has been calculated.</p> <p>Despite being able to set the size (see <code>narrative.size()</code>), it’s not always possible to contain the chart in the available space. This function will provide a <code>[width,height]</code> array of the layout extent <em>after</em> the layout has run.</p> </div> <div class="content"><div class='highlight'><pre>narrative.extent = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{ <span class="hljs-keyword">return</span> scenes.concat(introductions).reduce(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">max, d</span>)</span>{ <span class="hljs-keyword">var</span> bounds = d.bounds(); <span class="hljs-keyword">if</span> (bounds[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>] > max[<span class="hljs-number">1</span>]) { max[<span class="hljs-number">1</span>] = bounds[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>]; } <span class="hljs-keyword">if</span> (bounds[<span class="hljs-number">1</span>][<span class="hljs-number">0</span>] > max[<span class="hljs-number">0</span>]) { max[<span class="hljs-number">0</span>] = bounds[<span class="hljs-number">1</span>][<span class="hljs-number">0</span>]; } <span class="hljs-keyword">return</span> max; }, [<span class="hljs-number">0</span>,<span class="hljs-number">0</span>]); };</pre></div></div> </li> <li id="section-19"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-19">¶</a> </div> <h2 id="path-space">Path space</h2> </div> </li> <li id="section-20"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-20">¶</a> </div> <p><code>narrative.pathSpace([number])</code></p> <p>Set or get the space available to each character’s path.</p> </div> <div class="content"><div class='highlight'><pre>narrative.pathSpace = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_</span>) </span>{ <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">arguments</span>.length) { <span class="hljs-keyword">return</span> pathSpace; } pathSpace = _; <span class="hljs-keyword">return</span> narrative; };</pre></div></div> </li> <li id="section-21"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-21">¶</a> </div> <h2 id="group-margin">Group margin</h2> </div> </li> <li id="section-22"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-22">¶</a> </div> <p><code>narrative.groupMargin([margin])</code></p> <p>The characters are divided into groups based on the strength of their relationships (i.e. co-appearances in scenes). These groups are then arranged in a way designed to reduce congestion in the centre of the chart. To give thelayout a more open feel, a group margin can be set.</p> </div> <div class="content"><div class='highlight'><pre>narrative.groupMargin = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_</span>) </span>{ <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">arguments</span>.length) { <span class="hljs-keyword">return</span> groupMargin; } groupMargin = _; <span class="hljs-keyword">return</span> narrative; };</pre></div></div> </li> <li id="section-23"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-23">¶</a> </div> <h2 id="scene-padding">Scene padding</h2> </div> </li> <li id="section-24"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-24">¶</a> </div> <p><code>narrative.scenePadding([array])</code></p> <p>By default scenes have a height equal to <code>character height × character count</code> and a width of zero. You may want to allow for extra space around scenes so collisions with labels can be avoided. To set a padding pass an array of values matching the CSS padding argument order <code>[top, right, bottom, left]</code>.</p> </div> <div class="content"><div class='highlight'><pre>narrative.scenePadding = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_</span>) </span>{ <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">arguments</span>.length) { <span class="hljs-keyword">return</span> scenePadding; } scenePadding = _; <span class="hljs-keyword">return</span> narrative; };</pre></div></div> </li> <li id="section-25"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-25">¶</a> </div> <h2 id="label-size">Label size</h2> </div> </li> <li id="section-26"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-26">¶</a> </div> <p><code>narrative.labelSize([array])</code></p> <p>Set or get the default space to allocate in the layout for character labels. Must be a two element array <code>[width,height]</code>. Label sizes specific to each character which will override these defaults can be set by defining <code>height</code> and <code>width</code> properties on individual character objects.</p> </div> <div class="content"><div class='highlight'><pre>narrative.labelSize = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_</span>) </span>{ <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">arguments</span>.length) { <span class="hljs-keyword">return</span> labelSize; } labelSize = _; <span class="hljs-keyword">return</span> narrative; };</pre></div></div> </li> <li id="section-27"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-27">¶</a> </div> <h2 id="label-position">Label position</h2> </div> </li> <li id="section-28"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-28">¶</a> </div> <p><code>narrative.labelPosition([string])</code></p> <p>Set or get the default label position for character labels. Valid options are <code>above</code>, <code>below</code>, <code>left</code>, <code>right</code>. This can be overridden by setting defining a <code>labelPosition</code> property on individual character objects.</p> </div> <div class="content"><div class='highlight'><pre>narrative.labelPosition = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_</span>) </span>{ <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">arguments</span>.length) { <span class="hljs-keyword">return</span> labelPosition; } labelPosition = _; <span class="hljs-keyword">return</span> narrative; };</pre></div></div> </li> <li id="section-29"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-29">¶</a> </div> <h2 id="links">Links</h2> </div> </li> <li id="section-30"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-30">¶</a> </div> <p><code>narrative.links()</code></p> <p>Returns an array of links. Each link is consecutive appearances for a given character. Links are an object with <code>source</code> and <code>target</code> properties which are both appearance objects.</p> </div> <div class="content"><div class='highlight'><pre>narrative.links = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-keyword">return</span> links; };</pre></div></div> </li> <li id="section-31"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-31">¶</a> </div> <h2 id="link">Link</h2> </div> </li> <li id="section-32"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-32">¶</a> </div> <p><code>narrative.link()</code></p> <p>Returns a function for generating path strings for links. Links are objects with <code>source</code> and <code>target</code> properties which each contain an <code>x</code> and <code>y</code> property. In the context of the narrative chart these are either character apperance or introduction nodes.</p> </div> <div class="content"><div class='highlight'><pre>narrative.link = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-keyword">var</span> curvature = <span class="hljs-number">0.5</span>;</pre></div></div> </li> <li id="section-33"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-33">¶</a> </div> <h3 id="link-path">Link path</h3> <p><code>link([object])</code></p> <p>This function should be used to set the <code>path</code> attribute of links when displaying the narrative chart. It accepts an object and returns a path string linking the two.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">link</span>(<span class="hljs-params">d</span>) </span>{ <span class="hljs-keyword">var</span> x0,x1,y0,y1,cx0,cy0,cx1,cy1,ci;</pre></div></div> </li> <li id="section-34"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-34">¶</a> </div> <p>Set path end positions.</p> </div> <div class="content"><div class='highlight'><pre> x0 = (d.source.scene) ? d.source.scene.x + d.source.x : d.source.x; y0 = (d.source.scene) ? d.source.scene.y + d.source.y : d.source.y; x1 = (d.target.scene) ? d.target.scene.x + d.target.x : d.target.x; y1 = (d.target.scene) ? d.target.scene.y + d.target.y : d.target.y;</pre></div></div> </li> <li id="section-35"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-35">¶</a> </div> <p>Set control points.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (orientation === <span class="hljs-string">'vertical'</span>) { ci = d3.interpolateNumber(y0, y1); cx0 = x0; cy0 = ci(curvature); cx1 = x1; cy1 = ci(<span class="hljs-number">1</span>-curvature); } <span class="hljs-keyword">else</span> { ci = d3.interpolateNumber(x0, x1); cx0 = ci(curvature); cy0 = y0; cx1 = ci(<span class="hljs-number">1</span>-curvature); cy1 = y1; } <span class="hljs-keyword">return</span> <span class="hljs-string">"M"</span> + x0 + <span class="hljs-string">","</span> + y0 + <span class="hljs-string">"C"</span> + cx0 + <span class="hljs-string">","</span> + cy0 + <span class="hljs-string">" "</span> + cx1 + <span class="hljs-string">","</span> + cy1 + <span class="hljs-string">" "</span> + x1 + <span class="hljs-string">","</span> + y1; }</pre></div></div> </li> <li id="section-36"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-36">¶</a> </div> <h3 id="curvature">Curvature</h3> <p><code>link.curvature([number])</code></p> <p>Set or get the curvature which should be used to generate links. Should be in the range zero to one.</p> </div> <div class="content"><div class='highlight'><pre> link.curvature = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_</span>) </span>{ <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">arguments</span>.length) { <span class="hljs-keyword">return</span> curvature; } curvature = _; <span class="hljs-keyword">return</span> link; }; <span class="hljs-keyword">return</span> link; };</pre></div></div> </li> <li id="section-37"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-37">¶</a> </div> <h2 id="introductions">Introductions</h2> </div> </li> <li id="section-38"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-38">¶</a> </div> <p><code>narrative.introductions()</code></p> <p>Get an array of character introductions for plotting on the graph. Introductions are nodes (usually with labels) displayed before the first scene in which each character appears.</p> </div> <div class="content"><div class='highlight'><pre>narrative.introductions = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-keyword">return</span> introductions; };</pre></div></div> </li> <li id="section-39"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-39">¶</a> </div> <h2 id="layout">Layout</h2> </div> </li> <li id="section-40"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-40">¶</a> </div> <p><code>narrative.layout()</code></p> <p>Compute the narrative layout. This should be called after all options and data have been set and before attempting to use the layout’s output for display purposes.</p> </div> <div class="content"><div class='highlight'><pre>narrative.layout = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ computeSceneCharacters(); computeCharacterGroups(); setSceneGroups(); computeGroupAppearances(); sortGroups(); computeGroupPositions(); computeCharacterGroupPositions(); sortGroupAppearances(); computeSceneTiming(); computeAppearancePositions(); computeScenePositions(); createIntroductionNodes(); computeIntroductionPositions(); createLinks(); <span class="hljs-keyword">return</span> narrative; };</pre></div></div> </li> <li id="section-41"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-41">¶</a> </div> <p>Return the public API.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-keyword">return</span> narrative;</pre></div></div> </li> <li id="section-42"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-42">¶</a> </div> <h1 id="private-functions">Private functions</h1> </div> </li> <li id="section-43"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-43">¶</a> </div> </div> </li> <li id="section-44"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-44">¶</a> </div> <h2 id="initial-data-wrangling">Initial data wrangling</h2> </div> </li> <li id="section-45"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-45">¶</a> </div> <p>Populate the scenes with characters from the characters array. This method also cleanses the data to exclude characters which appear only once and scenes with fewer than two characters.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">computeSceneCharacters</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-keyword">var</span> appearances, finished;</pre></div></div> </li> <li id="section-46"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-46">¶</a> </div> <p>Create a map of scenes to characters (i.e. appearances).</p> </div> <div class="content"><div class='highlight'><pre> appearances = []; scenes.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">scene</span>)</span>{ scene.characters.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">character</span>) </span>{</pre></div></div> </li> <li id="section-47"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-47">¶</a> </div> <p>If the character isn’t an object assume it’s an index from the characters array.</p> </div> <div class="content"><div class='highlight'><pre> character = (<span class="hljs-keyword">typeof</span> character === <span class="hljs-string">'object'</span>) ? character : characters[character];</pre></div></div> </li> <li id="section-48"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-48">¶</a> </div> <p>Note forced character positions and sizes.</p> </div> <div class="content"><div class='highlight'><pre> character._x = character.x || <span class="hljs-literal">false</span>; character._y = character.y || <span class="hljs-literal">false</span>; character._width = character.width || <span class="hljs-literal">false</span>; character._height = character.height || <span class="hljs-literal">false</span>;</pre></div></div> </li> <li id="section-49"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-49">¶</a> </div> <p>Add this appearance to the map.</p> </div> <div class="content"><div class='highlight'><pre> appearances.push({character: character, scene: scene});</pre></div></div> </li> <li id="section-50"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-50">¶</a> </div> <p>Setup some properties on the character and scene that we’ll need later.</p> </div> <div class="content"><div class='highlight'><pre> scene.appearances = []; scene.bounds = getSceneBounds; character.appearances = []; });</pre></div></div> </li> <li id="section-51"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-51">¶</a> </div> <p>note forces scene positions.</p> </div> <div class="content"><div class='highlight'><pre> scene._x = scene.x || <span class="hljs-literal">false</span>; scene._y = scene.y || <span class="hljs-literal">false</span>; });</pre></div></div> </li> <li id="section-52"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-52">¶</a> </div> <p>Recursively filter appearances so we ultimately only include characters with more than a single appearance and scenes with more than a single character.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">while</span>(!finished) { finished = <span class="hljs-literal">true</span>; appearances = appearances.filter(filterAppearances); }</pre></div></div> </li> <li id="section-53"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-53">¶</a> </div> <p>Filter appearances.</p> <p>TODO: this could probably be more efficient (maybe with an index <a href="https://gist.github.com/AshKyd/adc7fb024787bd543fc5">https://gist.github.com/AshKyd/adc7fb024787bd543fc5</a>)</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">filterAppearances</span>(<span class="hljs-params">appearance</span>)</span>{ <span class="hljs-keyword">var</span> counts, keep; counts = appearances.reduce(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">c, a</span>)</span>{ <span class="hljs-keyword">if</span> (appearance.character === a.character) { c[<span class="hljs-number">0</span>]++; } <span class="hljs-keyword">if</span> (appearance.scene === a.scene) { c[<span class="hljs-number">1</span>]++; } <span class="hljs-keyword">return</span> c; }, [<span class="hljs-number">0</span>,<span class="hljs-number">0</span>]); keep = counts[<span class="hljs-number">0</span>] >= <span class="hljs-number">1</span> && counts[<span class="hljs-number">1</span>] >= <span class="hljs-number">1</span>; finished = finished && keep; <span class="hljs-keyword">return</span> keep; }</pre></div></div> </li> <li id="section-54"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-54">¶</a> </div> <p>Re-construct <code>characters</code> and <code>scenes</code> arrays with filtered appearances.</p> </div> <div class="content"><div class='highlight'><pre> characters = []; scenes = []; appearances.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">appearance</span>)</span>{</pre></div></div> </li> <li id="section-55"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-55">¶</a> </div> <p>Cross reference scenes and characters based on appearances.</p> </div> <div class="content"><div class='highlight'><pre> appearance.scene.appearances.push(appearance); appearance.character.appearances.push(appearance); <span class="hljs-keyword">if</span> (characters.indexOf(appearance.character) === <span class="hljs-number">-1</span>) { characters.push(appearance.character); } <span class="hljs-keyword">if</span> (scenes.indexOf(appearance.scene) === <span class="hljs-number">-1</span>) { scenes.push(appearance.scene); } }); }</pre></div></div> </li> <li id="section-56"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-56">¶</a> </div> <h2 id="character-clustering">Character clustering</h2> </div> </li> <li id="section-57"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-57">¶</a> </div> <p>Cluster characters based on their co-occurence in scenes</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">computeCharacterGroups</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-keyword">var</span> nodes, edges, clusters, partitioner, groupsMap, initGroups;</pre></div></div> </li> <li id="section-58"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-58">¶</a> </div> <p>An array of character indexes.</p> </div> <div class="content"><div class='highlight'><pre> nodes = characters.map(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">d,i</span>)</span>{<span class="hljs-keyword">return</span> i;}); initGroups = characters.reduce(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">g,d,i</span>)</span>{ <span class="hljs-keyword">if</span> (d.initialgroup) { g[i] = +d.initialgroup; } <span class="hljs-keyword">return</span> g; },{});</pre></div></div> </li> <li id="section-59"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-59">¶</a> </div> <p>Calculate the edges based on a character’s involvement in scenes.</p> </div> <div class="content"><div class='highlight'><pre> edges = []; scenes.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">scene</span>)</span>{ edges = edges.concat(sceneEdges(scene.appearances)); });</pre></div></div> </li> <li id="section-60"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-60">¶</a> </div> <p>Consolidate edges into a unique set of relationships with a weighting based on how often they appear together.</p> </div> <div class="content"><div class='highlight'><pre> edges = edges.reduce(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">result, edge</span>) </span>{ <span class="hljs-keyword">var</span> resultEdge; resultEdge = result.filter(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">resultEdge</span>)</span>{ <span class="hljs-keyword">return</span> (resultEdge.target === edge[<span class="hljs-number">0</span>] || resultEdge.target === edge[<span class="hljs-number">1</span>]) && (resultEdge.source === edge[<span class="hljs-number">0</span>] || resultEdge.source === edge[<span class="hljs-number">1</span>]); })[<span class="hljs-number">0</span>] || {source: edge[<span class="hljs-number">0</span>], target: edge[<span class="hljs-number">1</span>], weight: <span class="hljs-number">0</span>}; resultEdge.weight++; <span class="hljs-keyword">if</span> (resultEdge.weight === <span class="hljs-number">1</span>) { result.push(resultEdge); } <span class="hljs-keyword">return</span> result; }, []);</pre></div></div> </li> <li id="section-61"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-61">¶</a> </div> <p>Generate the groups.</p> </div> <div class="content"><div class='highlight'><pre> partitioner = jLouvain().nodes(nodes).edges(edges); <span class="hljs-keyword">if</span> (initGroups) { partitioner.partition_init(initGroups); } clusters = partitioner();</pre></div></div> </li> <li id="section-62"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-62">¶</a> </div> <p>Put all characters in groups with bi-directional reference.</p> </div> <div class="content"><div class='highlight'><pre> groups = []; groupsMap = {}; characters.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">character, i</span>)</span>{ <span class="hljs-keyword">var</span> groupId, group; groupId = clusters[i]; group = groupsMap[groupId]; <span class="hljs-keyword">if</span> (!group) { group = {id: groupId, characters: []}; groups.push(group); groupsMap[groupId] = group; } group.characters.push(character); character.group = group; });</pre></div></div> </li> <li id="section-63"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-63">¶</a> </div> <p>Creates a single link between each pair of characters in a scene.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sceneEdges</span>(<span class="hljs-params">list</span>) </span>{ <span class="hljs-keyword">var</span> i, j, matrix; matrix = []; <span class="hljs-keyword">for</span> (i=list.length;i--;){ <span class="hljs-keyword">for</span> (j=i;j--;){ matrix.push([characters.indexOf(list[i].character),characters.indexOf(list[j].character)]); } } <span class="hljs-keyword">return</span> matrix; } }</pre></div></div> </li> <li id="section-64"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-64">¶</a> </div> <h2 id="group-scenes">Group scenes</h2> </div> </li> <li id="section-65"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-65">¶</a> </div> <p>Each scene is assigned to a group based on the median character group for characters appearing in that scene. <em>Note:</em> “median” here is a mistake, it should be mode.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setSceneGroups</span>(<span class="hljs-params"></span>) </span>{ scenes.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">scene</span>)</span>{ <span class="hljs-keyword">var</span> groupCounts, groupCountsMap, medianGroup; groupCounts = []; groupCountsMap = {}; scene.appearances.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">appearance</span>)</span>{ <span class="hljs-keyword">var</span> count, index; index = groups.indexOf(appearance.character.group); count = groupCountsMap[index]; <span class="hljs-keyword">if</span> (!count) { count = {groupIndex: index, count: <span class="hljs-number">0</span>}; groupCountsMap[index] = count; groupCounts.push(count); } count.count++; }); groupCounts.sort(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">a,b</span>)</span>{ <span class="hljs-keyword">return</span> a.count-b.count; }); medianGroup = groups[groupCounts.pop().groupIndex];</pre></div></div> </li> <li id="section-66"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-66">¶</a> </div> <p>While we’re here record how many scenes this group is the modal group for.</p> </div> <div class="content"><div class='highlight'><pre> medianGroup.medianCount = medianGroup.medianCount || <span class="hljs-number">0</span>; medianGroup.medianCount++; scene.group = medianGroup; }); }</pre></div></div> </li> <li id="section-67"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-67">¶</a> </div> <h2 id="group-appearances">Group appearances</h2> </div> </li> <li id="section-68"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-68">¶</a> </div> <p>Assign unique set of characters to each group based on appearances in scenes belonging to that group.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">computeGroupAppearances</span>(<span class="hljs-params"></span>) </span>{ scenes.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">scene</span>)</span>{ <span class="hljs-keyword">var</span> characters; characters = scene.appearances.map(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">a</span>)</span>{ <span class="hljs-keyword">return</span> a.character; }); scene.group.appearances = scene.group.appearances || []; scene.group.appearances = scene.group.appearances.concat(characters.filter(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">character</span>)</span>{ <span class="hljs-keyword">return</span> scene.group.appearances.indexOf(character) === <span class="hljs-number">-1</span>; })); }); }</pre></div></div> </li> <li id="section-69"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-69">¶</a> </div> <h2 id="sort-groups">Sort groups</h2> </div> </li> <li id="section-70"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-70">¶</a> </div> <p>Sort the array of groups so groups which are most often the median are at the extremes of the array. The centre most group should be the group which is least often the median group of a scene.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sortGroups</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-keyword">var</span> alt, sortedGroups, group, i;</pre></div></div> </li> <li id="section-71"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-71">¶</a> </div> <p>First sort by the group’s medianCount property (the number of times the group is the median group in a scene).</p> </div> <div class="content"><div class='highlight'><pre> groups.sort(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">a,b</span>)</span>{ <span class="hljs-keyword">return</span> b.medianCount-a.medianCount; });</pre></div></div> </li> <li id="section-72"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-72">¶</a> </div> <p>Specify order property and shuffle out groups into an ordered array.</p> </div> <div class="content"><div class='highlight'><pre> sortedGroups = []; i = <span class="hljs-number">0</span>; <span class="hljs-keyword">while</span> (groups.length) { group = (alt) ? groups.pop() : groups.shift(); group.order = i; i++; sortedGroups.push(group); alt = !alt; } groups = sortedGroups; }</pre></div></div> </li> <li id="section-73"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-73">¶</a> </div> <h2 id="group-positions">Group positions</h2> </div> </li> <li id="section-74"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-74">¶</a> </div> <p>Compute the actual min and max y-positions of each group.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">computeGroupPositions</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-keyword">var</span> max; max = <span class="hljs-number">0</span>; groups.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">group</span>)</span>{ group.min = max; group.max = max = characterGroupHeight(group.appearances.length) + group.min; max += groupMargin; }); }</pre></div></div> </li> <li id="section-75"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-75">¶</a> </div> <h2 id="character-group-positions">Character group positions</h2> </div> </li> <li id="section-76"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-76">¶</a> </div> <p>Compute the position of each character within its group.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">computeCharacterGroupPositions</span>(<span class="hljs-params"></span>) </span>{ characters.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">character</span>)</span>{ <span class="hljs-keyword">var</span> sum, count; sum = count = <span class="hljs-number">0</span>; character.appearances.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">appearance</span>) </span>{ count++; sum += groups.indexOf(appearance.scene.group); }); character.averageScenePosition = sum/count; }); groups.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">group</span>)</span>{ group.characters.sort(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">a,b</span>)</span>{ <span class="hljs-keyword">var</span> diff;</pre></div></div> </li> <li id="section-77"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-77">¶</a> </div> <p>Average scene position.</p> </div> <div class="content"><div class='highlight'><pre> diff = a.averageScenePosition - b.averageScenePosition; <span class="hljs-keyword">if</span> (diff !== <span class="hljs-number">0</span>) { <span class="hljs-keyword">return</span> diff; } <span class="hljs-keyword">return</span> characters.indexOf(a)-characters.indexOf(b); }); }); }</pre></div></div> </li> <li id="section-78"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-78">¶</a> </div> <h2 id="sort-group-appearances">Sort group appearances</h2> </div> </li> <li id="section-79"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-79">¶</a> </div> <p>Group appearances (<code>group.appearances</code>) is an array of all characters which appear in scenes assigned to this group.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sortGroupAppearances</span>(<span class="hljs-params"></span>) </span>{ groups.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">group</span>)</span>{ group.appearances.sort(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">a,b</span>)</span>{ <span class="hljs-keyword">var</span> diff;</pre></div></div> </li> <li id="section-80"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-80">¶</a> </div> <p>Try simple group order.</p> </div> <div class="content"><div class='highlight'><pre> diff = a.group.order-b.group.order; <span class="hljs-keyword">if</span> (diff !== <span class="hljs-number">0</span>) { <span class="hljs-keyword">return</span> diff; }</pre></div></div> </li> <li id="section-81"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-81">¶</a> </div> <p>Average scene position.</p> </div> <div class="content"><div class='highlight'><pre> diff = a.averageScenePosition - b.averageScenePosition; <span class="hljs-keyword">if</span> (diff !== <span class="hljs-number">0</span>) { <span class="hljs-keyword">return</span> diff; }</pre></div></div> </li> <li id="section-82"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-82">¶</a> </div> <p>Array position.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> characters.indexOf(a)-characters.indexOf(b); }); }); }</pre></div></div> </li> <li id="section-83"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-83">¶</a> </div> <h2 id="scene-timing">Scene timing</h2> </div> </li> <li id="section-84"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-84">¶</a> </div> <p>Compute the scene timing.</p> <p>TODO: support dates</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">computeSceneTiming</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-keyword">var</span> duration = <span class="hljs-number">1</span>; scenes.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">scene</span>)</span>{ scene.start = scene.start || duration; scene.duration = scene.duration || <span class="hljs-number">1</span>; duration += scene.duration; }); scale = ((orientation === <span class="hljs-string">'vertical'</span>) ? size[<span class="hljs-number">1</span>]-labelSize[<span class="hljs-number">1</span>] : size[<span class="hljs-number">0</span>]-labelSize[<span class="hljs-number">0</span>])/duration; }</pre></div></div> </li> <li id="section-85"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-85">¶</a> </div> <h2 id="character-positions">Character positions</h2> </div> </li> <li id="section-86"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-86">¶</a> </div> <p>Compute the position of characters within a scene.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">computeAppearancePositions</span>(<span class="hljs-params"></span>) </span>{ scenes.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">scene</span>)</span>{ scene.appearances.sort(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">a,b</span>)</span>{ <span class="hljs-keyword">var</span> diff;</pre></div></div> </li> <li id="section-87"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-87">¶</a> </div> <p>Try simple group order.</p> </div> <div class="content"><div class='highlight'><pre> diff = a.character.group.order-b.character.group.order; <span class="hljs-keyword">if</span> (diff !== <span class="hljs-number">0</span>) { <span class="hljs-keyword">return</span> diff; }</pre></div></div> </li> <li id="section-88"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-88">¶</a> </div> <p>For characters in the same group use average scene position.</p> </div> <div class="content"><div class='highlight'><pre> diff = a.character.averageScenePosition - b.character.averageScenePosition; <span class="hljs-keyword">if</span> (diff !== <span class="hljs-number">0</span>) { <span class="hljs-keyword">return</span> diff; }</pre></div></div> </li> <li id="section-89"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-89">¶</a> </div> <p>All else failing use main characters array order to keep things consistent.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">return</span> characters.indexOf(a.character)-characters.indexOf(b.character); }); scene.appearances.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">appearance,i</span>) </span>{ <span class="hljs-keyword">if</span> (orientation === <span class="hljs-string">'vertical'</span>) { appearance.y = scenePadding[<span class="hljs-number">0</span>]; appearance.x = characterPosition(i) + scenePadding[<span class="hljs-number">3</span>]; } <span class="hljs-keyword">else</span> { appearance.y = characterPosition(i) + scenePadding[<span class="hljs-number">0</span>]; appearance.x = scenePadding[<span class="hljs-number">3</span>]; } }); }); }</pre></div></div> </li> <li id="section-90"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-90">¶</a> </div> <h2 id="position-scenes">Position scenes</h2> </div> </li> <li id="section-91"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-91">¶</a> </div> <p>Compute the actual x and y positions for each scene.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">computeScenePositions</span>(<span class="hljs-params"></span>) </span>{ scenes.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">scene</span>) </span>{ <span class="hljs-keyword">var</span> sum, avg, appearances; scene.height = characterGroupHeight(scene.appearances.length) + scenePadding[<span class="hljs-number">0</span>] + scenePadding[<span class="hljs-number">2</span>]; scene.width = scenePadding[<span class="hljs-number">1</span>] + scenePadding[<span class="hljs-number">3</span>]; appearances = scene.appearances.filter(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">appearance</span>)</span>{ <span class="hljs-keyword">return</span> appearance.character.group !== scene.group; }); <span class="hljs-keyword">if</span> (!appearances.length) { appearances = scene.appearances; } sum = appearances.reduce(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">total, appearance</span>)</span>{ <span class="hljs-keyword">return</span> total += characterPosition(scene.group.appearances.indexOf(appearance.character)) + scene.group.min; }, <span class="hljs-number">0</span>); avg = sum/appearances.length; <span class="hljs-keyword">if</span> (orientation === <span class="hljs-string">'vertical'</span>) { scene.x = scene._x || <span class="hljs-built_in">Math</span>.max(<span class="hljs-number">0</span>, <span class="hljs-built_in">Math</span>.min(size[<span class="hljs-number">0</span>], avg - scene.width/<span class="hljs-number">2</span>)); scene.y = scene._y || <span class="hljs-built_in">Math</span>.max(<span class="hljs-number">0</span>, <span class="hljs-built_in">Math</span>.min(size[<span class="hljs-number">1</span>], scale * scene.start + labelSize[<span class="hljs-number">1</span>])); } <span class="hljs-keyword">else</span> { scene.x = scene._x || <span class="hljs-built_in">Math</span>.max(<span class="hljs-number">0</span>, <span class="hljs-built_in">Math</span>.min(size[<span class="hljs-number">0</span>], scale * scene.start + labelSize[<span class="hljs-number">0</span>])); scene.y = scene._y || <span class="hljs-built_in">Math</span>.max(<span class="hljs-number">0</span>, <span class="hljs-built_in">Math</span>.min(size[<span class="hljs-number">1</span>], avg - scene.height/<span class="hljs-number">2</span>)); } }); }</pre></div></div> </li> <li id="section-92"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-92">¶</a> </div> <h2 id="introduction-nodes">Introduction nodes</h2> </div> </li> <li id="section-93"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-93">¶</a> </div> <p>Create a collection of character ‘introduction’ nodes. These are nodes which are displayed before the first appearance of each character.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createIntroductionNodes</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-keyword">var</span> appearances; appearances = characters.map(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">character</span>)</span>{ <span class="hljs-keyword">return</span> character.appearances[<span class="hljs-number">0</span>]; }); introductions = []; appearances.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">appearance</span>)</span>{ <span class="hljs-keyword">var</span> introduction, x, y;</pre></div></div> </li> <li id="section-94"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-94">¶</a> </div> <p>Create the introduction object.</p> </div> <div class="content"><div class='highlight'><pre> introduction = { character: appearance.character, bounds: getLabelBounds };</pre></div></div> </li> <li id="section-95"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-95">¶</a> </div> <p>Set the default position.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (orientation === <span class="hljs-string">'vertical'</span>) { x = appearance.scene.x + appearance.x; y = appearance.scene.y - <span class="hljs-number">0.5</span> * scale;</pre></div></div> </li> <li id="section-96"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-96">¶</a> </div> <p>Move x-axis position to the dedicated label space if it makes sense. if (x-labelSize[0] < labelSize[0]) { x = labelSize[0]; }</p> </div> <div class="content"><div class='highlight'><pre> } <span class="hljs-keyword">else</span> { x = appearance.scene.x - <span class="hljs-number">0.5</span> * scale; y = appearance.scene.y + appearance.y;</pre></div></div> </li> <li id="section-97"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-97">¶</a> </div> <p>Move x-axis position to the dedicated label space if it makes sense.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (x-labelSize[<span class="hljs-number">0</span>] < labelSize[<span class="hljs-number">0</span>]) { x = labelSize[<span class="hljs-number">0</span>]; } } <span class="hljs-keyword">if</span> (orientation === <span class="hljs-string">'vertical'</span>) { introduction.x = appearance.character._x || <span class="hljs-built_in">Math</span>.max(<span class="hljs-number">0</span> + labelSize[<span class="hljs-number">0</span>]/<span class="hljs-number">2</span>, <span class="hljs-built_in">Math</span>.min(size[<span class="hljs-number">0</span>]-labelSize[<span class="hljs-number">0</span>]/<span class="hljs-number">2</span>, x)); introduction.y = appearance.character._y || <span class="hljs-built_in">Math</span>.max(<span class="hljs-number">0</span>, <span class="hljs-built_in">Math</span>.min(size[<span class="hljs-number">1</span>]-labelSize[<span class="hljs-number">1</span>], y)); } <span class="hljs-keyword">else</span> { introduction.x = appearance.character._x || <span class="hljs-built_in">Math</span>.max(<span class="hljs-number">0</span>, <span class="hljs-built_in">Math</span>.min(size[<span class="hljs-number">0</span>]-labelSize[<span class="hljs-number">0</span>], x)); introduction.y = appearance.character._y || <span class="hljs-built_in">Math</span>.max(<span class="hljs-number">0</span> + labelSize[<span class="hljs-number">1</span>]/<span class="hljs-number">2</span>, <span class="hljs-built_in">Math</span>.min(size[<span class="hljs-number">1</span>]-labelSize[<span class="hljs-number">1</span>]/<span class="hljs-number">2</span>, y)); } introduction.width = appearance.character._width || labelSize[<span class="hljs-number">0</span>]; introduction.height = appearance.character._height || labelSize[<span class="hljs-number">1</span>]; appearance.character.introduction = introduction; introductions.push(introduction); }); }</pre></div></div> </li> <li id="section-98"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-98">¶</a> </div> <h2 id="introduction-positions">Introduction positions</h2> </div> </li> <li id="section-99"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-99">¶</a> </div> <p>Layout the introduction nodes so that wherever possible they don’t overlap scenes or each other.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">computeIntroductionPositions</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-keyword">var</span> collidables, intros;</pre></div></div> </li> <li id="section-100"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-100">¶</a> </div> <p>Get a list of things introductions can collide with.</p> </div> <div class="content"><div class='highlight'><pre> collidables = introductions.concat(scenes);</pre></div></div> </li> <li id="section-101"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-101">¶</a> </div> <p>Use a copy of the introductions array so we can sort it without changing the main array’s order.</p> </div> <div class="content"><div class='highlight'><pre> intros = introductions.slice();</pre></div></div> </li> <li id="section-102"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-102">¶</a> </div> <p>Sort by y-axis position top to bottom.</p> </div> <div class="content"><div class='highlight'><pre> intros.sort(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">a,b</span>)</span>{ <span class="hljs-keyword">return</span> a.y-b.y; });</pre></div></div> </li> <li id="section-103"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-103">¶</a> </div> <p>Attempt to resolve collisions.</p> </div> <div class="content"><div class='highlight'><pre> intros.forEach((orientation === <span class="hljs-string">'vertical'</span>) ? resolveCollisionsVertical : resolveCollisionsHorizontal);</pre></div></div> </li> <li id="section-104"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-104">¶</a> </div> <p>Resolve collisions with horizontal layout.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">resolveCollisionsHorizontal</span>(<span class="hljs-params">introduction</span>)</span>{ <span class="hljs-keyword">var</span> moveOptions, collisionBounds, introBounds, move, _y, collisions, movable;</pre></div></div> </li> <li id="section-105"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-105">¶</a> </div> <p>Get the full list of items this introduction collides with</p> </div> <div class="content"><div class='highlight'><pre> collisions = collidesWith(introduction);</pre></div></div> </li> <li id="section-106"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-106">¶</a> </div> <p>No need to continue if there are no collisions.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (!collisions){ <span class="hljs-keyword">return</span>; }</pre></div></div> </li> <li id="section-107"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-107">¶</a> </div> <p>Move colliding items out of the way if possible.</p> </div> <div class="content"><div class='highlight'><pre> movable = collisions.filter(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">collision</span>)</span>{ <span class="hljs-keyword">return</span> (collision.character); }); movable.forEach(moveCollision);</pre></div></div> </li> <li id="section-108"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-108">¶</a> </div> <p>Now only consider immovables (i.e. scene nodes).</p> </div> <div class="content"><div class='highlight'><pre> collisions = collisions.filter(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">collision</span>)</span>{ <span class="hljs-keyword">return</span> !(collision.character); });</pre></div></div> </li> <li id="section-109"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-109">¶</a> </div> <p>No need to continue if there are no collisions.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (!collisions){ <span class="hljs-keyword">return</span>; }</pre></div></div> </li> <li id="section-110"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-110">¶</a> </div> <p>Get a bounding box for all remaining colliding nodes.</p> </div> <div class="content"><div class='highlight'><pre> collisionBounds = bBox(collisions); introBounds = introduction.bounds();</pre></div></div> </li> <li id="section-111"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-111">¶</a> </div> <p>Record the original y-axis position so we can revert if a move is a failure.</p> </div> <div class="content"><div class='highlight'><pre> _y = introduction.y;</pre></div></div> </li> <li id="section-112"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-112">¶</a> </div> <p>Calculate the two move options (up or down).</p> </div> <div class="content"><div class='highlight'><pre> moveOptions = [collisionBounds[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>] - introBounds[<span class="hljs-number">0</span>][<span class="hljs-number">1</span>], collisionBounds[<span class="hljs-number">0</span>][<span class="hljs-number">1</span>] - introBounds[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>]];</pre></div></div> </li> <li id="section-113"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-113">¶</a> </div> <p>Sort by absolute distance. Try the smallest move first.</p> </div> <div class="content"><div class='highlight'><pre> moveOptions.sort(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">a,b</span>)</span>{ <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.abs(a)-<span class="hljs-built_in">Math</span>.abs(b); });</pre></div></div> </li> <li id="section-114"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-114">¶</a> </div> <p>Try the move options in turn.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">while</span> (move = moveOptions.shift()) { introduction.y += move; collisions = collidesWith(introduction); <span class="hljs-keyword">if</span> (collisions) { <span class="hljs-keyword">if</span> (move > <span class="hljs-number">0</span> && collisions.every(isMovable)) { collisions.forEach(moveCollision); <span class="hljs-keyword">break</span>; } <span class="hljs-keyword">else</span> { introduction.y = _y; } } <span class="hljs-keyword">else</span> { <span class="hljs-keyword">break</span>; } }</pre></div></div> </li> <li id="section-115"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-115">¶</a> </div> <p>Move the colliding nodes.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">moveCollision</span>(<span class="hljs-params">collision</span>) </span>{ collision.y += introduction.bounds()[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>] - collision.bounds()[<span class="hljs-number">0</span>][<span class="hljs-number">1</span>]; } }</pre></div></div> </li> <li id="section-116"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-116">¶</a> </div> <p>Resolve collisions with vertical layout.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">resolveCollisionsVertical</span>(<span class="hljs-params">introduction</span>)</span>{ <span class="hljs-keyword">var</span> moveOptions, collisionBounds, introBounds, move, _y, collisions, movable;</pre></div></div> </li> <li id="section-117"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-117">¶</a> </div> <p>Get the full list of items this introduction collides with</p> </div> <div class="content"><div class='highlight'><pre> collisions = collidesWith(introduction);</pre></div></div> </li> <li id="section-118"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-118">¶</a> </div> <p>No need to continue if there are no collisions.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (!collisions){ <span class="hljs-keyword">return</span>; }</pre></div></div> </li> <li id="section-119"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-119">¶</a> </div> <p>Move colliding items out of the way if possible.</p> </div> <div class="content"><div class='highlight'><pre> movable = collisions.filter(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">collision</span>)</span>{ <span class="hljs-keyword">return</span> (collision.character); }); movable.forEach(moveCollision);</pre></div></div> </li> <li id="section-120"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-120">¶</a> </div> <p>Now only consider immovables (i.e. scene nodes).</p> </div> <div class="content"><div class='highlight'><pre> collisions = collisions.filter(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">collision</span>)</span>{ <span class="hljs-keyword">return</span> !(collision.character); });</pre></div></div> </li> <li id="section-121"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-121">¶</a> </div> <p>No need to continue if there are no collisions.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> (!collisions){ <span class="hljs-keyword">return</span>; }</pre></div></div> </li> <li id="section-122"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-122">¶</a> </div> <p>Get a bounding box for all remaining colliding nodes.</p> </div> <div class="content"><div class='highlight'><pre> collisionBounds = bBox(collisions); introBounds = introduction.bounds();</pre></div></div> </li> <li id="section-123"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-123">¶</a> </div> <p>Record the original y-axis position so we can revert if a move is a failure.</p> </div> <div class="content"><div class='highlight'><pre> _y = introduction.y;</pre></div></div> </li> <li id="section-124"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-124">¶</a> </div> <p>Calculate the two move options (up or down).</p> </div> <div class="content"><div class='highlight'><pre> moveOptions = [collisionBounds[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>] - introBounds[<span class="hljs-number">0</span>][<span class="hljs-number">1</span>], collisionBounds[<span class="hljs-number">0</span>][<span class="hljs-number">1</span>] - introBounds[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>]];</pre></div></div> </li> <li id="section-125"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-125">¶</a> </div> <p>Sort by absolute distance. Try the smallest move first.</p> </div> <div class="content"><div class='highlight'><pre> moveOptions.sort(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">a,b</span>)</span>{ <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.abs(a)-<span class="hljs-built_in">Math</span>.abs(b); });</pre></div></div> </li> <li id="section-126"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-126">¶</a> </div> <p>Try the move options in turn.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">while</span> (move = moveOptions.shift()) { introduction.y += move; collisions = collidesWith(introduction); <span class="hljs-keyword">if</span> (collisions) { <span class="hljs-keyword">if</span> (move > <span class="hljs-number">0</span> && collisions.every(isMovable)) { collisions.forEach(moveCollision); <span class="hljs-keyword">break</span>; } <span class="hljs-keyword">else</span> { introduction.y = _y; } } <span class="hljs-keyword">else</span> { <span class="hljs-keyword">break</span>; } }</pre></div></div> </li> <li id="section-127"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-127">¶</a> </div> <p>Move the colliding nodes.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">moveCollision</span>(<span class="hljs-params">collision</span>) </span>{ collision.y += introduction.bounds()[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>] - collision.bounds()[<span class="hljs-number">0</span>][<span class="hljs-number">1</span>]; } }</pre></div></div> </li> <li id="section-128"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-128">¶</a> </div> <p>Is the supplied node movable?</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">isMovable</span>(<span class="hljs-params">collision</span>) </span>{ <span class="hljs-keyword">return</span> (collision.character); }</pre></div></div> </li> <li id="section-129"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-129">¶</a> </div> <p>Create a bounding box around a collection of nodes.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">bBox</span>(<span class="hljs-params">arr</span>) </span>{ <span class="hljs-keyword">var</span> x0,x1,y0,y1; x0 = d3.min(arr, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">d</span>)</span>{ <span class="hljs-keyword">return</span> d.bounds()[<span class="hljs-number">0</span>][<span class="hljs-number">0</span>]; }); x1 = d3.max(arr, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">d</span>) </span>{ <span class="hljs-keyword">return</span> d.bounds()[<span class="hljs-number">1</span>][<span class="hljs-number">0</span>]; }); y0 = d3.min(arr, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">d</span>)</span>{ <span class="hljs-keyword">return</span> d.bounds()[<span class="hljs-number">0</span>][<span class="hljs-number">1</span>]; }); y1 = d3.max(arr, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">d</span>) </span>{ <span class="hljs-keyword">return</span> d.bounds()[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>]; }); <span class="hljs-keyword">return</span> [[x0,y0],[x1,y1]]; }</pre></div></div> </li> <li id="section-130"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-130">¶</a> </div> <p>Gets a list of all other nodes that this introduction collides with.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">collidesWith</span>(<span class="hljs-params">introduction</span>) </span>{ <span class="hljs-keyword">var</span> i, ii, collisions; collisions = []; <span class="hljs-keyword">for</span> (i=<span class="hljs-number">0</span>,ii=collidables.length;i<ii;i++) { <span class="hljs-keyword">if</span> (introduction !== collidables[i] && collides(introduction.bounds(), collidables[i].bounds())) { collisions.push(collidables[i]); } } <span class="hljs-keyword">return</span> (collisions.length) ? collisions : <span class="hljs-literal">false</span>; }</pre></div></div> </li> <li id="section-131"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-131">¶</a> </div> <p>Check for overlap between two bounding boxes.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">collides</span>(<span class="hljs-params">a,b</span>) </span>{ <span class="hljs-keyword">return</span> !(</pre></div></div> </li> <li id="section-132"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-132">¶</a> </div> <p>Verticals.</p> </div> <div class="content"><div class='highlight'><pre> a[<span class="hljs-number">1</span>][<span class="hljs-number">0</span>] <= b[<span class="hljs-number">0</span>][<span class="hljs-number">0</span>] || b[<span class="hljs-number">1</span>][<span class="hljs-number">0</span>] <= a[<span class="hljs-number">0</span>][<span class="hljs-number">0</span>] ||</pre></div></div> </li> <li id="section-133"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-133">¶</a> </div> <p>Horizontals.</p> </div> <div class="content"><div class='highlight'><pre> a[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>] <= b[<span class="hljs-number">0</span>][<span class="hljs-number">1</span>] || b[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>] <= a[<span class="hljs-number">0</span>][<span class="hljs-number">1</span>]); } }</pre></div></div> </li> <li id="section-134"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-134">¶</a> </div> <h2 id="links">Links</h2> </div> </li> <li id="section-135"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-135">¶</a> </div> <p>Create a collection of links between appearances.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createLinks</span>(<span class="hljs-params"></span>) </span>{ links = []; characters.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">character</span>)</span>{ <span class="hljs-keyword">var</span> i;</pre></div></div> </li> <li id="section-136"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-136">¶</a> </div> <p>Links to intro nodes.</p> </div> <div class="content"><div class='highlight'><pre> links.push({ character: character, source: character.introduction, target: character.appearances[<span class="hljs-number">0</span>] });</pre></div></div> </li> <li id="section-137"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-137">¶</a> </div> <p>Standard appearance links.</p> </div> <div class="content"><div class='highlight'><pre> <span class="hljs-keyword">for</span> (i=<span class="hljs-number">1</span>;i<character.appearances.length;i++) { links.push({ character: character, source: character.appearances[i<span class="hljs-number">-1</span>], target: character.appearances[i] }); } }); }</pre></div></div> </li> <li id="section-138"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-138">¶</a> </div> <h1 id="utility-functions">Utility functions</h1> </div> </li> <li id="section-139"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-139">¶</a> </div> <p>Get the actual y-axis position of a character with the given (zero based) index.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">characterPosition</span>(<span class="hljs-params">index</span>) </span>{ <span class="hljs-keyword">return</span> index * pathSpace + pathSpace / <span class="hljs-number">2</span>; }</pre></div></div> </li> <li id="section-140"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-140">¶</a> </div> <p>Get the actual height of a group based on a character count.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">characterGroupHeight</span>(<span class="hljs-params">count</span>) </span>{ <span class="hljs-keyword">return</span> characterPosition(count) - pathSpace/<span class="hljs-number">2</span>; }</pre></div></div> </li> <li id="section-141"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-141">¶</a> </div> <h2 id="scene-bounds">Scene bounds</h2> </div> </li> <li id="section-142"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-142">¶</a> </div> <p>This is attached to all scene objects as <code>scene.bound</code> and returns the bounds for the scene node.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getSceneBounds</span>(<span class="hljs-params"></span>)</span>{ <span class="hljs-keyword">return</span> [[<span class="hljs-keyword">this</span>.x,<span class="hljs-keyword">this</span>.y],[<span class="hljs-keyword">this</span>.x+<span class="hljs-keyword">this</span>.width,<span class="hljs-keyword">this</span>.y+<span class="hljs-keyword">this</span>.height]]; }</pre></div></div> </li> <li id="section-143"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-143">¶</a> </div> <h2 id="label-bounds">Label bounds</h2> </div> </li> <li id="section-144"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-144">¶</a> </div> <p>This is attached to all character objects as <code>character.bounds</code> and returns the bounds of the character’s introduction label.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getLabelBounds</span>(<span class="hljs-params"></span>)</span>{ <span class="hljs-keyword">switch</span>(labelPosition) { <span class="hljs-keyword">case</span>(<span class="hljs-string">'left'</span>): <span class="hljs-keyword">return</span> [[<span class="hljs-keyword">this</span>.x-<span class="hljs-keyword">this</span>.width,<span class="hljs-keyword">this</span>.y-<span class="hljs-keyword">this</span>.height/<span class="hljs-number">2</span>],[<span class="hljs-keyword">this</span>.x,<span class="hljs-keyword">this</span>.y+<span class="hljs-keyword">this</span>.height/<span class="hljs-number">2</span>]]; <span class="hljs-keyword">case</span>(<span class="hljs-string">'above'</span>): <span class="hljs-keyword">return</span> [[<span class="hljs-keyword">this</span>.x-<span class="hljs-keyword">this</span>.width/<span class="hljs-number">2</span>,<span class="hljs-keyword">this</span>.y-<span class="hljs-keyword">this</span>.height],[<span class="hljs-keyword">this</span>.x+<span class="hljs-keyword">this</span>.width/<span class="hljs-number">2</span>,<span class="hljs-keyword">this</span>.y]]; <span class="hljs-keyword">case</span>(<span class="hljs-string">'right'</span>): <span class="hljs-keyword">return</span> [[<span class="hljs-keyword">this</span>.x,<span class="hljs-keyword">this</span>.y-<span class="hljs-keyword">this</span>.height/<span class="hljs-number">2</span>],[<span class="hljs-keyword">this</span>.x+<span class="hljs-keyword">this</span>.width,<span class="hljs-keyword">this</span>.y+<span class="hljs-keyword">this</span>.height/<span class="hljs-number">2</span>]]; <span class="hljs-keyword">case</span>(<span class="hljs-string">'below'</span>): <span class="hljs-keyword">return</span> [[<span class="hljs-keyword">this</span>.x-<span class="hljs-keyword">this</span>.width/<span class="hljs-number">2</span>,<span class="hljs-keyword">this</span>.y],[<span class="hljs-keyword">this</span>.x+<span class="hljs-keyword">this</span>.width/<span class="hljs-number">2</span>,<span class="hljs-keyword">this</span>.y+<span class="hljs-keyword">this</span>.height]]; } } };</pre></div></div> </li> </ul> </div> </body> </html>