Skip to content

Instantly share code, notes, and snippets.

@sonbe
Last active September 26, 2017 03:00
Show Gist options
  • Save sonbe/9328a6a0fb81e4e31d13094040099133 to your computer and use it in GitHub Desktop.
Save sonbe/9328a6a0fb81e4e31d13094040099133 to your computer and use it in GitHub Desktop.
test_narrative_chart_03
license: mit
/*--------------------- Typography ----------------------------*/
@font-face {
font-family: 'aller-light';
src: url('public/fonts/aller-light.eot');
src: url('public/fonts/aller-light.eot?#iefix') format('embedded-opentype'),
url('public/fonts/aller-light.woff') format('woff'),
url('public/fonts/aller-light.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'aller-bold';
src: url('public/fonts/aller-bold.eot');
src: url('public/fonts/aller-bold.eot?#iefix') format('embedded-opentype'),
url('public/fonts/aller-bold.woff') format('woff'),
url('public/fonts/aller-bold.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'roboto-black';
src: url('public/fonts/roboto-black.eot');
src: url('public/fonts/roboto-black.eot?#iefix') format('embedded-opentype'),
url('public/fonts/roboto-black.woff') format('woff'),
url('public/fonts/roboto-black.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
/*--------------------- Layout ----------------------------*/
html { height: 100%; }
body {
font-family: "aller-light";
font-size: 14px;
line-height: 18px;
color: #30404f;
margin: 0; padding: 0;
height:100%;
}
#container { min-height: 100%; }
a {
color: #000;
}
b, strong {
font-weight: normal;
font-family: "aller-bold";
}
p {
margin: 15px 0 0px;
}
.annotation ul, .annotation ol {
margin: 25px 0;
}
.annotation ul li, .annotation ol li {
font-size: 14px;
line-height: 18px;
margin: 10px 0;
}
h1, h2, h3, h4, h5, h6 {
color: #112233;
line-height: 1em;
font-weight: normal;
font-family: "roboto-black";
text-transform: uppercase;
margin: 30px 0 15px 0;
}
h1 {
margin-top: 40px;
}
h2 {
font-size: 1.26em;
}
hr {
border: 0;
background: 1px #ddd;
height: 1px;
margin: 20px 0;
}
pre, tt, code {
font-size: 12px; line-height: 16px;
font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace;
margin: 0; padding: 0;
}
.annotation pre {
display: block;
margin: 0;
padding: 7px 10px;
background: #fcfcfc;
-moz-box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
-webkit-box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
overflow-x: auto;
}
.annotation pre code {
border: 0;
padding: 0;
background: transparent;
}
blockquote {
border-left: 5px solid #ccc;
margin: 0;
padding: 1px 0 1px 1em;
}
.sections blockquote p {
font-family: Menlo, Consolas, Monaco, monospace;
font-size: 12px; line-height: 16px;
color: #999;
margin: 10px 0 0;
white-space: pre-wrap;
}
ul.sections {
list-style: none;
padding:0 0 5px 0;;
margin:0;
}
/*
Force border-box so that % widths fit the parent
container without overlap because of margin/padding.
More Info : http://www.quirksmode.org/css/box.html
*/
ul.sections > li > div {
-moz-box-sizing: border-box; /* firefox */
-ms-box-sizing: border-box; /* ie */
-webkit-box-sizing: border-box; /* webkit */
-khtml-box-sizing: border-box; /* konqueror */
box-sizing: border-box; /* css3 */
}
/*---------------------- Jump Page -----------------------------*/
#jump_to, #jump_page {
margin: 0;
background: white;
-webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
-webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;
font: 16px Arial;
cursor: pointer;
text-align: right;
list-style: none;
}
#jump_to a {
text-decoration: none;
}
#jump_to a.large {
display: none;
}
#jump_to a.small {
font-size: 22px;
font-weight: bold;
color: #676767;
}
#jump_to, #jump_wrapper {
position: fixed;
right: 0; top: 0;
padding: 10px 15px;
margin:0;
}
#jump_wrapper {
display: none;
padding:0;
}
#jump_to:hover #jump_wrapper {
display: block;
}
#jump_page_wrapper{
position: fixed;
right: 0;
top: 0;
bottom: 0;
}
#jump_page {
padding: 5px 0 3px;
margin: 0 0 25px 25px;
max-height: 100%;
overflow: auto;
}
#jump_page .source {
display: block;
padding: 15px;
text-decoration: none;
border-top: 1px solid #eee;
}
#jump_page .source:hover {
background: #f5f5ff;
}
#jump_page .source:first-child {
}
/*---------------------- Low resolutions (> 320px) ---------------------*/
@media only screen and (min-width: 320px) {
.pilwrap { display: none; }
ul.sections > li > div {
display: block;
padding:5px 10px 0 10px;
}
ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol {
padding-left: 30px;
}
ul.sections > li > div.content {
overflow-x:auto;
-webkit-box-shadow: inset 0 0 5px #e5e5ee;
box-shadow: inset 0 0 5px #e5e5ee;
border: 1px solid #dedede;
margin:5px 10px 5px 10px;
padding-bottom: 5px;
}
ul.sections > li > div.annotation pre {
margin: 7px 0 7px;
padding-left: 15px;
}
ul.sections > li > div.annotation p tt, .annotation code {
background: #f8f8ff;
border: 1px solid #dedede;
font-size: 12px;
padding: 0 0.2em;
}
}
/*---------------------- (> 481px) ---------------------*/
@media only screen and (min-width: 481px) {
#container {
position: relative;
}
body {
background-color: #F5F5FF;
font-size: 15px;
line-height: 21px;
}
pre, tt, code {
line-height: 18px;
}
p, ul, ol {
margin: 0 0 15px;
}
#jump_to {
padding: 5px 10px;
}
#jump_wrapper {
padding: 0;
}
#jump_to, #jump_page {
font: 10px Arial;
text-transform: uppercase;
}
#jump_page .source {
padding: 5px 10px;
}
#jump_to a.large {
display: inline-block;
}
#jump_to a.small {
display: none;
}
#background {
position: absolute;
top: 0; bottom: 0;
width: 350px;
background: #fff;
border-right: 1px solid #e5e5ee;
z-index: -1;
}
ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol {
padding-left: 40px;
}
ul.sections > li {
white-space: nowrap;
}
ul.sections > li > div {
display: inline-block;
}
ul.sections > li > div.annotation {
max-width: 350px;
min-width: 350px;
min-height: 5px;
padding: 13px;
overflow-x: hidden;
white-space: normal;
vertical-align: top;
text-align: left;
}
ul.sections > li > div.annotation pre {
margin: 15px 0 15px;
padding-left: 15px;
}
ul.sections > li > div.content {
padding: 13px;
vertical-align: top;
border: none;
-webkit-box-shadow: none;
box-shadow: none;
}
.pilwrap {
position: relative;
display: inline;
}
.pilcrow {
font: 12px Arial;
text-decoration: none;
color: #454545;
position: absolute;
top: 3px; left: -20px;
padding: 1px 2px;
opacity: 0;
-webkit-transition: opacity 0.2s linear;
}
.for-h1 .pilcrow {
top: 47px;
}
.for-h2 .pilcrow, .for-h3 .pilcrow, .for-h4 .pilcrow {
top: 35px;
}
ul.sections > li > div.annotation:hover .pilcrow {
opacity: 1;
}
}
/*---------------------- (> 1025px) ---------------------*/
@media only screen and (min-width: 1025px) {
body {
font-size: 16px;
line-height: 24px;
}
#background {
width: 525px;
}
ul.sections > li > div.annotation {
max-width: 525px;
min-width: 525px;
padding: 10px 25px 1px 50px;
}
ul.sections > li > div.content {
padding: 9px 15px 16px 25px;
}
}
/*---------------------- Syntax Highlighting -----------------------------*/
td.linenos { background-color: #f0f0f0; padding-right: 10px; }
span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
/*
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
*/
pre code {
display: block; padding: 0.5em;
color: #000;
background: #f8f8ff
}
pre .hljs-comment,
pre .hljs-template_comment,
pre .hljs-diff .hljs-header,
pre .hljs-javadoc {
color: #408080;
font-style: italic
}
pre .hljs-keyword,
pre .hljs-assignment,
pre .hljs-literal,
pre .hljs-css .hljs-rule .hljs-keyword,
pre .hljs-winutils,
pre .hljs-javascript .hljs-title,
pre .hljs-lisp .hljs-title,
pre .hljs-subst {
color: #954121;
/*font-weight: bold*/
}
pre .hljs-number,
pre .hljs-hexcolor {
color: #40a070
}
pre .hljs-string,
pre .hljs-tag .hljs-value,
pre .hljs-phpdoc,
pre .hljs-tex .hljs-formula {
color: #219161;
}
pre .hljs-title,
pre .hljs-id {
color: #19469D;
}
pre .hljs-params {
color: #00F;
}
pre .hljs-javascript .hljs-title,
pre .hljs-lisp .hljs-title,
pre .hljs-subst {
font-weight: normal
}
pre .hljs-class .hljs-title,
pre .hljs-haskell .hljs-label,
pre .hljs-tex .hljs-command {
color: #458;
font-weight: bold
}
pre .hljs-tag,
pre .hljs-tag .hljs-title,
pre .hljs-rules .hljs-property,
pre .hljs-django .hljs-tag .hljs-keyword {
color: #000080;
font-weight: normal
}
pre .hljs-attribute,
pre .hljs-variable,
pre .hljs-instancevar,
pre .hljs-lisp .hljs-body {
color: #008080
}
pre .hljs-regexp {
color: #B68
}
pre .hljs-class {
color: #458;
font-weight: bold
}
pre .hljs-symbol,
pre .hljs-ruby .hljs-symbol .hljs-string,
pre .hljs-ruby .hljs-symbol .hljs-keyword,
pre .hljs-ruby .hljs-symbol .hljs-keymethods,
pre .hljs-lisp .hljs-keyword,
pre .hljs-tex .hljs-special,
pre .hljs-input_number {
color: #990073
}
pre .hljs-builtin,
pre .hljs-constructor,
pre .hljs-built_in,
pre .hljs-lisp .hljs-title {
color: #0086b3
}
pre .hljs-preprocessor,
pre .hljs-pi,
pre .hljs-doctype,
pre .hljs-shebang,
pre .hljs-cdata {
color: #999;
font-weight: bold
}
pre .hljs-deletion {
background: #fdd
}
pre .hljs-addition {
background: #dfd
}
pre .hljs-diff .hljs-change {
background: #0086b3
}
pre .hljs-chunk {
color: #aaa
}
pre .hljs-tex .hljs-formula {
opacity: 0.5;
}
<!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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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>&#39;horizontal&#39;</code> (default)
or <code>&#39;vertical&#39;</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">&#182;</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">&#182;</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>] &gt; 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>] &gt; 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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</a>
</div>
</div>
</li>
<li id="section-44">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-44">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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>] &gt;= <span class="hljs-number">1</span> &amp;&amp; counts[<span class="hljs-number">1</span>] &gt;= <span class="hljs-number">1</span>;
finished = finished &amp;&amp; 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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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>]) &amp;&amp;
(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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</a>
</div>
<p>Move x-axis position to the dedicated label space if it makes sense.
if (x-labelSize[0] &lt; 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">&#182;</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>] &lt; 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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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 &gt; <span class="hljs-number">0</span> &amp;&amp; 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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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 &gt; <span class="hljs-number">0</span> &amp;&amp; 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">&#182;</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">&#182;</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">&#182;</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">&#182;</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&lt;ii;i++) {
<span class="hljs-keyword">if</span> (introduction !== collidables[i] &amp;&amp; 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">&#182;</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">&#182;</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>] &lt;= 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>] &lt;= 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">&#182;</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>] &lt;= 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>] &lt;= 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">&#182;</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">&#182;</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">&#182;</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">&#182;</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&lt;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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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">&#182;</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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment