2017-08-04 04:50:37 +00:00
/ * S o r r y , t h i s c o d e i s p r e t t y d a m n u g l y . I s p e n d 8 h o u r s 5 d a y s a w e e k r e f a c t o r i n g c o d e , I j u s t d o n ' t f e e l l i k e m a k i n g
* it better right now . Maybe I ' ll have enough caffiene in my veins and nothing better to do sometime to put this all
* into ES2015 modules or something .
* /
// global vars
2017-08-01 03:59:24 +00:00
var renderer ;
var stage ;
var screenWidth ;
var screenHeight ;
var counter ;
var totalScreenPixels ;
var connectionDistance ;
var pointShiftDistance ;
var polygon ;
var startPoints ;
var polygonPoints ;
var lastLoop ;
var thisLoop ;
var fps ;
var fpsGraphic ;
var scrollDelta ;
var pointShiftBiasX ;
var pointShiftBiasY ;
2017-08-13 06:46:51 +00:00
var numPoints ;
2017-08-04 04:50:37 +00:00
// global non-configurable vars (modifying these might break stuff)
var click = null ;
var hover = null ;
var lastHover = null ;
var clickEnd = false ;
2017-08-10 05:07:09 +00:00
var sprites = [ ] ;
2017-08-13 05:15:24 +00:00
var reset = false ;
2017-08-04 04:50:37 +00:00
// global configurable vars
var resolution = 1 ; // scaling for PIXI renderer
var debug = false ; // toggles drawing extra indicators for debugging
var fpsEnabled = debug ; // toggles the FPS counter
var cycleDuration = 60 ; // length of a point's "cycle": number of frames it takes for it to travel to its chosen destination
var connectionLimit = 10 ; // maximum number of lines drawn from one point to others within connection distance
// colorShiftAmt = 80; // disabled for now
var disconnectedColorShiftAmt = 10 ; // when a point is alone (not connected), shift RGB values by this amount every tick
var allTweeningFns = [ // array of all possible tweening functions, these are defined below
2017-08-03 04:17:17 +00:00
linearTweening ,
easeInSine ,
easeOutSine ,
easeInOutSine ,
easeInQuad ,
easeOutQuad ,
easeInOutQuad ,
easeInCubic ,
easeOutCubic ,
easeInOutCubic ,
easeInExpo ,
easeOutExpo ,
easeInOutExpo ,
easeInCirc ,
easeOutCirc ,
easeInOutCirc ,
easeOutBounce ,
easeInBounce ,
easeInOutBounce ,
easeInElastic ,
easeOutElastic ,
easeInOutElastic ,
easeInBack ,
easeOutBack ,
easeInOutBack
] ;
2017-08-04 04:50:37 +00:00
// sets of tweening functions that I think look good with points randomly choose from them
var tweeningSets = { // numbers refer to indicies into the allTweeningsFns array above
2017-08-03 04:17:17 +00:00
linear : [ 0 ] ,
meandering : [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] ,
snappy : [ 10 , 11 , 12 , 13 , 14 , 15 ] ,
bouncy : [ 16 , 17 , 18 ] ,
elastic : [ 19 , 20 , 21 ] ,
2017-08-11 04:20:02 +00:00
back : [ 24 ]
2017-08-03 04:17:17 +00:00
} ;
2017-08-19 19:19:54 +00:00
var tweeningFns = tweeningSets . back ; // the actual set of tweening functions points will randomly choose from
2017-08-04 04:50:37 +00:00
// click effect related config vars
var clickPullRateStart = 0.01 ; // initial value for the ratio of a point's distance from the click position to travel in one cycle
var clickPullRateInc = 0.005 ; // amount to increase clickPullRate every tick that a click is held
var clickPullRateMax = 0.5 ; // maximum value of clickPullRate
2017-08-01 05:34:53 +00:00
var clickPullRate = clickPullRateStart ;
2017-08-11 04:45:47 +00:00
var clickMaxDistStart = 50 ; // initial value for the effect radius of a click: points this distance from click position will be pulled (overridden if small screen size)
2017-08-04 04:50:37 +00:00
var clickMaxDistInc = 2 ; // amount to increase clickMaxDist every tick that a click is held
var clickMaxDistMax = 5000 ; // maximum value of clickMaxDist
2017-08-01 05:34:53 +00:00
var clickMaxDist = clickMaxDistStart ;
2017-08-04 04:50:37 +00:00
var clickInertiaStart = - 0.7 ; // initial value of the ratio of point's origin distance from the click position to be added to point's new target
2017-08-03 04:17:17 +00:00
var clickInertia = clickInertiaStart ;
2017-08-04 04:50:37 +00:00
var clickTweeningFnStart = null ; // initial value of the specific tweening function to assign to points in effect radius (null will not change functions)
2017-08-03 04:17:17 +00:00
var clickTweeningFn = clickTweeningFnStart ;
2017-08-04 04:50:37 +00:00
var clickColorShiftAmt = disconnectedColorShiftAmt * 3 ; // amount of RGB color value to shift for each point in effect radius
2017-08-04 15:30:12 +00:00
var clickPullRateEnd = - 0.5 ; // value of clickPullRate during tick after end of click (for "rebound" effect)
2017-08-04 04:50:37 +00:00
var clickInertiaEnd = 0.3 ; // value of clickInertia during tick after end of click
var clickTweeningFnEnd = 12 ; // value of clickTweeningFn during tick after end of click (number refers to index into allTweeningsFns)
// hover effect related config vars
var hoverPushRate = - 0.05 ; // ratio of a point's distance from the hover position to travel in one cycle
var hoverInertia = 0.8 ; // ratio of a point's origin distance from the click position to be added to point's new target
2017-08-11 04:45:47 +00:00
var hoverMaxDistStart = 75 ; // initial value for the effect radius of a hover: points this distance from hover position will be pushed (overridden if small screen size)
2017-08-04 04:50:37 +00:00
var hoverMaxDistMax = 1000 ; // maximum value of hoverMaxDist
2017-08-03 04:17:17 +00:00
var hoverMaxDist = hoverMaxDistStart ;
2017-08-04 04:50:37 +00:00
var hoverTweeningFn = 5 ; // specific tweening function to assign to points in effect radius
2017-08-11 04:14:33 +00:00
var zRange = 50 ; // maximum value for the range of possible z coords for a point
2017-08-10 05:07:09 +00:00
var nodeImg = 'img/node.png' ; // image file location for representing every point
2017-08-11 04:14:33 +00:00
var nodeImgRes = 100 ; // resolution of nodeImg file in pixels (aspect ratio should be square)
2017-08-13 07:20:53 +00:00
// var minNodeDiameter = 3; // minimum pixel size of point on canvas when z coord is 0, maximum is this value plus zRange
var nodeSize = 3 ; // with z coord ignored, this value is used for scaling the drawing for each node
2017-08-11 04:14:33 +00:00
var drawNodes = true ; // whether to display circles at each point's current position
var drawLines = true ; // whether to display lines connecting points if they are in connection distance
2017-08-13 04:24:56 +00:00
var lineSize = 1 ; // thickness in pixels of drawn lines between points
2017-05-08 00:44:46 +00:00
2017-08-04 04:50:37 +00:00
/* TWEENING FUNCTIONS */
// These are modified versions of the jquery easing functions:
// https://github.com/danro/jquery-easing/blob/master/jquery.easing.js
// See license in LICENSE-3RD-PARTY.txt
2017-05-08 00:44:46 +00:00
2017-08-03 04:17:17 +00:00
/* eslint-disable no-unused-vars */
2017-05-08 00:44:46 +00:00
function linearTweening ( t , b , c , d ) {
// t = current time
// b = start value
// c = change in value
// d = duration
2017-08-01 03:59:24 +00:00
return ( ( c * t ) / d ) + b ;
2017-05-08 00:44:46 +00:00
}
function easeOutBounce ( t , b , c , d ) {
2017-08-01 03:59:24 +00:00
if ( ( t /= d ) < ( 1 / 2.75 ) ) {
return c * ( 7.5625 * t * t ) + b ;
} else if ( t < ( 2 / 2.75 ) ) {
return c * ( 7.5625 * ( t -= ( 1.5 / 2.75 ) ) * t + 0.75 ) + b ;
} else if ( t < ( 2.5 / 2.75 ) ) {
return c * ( 7.5625 * ( t -= ( 2.25 / 2.75 ) ) * t + 0.9375 ) + b ;
} else {
return c * ( 7.5625 * ( t -= ( 2.625 / 2.75 ) ) * t + 0.984375 ) + b ;
}
2017-05-08 00:44:46 +00:00
}
2017-08-03 04:17:17 +00:00
function easeInBounce ( t , b , c , d ) {
return c - easeOutBounce ( d - t , 0 , c , d ) + b ;
}
function easeInOutBounce ( t , b , c , d ) {
if ( t < d / 2 ) return easeInBounce ( t * 2 , 0 , c , d ) * 0.5 + b ;
return easeOutBounce ( t * 2 - d , 0 , c , d ) * 0.5 + c * 0.5 + b ;
}
2017-05-08 00:44:46 +00:00
function easeInSine ( t , b , c , d ) {
2017-08-01 03:59:24 +00:00
return - c * Math . cos ( t / d * ( Math . PI / 2 ) ) + c + b ;
2017-05-08 00:44:46 +00:00
}
function easeOutSine ( t , b , c , d ) {
2017-08-01 03:59:24 +00:00
return c * Math . sin ( t / d * ( Math . PI / 2 ) ) + b ;
2017-05-08 00:44:46 +00:00
}
function easeInOutSine ( t , b , c , d ) {
2017-08-01 03:59:24 +00:00
return - c / 2 * ( Math . cos ( Math . PI * t / d ) - 1 ) + b ;
2017-05-08 00:44:46 +00:00
}
function easeInQuad ( t , b , c , d ) {
2017-08-01 03:59:24 +00:00
return c * ( t /= d ) * t + b ;
2017-05-08 00:44:46 +00:00
}
function easeOutQuad ( t , b , c , d ) {
2017-08-01 03:59:24 +00:00
return - c * ( t /= d ) * ( t - 2 ) + b ;
2017-05-08 00:44:46 +00:00
}
function easeInOutQuad ( t , b , c , d ) {
2017-08-01 03:59:24 +00:00
if ( ( t /= d / 2 ) < 1 ) return c / 2 * t * t + b ;
return - c / 2 * ( ( -- t ) * ( t - 2 ) - 1 ) + b ;
2017-05-08 00:44:46 +00:00
}
function easeInCubic ( t , b , c , d ) {
2017-08-01 03:59:24 +00:00
return c * ( t /= d ) * t * t + b ;
2017-05-08 00:44:46 +00:00
}
function easeOutCubic ( t , b , c , d ) {
2017-08-01 03:59:24 +00:00
return c * ( ( t = t / d - 1 ) * t * t + 1 ) + b ;
2017-05-08 00:44:46 +00:00
}
function easeInOutCubic ( t , b , c , d ) {
2017-08-01 03:59:24 +00:00
if ( ( t /= d / 2 ) < 1 ) return c / 2 * t * t * t + b ;
return c / 2 * ( ( t -= 2 ) * t * t + 2 ) + b ;
2017-05-08 00:44:46 +00:00
}
function easeInExpo ( t , b , c , d ) {
2017-08-01 03:59:24 +00:00
return ( t === 0 ) ? b : c * Math . pow ( 2 , 10 * ( t / d - 1 ) ) + b ;
2017-05-08 00:44:46 +00:00
}
function easeOutExpo ( t , b , c , d ) {
2017-08-01 03:59:24 +00:00
return ( t === d ) ? b + c : c * ( - Math . pow ( 2 , - 10 * t / d ) + 1 ) + b ;
2017-05-08 00:44:46 +00:00
}
function easeInOutExpo ( t , b , c , d ) {
2017-08-01 03:59:24 +00:00
if ( t === 0 ) return b ;
if ( t === d ) return b + c ;
if ( ( t /= d / 2 ) < 1 ) return c / 2 * Math . pow ( 2 , 10 * ( t - 1 ) ) + b ;
return c / 2 * ( - Math . pow ( 2 , - 10 * -- t ) + 2 ) + b ;
2017-05-08 00:44:46 +00:00
}
function easeInElastic ( t , b , c , d ) {
2017-08-01 03:59:24 +00:00
var s = 1.70158 ; var p = 0 ; var a = c ;
if ( t === 0 ) return b ; if ( ( t /= d ) === 1 ) return b + c ; if ( ! p ) p = d * 0.3 ;
if ( a < Math . abs ( c ) ) { a = c ; s = p / 4 ; } else s = p / ( 2 * Math . PI ) * Math . asin ( c / a ) ;
return - ( a * Math . pow ( 2 , 10 * ( t -= 1 ) ) * Math . sin ( ( t * d - s ) * ( 2 * Math . PI ) / p ) ) + b ;
2017-05-08 00:44:46 +00:00
}
function easeOutElastic ( t , b , c , d ) {
2017-08-01 03:59:24 +00:00
var s = 1.70158 ; var p = 0 ; var a = c ;
if ( t === 0 ) return b ; if ( ( t /= d ) === 1 ) return b + c ; if ( ! p ) p = d * 0.3 ;
if ( a < Math . abs ( c ) ) { a = c ; s = p / 4 ; } else s = p / ( 2 * Math . PI ) * Math . asin ( c / a ) ;
return a * Math . pow ( 2 , - 10 * t ) * Math . sin ( ( t * d - s ) * ( 2 * Math . PI ) / p ) + c + b ;
2017-05-08 00:44:46 +00:00
}
function easeInOutElastic ( t , b , c , d ) {
2017-08-01 03:59:24 +00:00
var s = 1.70158 ; var p = 0 ; var a = c ;
if ( t === 0 ) return b ; if ( ( t /= d / 2 ) === 2 ) return b + c ; if ( ! p ) p = d * ( 0.3 * 1.5 ) ;
if ( a < Math . abs ( c ) ) { a = c ; s = p / 4 ; } else s = p / ( 2 * Math . PI ) * Math . asin ( c / a ) ;
if ( t < 1 ) return - 0.5 * ( a * Math . pow ( 2 , 10 * ( t -= 1 ) ) * Math . sin ( ( t * d - s ) * ( 2 * Math . PI ) / p ) ) + b ;
return a * Math . pow ( 2 , - 10 * ( t -= 1 ) ) * Math . sin ( ( t * d - s ) * ( 2 * Math . PI ) / p ) * 0.5 + c + b ;
2017-05-08 00:44:46 +00:00
}
2017-08-03 04:17:17 +00:00
function easeInBack ( t , b , c , d , s ) {
if ( s === undefined ) s = 1.70158 ;
return c * ( t /= d ) * t * ( ( s + 1 ) * t - s ) + b ;
}
function easeOutBack ( t , b , c , d , s ) {
if ( s === undefined ) s = 1.70158 ;
return c * ( ( t = t / d - 1 ) * t * ( ( s + 1 ) * t + s ) + 1 ) + b ;
}
function easeInOutBack ( t , b , c , d , s ) {
if ( s === undefined ) s = 1.70158 ;
if ( ( t /= d / 2 ) < 1 ) return c / 2 * ( t * t * ( ( ( s *= ( 1.525 ) ) + 1 ) * t - s ) ) + b ;
return c / 2 * ( ( t -= 2 ) * t * ( ( ( s *= ( 1.525 ) ) + 1 ) * t + s ) + 2 ) + b ;
}
2017-05-08 00:44:46 +00:00
function easeInCirc ( t , b , c , d ) {
2017-08-01 03:59:24 +00:00
return - c * ( Math . sqrt ( 1 - ( t /= d ) * t ) - 1 ) + b ;
2017-05-08 00:44:46 +00:00
}
function easeOutCirc ( t , b , c , d ) {
2017-08-01 03:59:24 +00:00
return c * Math . sqrt ( 1 - ( t = t / d - 1 ) * t ) + b ;
2017-05-08 00:44:46 +00:00
}
function easeInOutCirc ( t , b , c , d ) {
2017-08-01 03:59:24 +00:00
if ( ( t /= d / 2 ) < 1 ) return - c / 2 * ( Math . sqrt ( 1 - t * t ) - 1 ) + b ;
return c / 2 * ( Math . sqrt ( 1 - ( t -= 2 ) * t ) + 1 ) + b ;
2017-05-08 00:44:46 +00:00
}
2017-08-01 03:59:24 +00:00
/* eslint-enable no-unused-vars */
2017-05-08 00:44:46 +00:00
2017-08-13 04:56:47 +00:00
/* TOGGLE FUNCTIONS */
2017-08-04 04:50:37 +00:00
2017-08-12 22:50:51 +00:00
function toggleHelp ( ) {
var help , controls ;
help = document . getElementById ( 'help' ) ;
controls = document . getElementById ( 'controls' ) ;
if ( help . style . display === 'none' ) {
help . style . display = 'block' ;
// hide controls if open (only want one panel open at a time)
if ( controls . style . display === 'block' ) {
controls . style . display = 'none' ;
}
} else {
help . style . display = 'none' ;
}
}
function toggleControls ( ) {
var help , controls ;
help = document . getElementById ( 'help' ) ;
controls = document . getElementById ( 'controls' ) ;
if ( controls . style . display === 'none' ) {
controls . style . display = 'block' ;
// hide help if open (only want one panel open at a time)
if ( help . style . display === 'block' ) {
help . style . display = 'none' ;
}
} else {
controls . style . display = 'none' ;
}
}
2017-08-13 03:15:24 +00:00
function toggleFPS ( ) {
2017-08-13 04:24:56 +00:00
var fpsCheckbox = document . getElementsByName ( 'fpsCounterToggle' ) [ 0 ] ;
2017-08-13 03:15:24 +00:00
if ( fpsEnabled ) {
stage . removeChild ( fpsGraphic ) ;
fpsEnabled = false ;
} else {
stage . addChild ( fpsGraphic ) ;
fpsEnabled = true ;
lastLoop = new Date ( ) ;
}
fpsCheckbox . checked = fpsEnabled ;
}
function toggleDebug ( ) {
2017-08-13 04:24:56 +00:00
var fpsCheckbox = document . getElementsByName ( 'fpsCounterToggle' ) [ 0 ] ;
var debugCheckbox = document . getElementsByName ( 'debugToggle' ) [ 0 ] ;
2017-08-13 03:15:24 +00:00
if ( debug ) {
if ( fpsEnabled ) {
stage . removeChild ( fpsGraphic ) ;
}
debug = false ;
fpsEnabled = debug ;
} else {
if ( ! fpsEnabled ) {
stage . addChild ( fpsGraphic ) ;
}
debug = true ;
fpsEnabled = debug ;
lastLoop = new Date ( ) ;
}
fpsCheckbox . checked = fpsEnabled ;
debugCheckbox . checked = debug ;
}
2017-08-13 04:24:56 +00:00
function toggleNodes ( ) {
nodesCheckbox = document . getElementsByName ( 'nodesToggle' ) [ 0 ] ;
if ( drawNodes ) {
for ( i = 0 ; i < sprites . length ; i ++ ) {
sprites [ i ] . visible = false ;
}
drawNodes = false ;
} else {
for ( i = 0 ; i < sprites . length ; i ++ ) {
sprites [ i ] . visible = true ;
}
drawNodes = true ;
}
nodesCheckbox . checked = drawNodes ;
}
function toggleLines ( ) {
linesCheckbox = document . getElementsByName ( 'linesToggle' ) [ 0 ] ;
drawLines = ! drawLines ;
linesCheckbox . checked = drawLines ;
}
2017-08-13 04:56:47 +00:00
/* UTILITY FUNCTIONS */
2017-08-04 04:50:37 +00:00
function randomInt ( min , max ) {
// inclusive of min and max
return Math . floor ( Math . random ( ) * ( max - min + 1 ) ) + min ;
}
2017-05-08 00:44:46 +00:00
// from: http://stackoverflow.com/a/5624139
// modified to return integer literal
function rgbToHex ( color ) {
2017-08-01 03:59:24 +00:00
return parseInt ( ( ( 1 << 24 ) + ( color . r << 16 ) + ( color . g << 8 ) + color . b ) . toString ( 16 ) . slice ( 1 ) , 16 ) ;
2017-05-08 00:44:46 +00:00
}
/* Choose a random RGB color */
function randomColor ( ) {
2017-08-01 03:59:24 +00:00
return {
r : Math . floor ( Math . random ( ) * ( 255 + 1 ) ) ,
g : Math . floor ( Math . random ( ) * ( 255 + 1 ) ) ,
b : Math . floor ( Math . random ( ) * ( 255 + 1 ) )
} ;
2017-05-08 00:44:46 +00:00
}
/* Find the average of two RGB colors and return one RGB of that color */
function averageColor ( color1 , color2 ) {
2017-08-01 03:59:24 +00:00
return {
r : Math . round ( ( color1 . r + color2 . r ) / 2 ) ,
g : Math . round ( ( color1 . g + color2 . g ) / 2 ) ,
b : Math . round ( ( color1 . b + color2 . b ) / 2 )
} ;
2017-05-08 00:44:46 +00:00
}
/ *
* Find the average of two RGB colors where color1 is weighted against color2 by the given weight .
* weight is a decimal between 0 and 1.
* /
function weightedAverageColor ( color1 , color2 , weight ) {
2017-08-01 03:59:24 +00:00
return {
r : Math . round ( ( ( color1 . r * ( 2 * weight ) ) + ( color2 . r * ( 2 * ( 1 - weight ) ) ) ) / 2 ) ,
g : Math . round ( ( ( color1 . g * ( 2 * weight ) ) + ( color2 . g * ( 2 * ( 1 - weight ) ) ) ) / 2 ) ,
b : Math . round ( ( ( color1 . b * ( 2 * weight ) ) + ( color2 . b * ( 2 * ( 1 - weight ) ) ) ) / 2 )
} ;
2017-05-08 00:44:46 +00:00
}
/* Darken the color by a factor of 0 to 1, where 1 is black and 0 is white */
function shadeColor ( color , shadeFactor ) {
2017-08-01 03:59:24 +00:00
return {
r : Math . round ( color . r * ( 1 - shadeFactor ) ) ,
g : Math . round ( color . g * ( 1 - shadeFactor ) ) ,
b : Math . round ( color . b * ( 1 - shadeFactor ) )
} ;
2017-05-08 00:44:46 +00:00
}
/* Given a color component (red, green, or blue int), randomly shift by configurable amount */
function shiftColorComponent ( component , maxShiftAmt ) {
2017-08-01 03:59:24 +00:00
var shiftAmt = randomInt ( maxShiftAmt * - 1 , maxShiftAmt ) ;
var newComponent = component + shiftAmt ;
if ( ( newComponent < 0 ) || ( newComponent > 255 ) ) {
newComponent = component - shiftAmt ;
}
return newComponent ;
2017-05-08 00:44:46 +00:00
}
/* Randomly shift a RGB color by a configurable amount and return new RGB color */
function shiftColor ( color , maxShiftAmt ) {
2017-08-01 03:59:24 +00:00
return {
r : shiftColorComponent ( color . r , maxShiftAmt ) ,
g : shiftColorComponent ( color . g , maxShiftAmt ) ,
b : shiftColorComponent ( color . b , maxShiftAmt )
} ;
2017-05-08 00:44:46 +00:00
}
2017-08-01 05:34:53 +00:00
/* from: https://stackoverflow.com/a/17130415 */
2017-08-03 04:17:17 +00:00
function getMousePos ( evt , res ) {
var canvas = document . getElementsByTagName ( 'canvas' ) [ 0 ] ;
if ( canvas !== undefined ) {
var rect = canvas . getBoundingClientRect ( ) ;
return {
x : Math . round ( ( evt . clientX - rect . left ) / ( rect . right - rect . left ) * canvas . width / res ) ,
y : Math . round ( ( evt . clientY - rect . top ) / ( rect . bottom - rect . top ) * canvas . height / res )
} ;
}
}
2017-08-04 04:50:37 +00:00
function distance ( point1 , point2 ) {
var a = point1 [ 0 ] - point2 [ 0 ] ;
var b = point1 [ 1 ] - point2 [ 1 ] ;
return Math . sqrt ( a * a + b * b ) ;
}
2017-08-03 04:17:17 +00:00
function distancePos ( point1 , point2 ) {
2017-08-04 04:50:37 +00:00
// TODO: refactor distance and distancePos to the same function
2017-08-03 04:17:17 +00:00
var a = point1 . x - point2 . x ;
var b = point1 . y - point2 . y ;
return Math . sqrt ( a * a + b * b ) ;
2017-08-01 05:34:53 +00:00
}
2017-08-04 04:50:37 +00:00
// eslint-disable-next-line no-unused-vars
function shiftPointCounter ( original , maxShiftAmt ) {
var shiftAmt = randomInt ( maxShiftAmt * - 1 , 0 ) ;
var newCounter = original + shiftAmt ;
if ( newCounter < 0 ) {
newCounter = cycleDuration + shiftAmt ;
}
return newCounter ;
2017-05-08 00:44:46 +00:00
}
2017-08-04 04:50:37 +00:00
function relativeCounter ( counter , targetStart ) {
/* Return current progress of point in its cycle. AKA. what count would be if cycleDuration == targetStart */
var relCounter = counter - targetStart ;
if ( relCounter < 0 ) {
return cycleDuration + relCounter ;
}
return relCounter ;
}
2017-08-10 05:07:09 +00:00
function createSprite ( ) {
return new window . PIXI . Sprite (
window . PIXI . loader . resources [ nodeImg ] . texture
) ;
}
2017-08-13 19:11:44 +00:00
function clearSprites ( ) {
2017-08-13 06:46:51 +00:00
if ( sprites . length > 0 ) {
// need to clear out old sprites
for ( i = 0 ; i < sprites . length ; i ++ ) {
stage . removeChild ( sprites [ i ] ) ;
}
sprites = [ ] ;
}
2017-08-13 19:11:44 +00:00
}
/* POINT OPERATION FUNCTIONS */
function getRandomPoints ( numPoints , maxX , maxY , maxZ , tweeningFns ) {
var i , x , y , z , color , cycleStart , easingFn , sprite ;
var points = [ ] ;
2017-08-01 03:59:24 +00:00
for ( i = 0 ; i < numPoints ; i ++ ) {
x = randomInt ( 0 , maxX - 1 ) ;
y = randomInt ( 0 , maxY - 1 ) ;
2017-08-11 04:14:33 +00:00
// z = randomInt(0, maxZ - 1); // TODO: do something with the 3rd dimension
z = 0 ; // turns out that 3D is hard and I am a weak 2D boy
2017-08-01 03:59:24 +00:00
cycleStart = randomInt ( 0 , cycleDuration - 1 ) ;
color = randomColor ( ) ;
2017-08-03 04:17:17 +00:00
easingFn = tweeningFns [ Math . floor ( Math . random ( ) * tweeningFns . length ) ] ;
2017-08-13 06:46:51 +00:00
// save PIXI Sprite for each point in array
sprite = createSprite ( ) ;
2017-08-13 07:09:19 +00:00
if ( ! drawNodes ) sprite . visible = false ;
2017-08-13 06:46:51 +00:00
sprites . push ( sprite ) ;
stage . addChild ( sprite ) ;
2017-08-10 05:07:09 +00:00
points [ i ] = [ x , y , z , cycleStart , color , easingFn ] ;
2017-08-01 03:59:24 +00:00
}
return points ;
2017-05-08 00:44:46 +00:00
}
2017-08-13 19:11:44 +00:00
function addOrRemovePoints ( numPoints , points ) {
/* Given new value numPoints, remove or add new random points to match new count. */
var deletedSprites , newPoints ;
if ( points . target . length > numPoints ) {
points . original . splice ( numPoints - 1 ) ;
points . target . splice ( numPoints - 1 ) ;
points . tweened . splice ( numPoints - 1 ) ;
deletedSprites = sprites . splice ( numPoints - 1 ) ;
for ( var i = 0 ; i < deletedSprites . length ; i ++ ) {
stage . removeChild ( deletedSprites [ i ] ) ;
}
} else if ( points . target . length < numPoints ) {
newPoints = getRandomPoints ( numPoints - points . target . length , screenWidth , screenHeight , zRange , tweeningFns ) ;
points . original = points . original . concat ( newPoints ) ;
points . target = points . target . concat ( JSON . parse ( JSON . stringify ( newPoints ) ) ) ;
points . tweened = points . tweened . concat ( JSON . parse ( JSON . stringify ( newPoints ) ) ) ;
}
return points ;
}
2017-05-08 00:44:46 +00:00
function shiftPoints ( points , maxShiftAmt , counter , tweeningFns ) {
2017-08-01 03:59:24 +00:00
var i , shiftX , shiftY , candidateX , candidateY ;
for ( i = 0 ; i < points . original . length ; i ++ ) {
2017-08-10 05:07:09 +00:00
if ( points . target [ i ] [ 3 ] >= cycleDuration ) {
2017-05-08 00:44:46 +00:00
// cycleDuration was reduced and now this point's cycle is out of bounds. Randomly pick a new valid one.
2017-08-10 05:07:09 +00:00
points . target [ i ] [ 3 ] = randomInt ( 0 , cycleDuration - 1 ) ;
2017-08-01 03:59:24 +00:00
}
2017-08-10 05:07:09 +00:00
if ( points . target [ i ] [ 3 ] === counter ) {
2017-08-01 03:59:24 +00:00
points . original [ i ] = points . target [ i ] . slice ( ) ;
shiftX = randomInt ( maxShiftAmt * - 1 , maxShiftAmt ) ;
shiftY = randomInt ( maxShiftAmt * - 1 , maxShiftAmt ) ;
if ( ( ( shiftX < 0 ) && ( pointShiftBiasX === 1 ) ) || ( ( shiftX > 0 ) && ( pointShiftBiasX === - 1 ) ) ) {
shiftX = shiftX * - 1 ;
}
if ( ( ( shiftY < 0 ) && ( pointShiftBiasY === 1 ) ) || ( ( shiftY > 0 ) && ( pointShiftBiasY === - 1 ) ) ) {
shiftY = shiftY * - 1 ;
}
candidateX = points . original [ i ] [ 0 ] + shiftX ;
candidateY = points . original [ i ] [ 1 ] + shiftY ;
if ( ( candidateX > screenWidth ) || ( candidateX < 0 ) ) {
candidateX = points . original [ i ] [ 0 ] - shiftX ;
}
if ( ( candidateY > screenHeight ) || ( candidateY < 0 ) ) {
candidateY = points . original [ i ] [ 1 ] - shiftY ;
}
points . target [ i ] [ 0 ] = candidateX ;
points . target [ i ] [ 1 ] = candidateY ;
2017-08-10 05:07:09 +00:00
points . target [ i ] [ 5 ] = tweeningFns [ Math . floor ( Math . random ( ) * tweeningFns . length ) ] ;
2017-05-08 00:44:46 +00:00
// FIXME: buggy, makes points jump around too fast
2017-08-10 05:07:09 +00:00
// points.target[i][3] = shiftPointCounter(points.original[i][3], maxShiftAmt);
2017-08-01 03:59:24 +00:00
}
2017-05-08 00:44:46 +00:00
}
// clear pointShiftBiases now that they have been "used"
2017-08-01 03:59:24 +00:00
pointShiftBiasX = 0 ;
pointShiftBiasY = 0 ;
2017-05-08 00:44:46 +00:00
2017-08-01 03:59:24 +00:00
return points ;
2017-05-08 00:44:46 +00:00
}
2017-08-03 04:17:17 +00:00
function pullPoints ( points , clickPos , pullRate , inertia , maxDist , counter , resetPoints , tweeningFn ) {
2017-08-04 04:50:37 +00:00
var targetXDiff , targetYDiff , originXDiff , originYDiff ;
2017-08-01 05:34:53 +00:00
for ( var i = 0 ; i < points . target . length ; i ++ ) {
2017-08-04 04:50:37 +00:00
targetXDiff = clickPos . x - points . target [ i ] [ 0 ] ;
targetYDiff = clickPos . y - points . target [ i ] [ 1 ] ;
originXDiff = clickPos . x - points . original [ i ] [ 0 ] ;
originYDiff = clickPos . y - points . original [ i ] [ 1 ] ;
2017-08-13 06:46:51 +00:00
if ( Math . sqrt ( ( targetXDiff * targetXDiff ) + ( targetYDiff * targetYDiff ) ) <= maxDist ) {
// point is within effect radius
2017-08-03 04:17:17 +00:00
if ( resetPoints ) {
// Good for changing directions, reset the points original positions to their current positions
points . original [ i ] [ 0 ] = points . tweened [ i ] [ 0 ] ;
points . original [ i ] [ 1 ] = points . tweened [ i ] [ 1 ] ;
}
2017-08-04 04:50:37 +00:00
points . target [ i ] [ 0 ] += Math . round ( ( targetXDiff + ( inertia * originXDiff ) ) * pullRate ) ; // pull X
points . target [ i ] [ 1 ] += Math . round ( ( targetYDiff + ( inertia * originYDiff ) ) * pullRate ) ; // pull Y
// shift the color of each point in effect radius by some configurable amount
2017-08-10 05:07:09 +00:00
points . target [ i ] [ 4 ] = shiftColor ( points . original [ i ] [ 4 ] , clickColorShiftAmt ) ;
2017-08-03 04:17:17 +00:00
if ( tweeningFn !== null ) {
2017-08-04 04:50:37 +00:00
// Also switch the tweening function for all affected points for additional effect
// The tweening function will be re-assigned at the start of the point's next cycle
2017-08-03 04:17:17 +00:00
points . target [ i ] [ 4 ] = tweeningFn ;
}
2017-08-10 05:25:57 +00:00
if ( debug ) {
points . target [ i ] [ 6 ] = true ; // marks this point as affected
}
// If this point's cycle is near it's end, bump it up some ticks to make the animation smoother
if ( relativeCounter ( points . target [ i ] [ 3 ] ) > Math . roundcycleDuration - 10 ) {
points . target [ i ] [ 3 ] = ( points . target [ i ] [ 3 ] + Math . round ( cycleDuration / 2 ) ) % cycleDuration ;
}
} else {
if ( debug ) {
points . target [ i ] [ 6 ] = false ; // marks this point as unaffected
}
2017-08-01 17:18:04 +00:00
}
2017-08-01 05:34:53 +00:00
}
}
2017-08-13 06:46:51 +00:00
function clearAffectedPoints ( points ) {
for ( var i = 0 ; i < points . target . length ; i ++ ) {
points . target [ i ] [ 6 ] = false ;
}
}
2017-08-03 04:17:17 +00:00
function redistributeCycles ( points , oldCycleDuration , cycleDuration ) {
2017-08-04 04:50:37 +00:00
/ * G i v e n o l d a n d n e w c y c l e D u r a t i o n , r e - a s s i g n p o i n t s ' c y c l e s t a r t s t h a t e x p a n d / c o m p r e s s t o f i t t h e n e w r a n g e i n a
* way that ensures the current progress of the point in its cycle is around the same percentage ( so that the point
* does not jump erratically back or forward in it ' s current trajectory ) .
* /
// FIXME: if cycleDuration goes to 1 all points' cycles will be compressed to about the same value, and when
// cycleDuration goes back up, the values will remain the same, making the points appear to dance in sync.
2017-08-03 04:17:17 +00:00
var progress ;
2017-08-01 03:59:24 +00:00
for ( var i = 0 ; i < points . original . length ; i ++ ) {
2017-08-10 05:07:09 +00:00
progress = points . target [ i ] [ 3 ] / oldCycleDuration ;
points . target [ i ] [ 3 ] = Math . round ( progress * cycleDuration ) ;
2017-08-01 03:59:24 +00:00
}
return points ;
2017-05-08 00:44:46 +00:00
}
2017-08-13 02:41:13 +00:00
function randomizeCycles ( points , cycleDuration ) {
/* Assigns every point a new random cycle start */
for ( var i = 0 ; i < points . original . length ; i ++ ) {
points . target [ i ] [ 3 ] = randomInt ( 0 , cycleDuration - 1 ) ;
}
return points ;
}
2017-08-13 04:56:47 +00:00
function synchronizeCycles ( points , cycleDuration ) {
/* Assigns every point the same cycle start (0) */
for ( var i = 0 ; i < points . original . length ; i ++ ) {
points . target [ i ] [ 3 ] = 0 ;
}
return points ;
}
2017-08-04 04:50:37 +00:00
/* DRAW FUNCTIONS */
2017-05-08 00:44:46 +00:00
function drawPolygon ( polygon , points , counter , tweeningFns ) {
2017-08-11 04:14:33 +00:00
var i , j , easingFn , relativeCount , avgColor , shadedColor , connectionCount , dist , connectivity , scale , nodeDiameter ;
2017-05-08 00:44:46 +00:00
// calculate vectors
2017-08-01 03:59:24 +00:00
for ( i = 0 ; i < points . original . length ; i ++ ) {
2017-08-10 05:07:09 +00:00
easingFn = allTweeningFns [ points . target [ i ] [ 5 ] ] ;
relativeCount = relativeCounter ( counter , points . target [ i ] [ 3 ] ) ;
2017-08-04 02:59:53 +00:00
points . tweened [ i ] [ 0 ] = easingFn ( relativeCount , points . original [ i ] [ 0 ] , points . target [ i ] [ 0 ] - points . original [ i ] [ 0 ] , cycleDuration ) ;
points . tweened [ i ] [ 1 ] = easingFn ( relativeCount , points . original [ i ] [ 1 ] , points . target [ i ] [ 1 ] - points . original [ i ] [ 1 ] , cycleDuration ) ;
if ( debug ) {
// draw vector trajectories
2017-08-10 05:25:57 +00:00
if ( points . target [ i ] [ 6 ] ) {
polygon . lineStyle ( 1 , 0x008b8b , 1 ) ; // draw path different color if it is under effect of click/hover
} else {
polygon . lineStyle ( 1 , 0x191970 , 1 ) ;
}
2017-08-04 02:59:53 +00:00
polygon . moveTo ( points . tweened [ i ] [ 0 ] , points . tweened [ i ] [ 1 ] ) ;
for ( j = relativeCount ; j < cycleDuration ; j ++ ) {
polygon . lineTo (
easingFn ( j , points . original [ i ] [ 0 ] , points . target [ i ] [ 0 ] - points . original [ i ] [ 0 ] , cycleDuration ) ,
easingFn ( j , points . original [ i ] [ 1 ] , points . target [ i ] [ 1 ] - points . original [ i ] [ 1 ] , cycleDuration )
) ;
}
}
2017-08-01 03:59:24 +00:00
}
2017-05-08 00:44:46 +00:00
// draw lines
2017-08-01 03:59:24 +00:00
for ( i = 0 ; i < points . original . length ; i ++ ) {
connectionCount = 0 ;
for ( j = i + 1 ; j < points . original . length ; j ++ ) {
2017-05-08 00:44:46 +00:00
// TODO pick the N (connectionLimit) closest connections instead of the first N that occur sequentially.
2017-08-01 03:59:24 +00:00
if ( connectionCount >= connectionLimit ) break ;
dist = distance ( points . tweened [ i ] , points . tweened [ j ] ) ;
connectivity = dist / connectionDistance ;
if ( ( j !== i ) && ( dist <= connectionDistance ) ) {
2017-05-08 00:44:46 +00:00
// find average color of both points
2017-08-10 05:07:09 +00:00
if ( ( points . tweened [ i ] [ 3 ] === counter ) || ( points . tweened [ j ] [ 3 ] === counter ) ) {
2017-05-08 00:44:46 +00:00
// avgColor = shiftColor(avgColor, Math.round(colorShiftAmt * (1 - connectivity)));
2017-08-10 05:07:09 +00:00
points . tweened [ i ] [ 4 ] = weightedAverageColor ( points . tweened [ i ] [ 4 ] , points . tweened [ j ] [ 4 ] , connectivity ) ;
points . tweened [ j ] [ 4 ] = weightedAverageColor ( points . tweened [ j ] [ 4 ] , points . tweened [ i ] [ 4 ] , connectivity ) ;
2017-08-01 03:59:24 +00:00
}
2017-08-10 05:07:09 +00:00
avgColor = averageColor ( points . tweened [ i ] [ 4 ] , points . tweened [ j ] [ 4 ] ) ;
2017-08-01 03:59:24 +00:00
shadedColor = shadeColor ( avgColor , connectivity ) ;
2017-08-11 04:14:33 +00:00
if ( drawLines ) {
2017-08-13 04:24:56 +00:00
polygon . lineStyle ( lineSize , rgbToHex ( shadedColor ) , 1 ) ;
2017-08-11 04:14:33 +00:00
polygon . moveTo ( points . tweened [ i ] [ 0 ] , points . tweened [ i ] [ 1 ] ) ;
polygon . lineTo ( points . tweened [ j ] [ 0 ] , points . tweened [ j ] [ 1 ] ) ;
}
2017-08-01 03:59:24 +00:00
connectionCount = connectionCount + 1 ;
}
2017-05-08 00:44:46 +00:00
}
2017-08-01 03:59:24 +00:00
if ( connectionCount === 0 ) {
2017-08-10 05:07:09 +00:00
points . tweened [ i ] [ 4 ] = shiftColor ( points . tweened [ i ] [ 4 ] , disconnectedColorShiftAmt ) ;
2017-08-01 03:59:24 +00:00
}
2017-05-08 00:44:46 +00:00
2017-08-11 04:14:33 +00:00
if ( drawNodes ) {
// draw nodes
2017-08-13 07:20:53 +00:00
nodeDiameter = nodeSize ;
2017-08-11 04:14:33 +00:00
scale = nodeDiameter / nodeImgRes ;
sprites [ i ] . scale . x = scale ;
sprites [ i ] . scale . y = scale ;
sprites [ i ] . x = points . tweened [ i ] [ 0 ] - ( nodeDiameter / 2 ) ;
sprites [ i ] . y = points . tweened [ i ] [ 1 ] - ( nodeDiameter / 2 ) ;
sprites [ i ] . tint = rgbToHex ( points . tweened [ i ] [ 4 ] ) ;
}
2017-08-01 03:59:24 +00:00
}
2017-05-08 00:44:46 +00:00
}
2017-08-04 04:50:37 +00:00
/* MAIN LOOP */
2017-05-08 00:44:46 +00:00
function loop ( ) {
2017-08-19 17:53:59 +00:00
screenWidth = window . innerWidth ;
screenHeight = window . innerHeight ;
2017-08-01 03:59:24 +00:00
renderer . resize ( screenWidth , screenHeight ) ;
2017-05-08 00:44:46 +00:00
2017-08-01 03:59:24 +00:00
polygon . clear ( ) ;
2017-05-08 00:44:46 +00:00
2017-08-13 05:15:24 +00:00
if ( reset === true ) {
var newPoints ;
2017-08-13 19:11:44 +00:00
clearSprites ( ) ;
2017-08-13 06:46:51 +00:00
newPoints = getRandomPoints ( numPoints , screenWidth , screenHeight , zRange , tweeningFns ) ;
2017-08-13 05:15:24 +00:00
polygonPoints = {
original : newPoints ,
target : JSON . parse ( JSON . stringify ( newPoints ) ) ,
tweened : JSON . parse ( JSON . stringify ( newPoints ) )
} ;
reset = false ;
}
2017-08-01 05:34:53 +00:00
if ( click !== null ) {
if ( clickEnd ) {
2017-08-04 04:50:37 +00:00
// apply "rebound" effects
2017-08-03 04:17:17 +00:00
clickPullRate = clickPullRateEnd ;
clickInertia = clickInertiaEnd ;
clickTweeningFn = clickTweeningFnEnd ;
if ( debug ) {
2017-08-04 04:50:37 +00:00
// draw debug click effect radius red color when clickEnd == true
2017-08-03 04:17:17 +00:00
polygon . lineStyle ( 1 , 0xDC143C , 1 ) ;
polygon . drawCircle ( click . x , click . y , clickMaxDist ) ;
}
} else {
if ( debug ) {
2017-08-04 04:50:37 +00:00
// draw click effect radius blue when debug is on
2017-08-03 04:17:17 +00:00
polygon . lineStyle ( 1 , 0x483D8B , 1 ) ;
polygon . drawCircle ( click . x , click . y , clickMaxDist ) ;
}
2017-08-01 05:34:53 +00:00
}
2017-08-03 04:17:17 +00:00
2017-08-04 04:50:37 +00:00
// a pointer event is occuring and needs to affect the points in effect radius
2017-08-03 04:17:17 +00:00
pullPoints ( polygonPoints , click , clickPullRate , clickInertia , clickMaxDist , counter , clickEnd , clickTweeningFn ) ;
2017-08-01 05:34:53 +00:00
// slightly increase effect amount for next loop if click is still occuring
if ( clickMaxDist <= clickMaxDistMax ) {
clickMaxDist += clickMaxDistInc ;
}
if ( clickPullRate <= clickPullRateMax ) {
clickPullRate += clickPullRateInc ;
}
if ( clickEnd ) {
2017-08-04 04:50:37 +00:00
// done with rebound effect, re-initialize everything to prepare for next click
2017-08-01 05:34:53 +00:00
click = null ;
clickEnd = false ;
clickMaxDist = clickMaxDistStart ;
clickPullRate = clickPullRateStart ;
2017-08-03 04:17:17 +00:00
clickInertia = clickInertiaStart ;
clickTweeningFn = clickTweeningFnStart ;
2017-08-13 06:46:51 +00:00
clearAffectedPoints ( polygonPoints ) ;
2017-08-01 05:34:53 +00:00
}
2017-08-03 04:17:17 +00:00
} else if ( hover !== null ) {
if ( lastHover !== null ) {
2017-08-04 04:50:37 +00:00
// hover effect radius grows bigger the faster the mouse moves
2017-08-03 04:17:17 +00:00
hoverMaxDist += Math . min ( Math . round ( distancePos ( hover , lastHover ) ) , hoverMaxDistMax ) ;
}
if ( debug ) {
2017-08-04 04:50:37 +00:00
// draw hover effect radius yellow when debug is on
2017-08-03 04:17:17 +00:00
polygon . lineStyle ( 1 , 0xBDB76B , 1 ) ;
polygon . drawCircle ( hover . x , hover . y , hoverMaxDist ) ;
}
2017-08-04 04:50:37 +00:00
// a hover event is occuring and needs to affect the points in effect radius
2017-08-03 04:17:17 +00:00
pullPoints ( polygonPoints , hover , hoverPushRate , hoverInertia , hoverMaxDist , counter , false , hoverTweeningFn ) ;
hoverMaxDist = hoverMaxDistStart ;
lastHover = hover ;
2017-08-01 05:34:53 +00:00
}
2017-08-04 04:50:37 +00:00
// TODO: it would be cool to fill in triangles
2017-08-03 04:17:17 +00:00
// polygon.beginFill(0x00FF00);
2017-08-01 03:59:24 +00:00
drawPolygon ( polygon , polygonPoints , counter , tweeningFns ) ;
2017-08-03 04:17:17 +00:00
// polygon.endFill();
2017-05-08 00:44:46 +00:00
2017-08-01 03:59:24 +00:00
counter += 1 ;
counter = counter % cycleDuration ;
2017-05-08 00:44:46 +00:00
2017-08-01 03:59:24 +00:00
if ( counter === 0 && fpsEnabled ) {
thisLoop = new Date ( ) ;
fps = Math . round ( ( 1000 / ( thisLoop - lastLoop ) ) * cycleDuration ) ;
fpsGraphic . setText ( fps . toString ( ) ) ;
lastLoop = thisLoop ;
}
2017-05-08 00:44:46 +00:00
2017-08-04 04:50:37 +00:00
// points that have reached the end of their cycles need new targets
2017-08-01 03:59:24 +00:00
polygonPoints = shiftPoints ( polygonPoints , pointShiftDistance , counter , tweeningFns ) ;
2017-05-08 00:44:46 +00:00
// If user scrolled, modify cycleDuration by amount scrolled
2017-08-01 03:59:24 +00:00
if ( scrollDelta !== 0 ) {
2017-08-03 04:17:17 +00:00
var oldCycleDuration = cycleDuration ;
cycleDuration = Math . round ( cycleDuration + scrollDelta ) ;
2017-08-01 03:59:24 +00:00
if ( cycleDuration < 1 ) {
cycleDuration = 1 ;
}
scrollDelta = 0 ;
2017-08-03 04:17:17 +00:00
polygonPoints = redistributeCycles ( polygonPoints , oldCycleDuration , cycleDuration ) ;
2017-08-12 22:50:51 +00:00
// Update control inputs
var timeRange = document . getElementsByName ( 'timeRange' ) [ 0 ] ;
var timeInput = document . getElementsByName ( 'timeInput' ) [ 0 ] ;
timeRange . value = cycleDuration ;
timeInput . value = cycleDuration ;
2017-05-08 00:44:46 +00:00
}
// Tell the `renderer` to `render` the `stage`
2017-08-01 03:59:24 +00:00
renderer . render ( stage ) ;
window . requestAnimationFrame ( loop ) ;
2017-05-08 00:44:46 +00:00
}
function loopStart ( ) {
2017-08-19 17:53:59 +00:00
screenWidth = window . innerWidth ;
screenHeight = window . innerHeight ;
2017-05-08 00:44:46 +00:00
// Create the renderer
2017-08-01 05:34:53 +00:00
renderer = window . PIXI . autoDetectRenderer ( screenWidth , screenHeight , { antialias : true , resolution : resolution } ) ;
2017-05-08 00:44:46 +00:00
// Add the canvas to the HTML document
2017-08-01 03:59:24 +00:00
document . body . appendChild ( renderer . view ) ;
2017-05-08 00:44:46 +00:00
// Create a container object called the `stage`
2017-08-01 03:59:24 +00:00
stage = new window . PIXI . Container ( ) ;
renderer . view . style . position = 'absolute' ;
renderer . view . style . display = 'block' ;
renderer . autoResize = true ;
counter = 0 ;
totalScreenPixels = screenWidth + screenHeight ;
connectionDistance = Math . min ( Math . round ( totalScreenPixels / 16 ) , 75 ) ;
pointShiftDistance = Math . round ( totalScreenPixels / 45 ) ;
2017-08-11 04:45:47 +00:00
clickMaxDistStart = Math . min ( Math . round ( totalScreenPixels / 20 ) , clickMaxDistStart ) ;
2017-08-11 04:48:35 +00:00
hoverMaxDistStart = Math . min ( Math . round ( totalScreenPixels / 16 ) , hoverMaxDistStart ) ;
2017-08-01 03:59:24 +00:00
polygon = new window . PIXI . Graphics ( ) ;
2017-08-10 05:07:09 +00:00
stage . addChild ( polygon ) ;
2017-08-13 06:46:51 +00:00
numPoints = Math . round ( totalScreenPixels / 6 ) ;
startPoints = getRandomPoints ( numPoints , screenWidth , screenHeight , zRange , tweeningFns ) ;
2017-08-01 03:59:24 +00:00
polygonPoints = {
original : startPoints ,
target : JSON . parse ( JSON . stringify ( startPoints ) ) ,
tweened : JSON . parse ( JSON . stringify ( startPoints ) )
} ;
fpsGraphic = new window . PIXI . Text ( '0' , { font : '25px monospace' , fill : 'yellow' } ) ;
2017-08-13 02:41:13 +00:00
fpsGraphic . anchor = new window . PIXI . Point ( 1 , 0 ) ;
fpsGraphic . x = screenWidth - 1 ;
2017-08-01 03:59:24 +00:00
fpsGraphic . y = 0 ;
2017-08-01 03:24:09 +00:00
2017-08-01 03:59:24 +00:00
if ( fpsEnabled ) {
stage . addChild ( fpsGraphic ) ;
}
2017-05-08 00:44:46 +00:00
2017-08-01 03:59:24 +00:00
lastLoop = new Date ( ) ;
2017-05-08 00:44:46 +00:00
2017-08-01 03:59:24 +00:00
scrollDelta = 0 ;
2017-05-08 00:44:46 +00:00
2017-08-12 22:50:51 +00:00
// Try to fix bug where click initializes to a bogus value
click = null ;
hover = null ;
2017-08-01 03:59:24 +00:00
window . requestAnimationFrame ( loop ) ;
2017-05-08 00:44:46 +00:00
}
2017-08-10 05:07:09 +00:00
// Use PIXI loader to load image and then start animation
window . PIXI . loader
. add ( nodeImg )
. load ( loopStart ) ;
2017-05-08 00:44:46 +00:00
2017-08-19 17:39:10 +00:00
window . addEventListener ( 'load' , function ( ) {
2017-08-13 04:24:56 +00:00
var canvas , tweeningInputs , debugCheckbox , fpsCheckbox , nodeCheckbox , linesCheckbox ;
canvas = document . getElementsByTagName ( 'canvas' ) [ 0 ] ;
canvas . setAttribute ( 'tabindex' , 0 ) ;
2017-08-12 22:50:51 +00:00
tweeningInputs = document . getElementsByName ( 'tweening' ) ;
2017-08-13 04:24:56 +00:00
debugCheckbox = document . getElementsByName ( 'debugToggle' ) [ 0 ] ;
fpsCheckbox = document . getElementsByName ( 'fpsCounterToggle' ) [ 0 ] ;
nodesCheckbox = document . getElementsByName ( 'nodesToggle' ) [ 0 ] ;
linesCheckbox = document . getElementsByName ( 'linesToggle' ) [ 0 ] ;
2017-08-12 22:50:51 +00:00
/* MOUSE AND TOUCH EVENTS */
2017-08-03 04:17:17 +00:00
2017-08-12 22:50:51 +00:00
window . addEventListener ( 'mousewheel' , function ( e ) {
if ( e . target . tagName !== 'CANVAS' ) return ;
scrollDelta = scrollDelta + ( ( e . deltaY / 100 ) * 3 ) ;
} ) ;
2017-05-08 00:44:46 +00:00
2017-08-12 22:50:51 +00:00
window . addEventListener ( 'touchstart' , function ( e ) {
if ( e . target . tagName !== 'CANVAS' ) return ;
2017-08-13 04:24:56 +00:00
e . target . focus ( ) ;
2017-08-03 04:17:17 +00:00
click = getMousePos ( e . changedTouches [ 0 ] , resolution ) ;
2017-08-12 22:50:51 +00:00
clickEnd = false ;
} ) ;
2017-08-01 05:34:53 +00:00
2017-08-12 22:50:51 +00:00
window . addEventListener ( 'touchmove' , function ( e ) {
if ( e . target . tagName !== 'CANVAS' ) return ;
if ( click !== null ) {
click = getMousePos ( e . changedTouches [ 0 ] , resolution ) ;
2017-08-01 03:59:24 +00:00
}
2017-08-12 22:50:51 +00:00
} ) ;
window . addEventListener ( 'touchend' , function ( e ) {
clickEnd = true ;
} ) ;
window . addEventListener ( 'touchcancel' , function ( e ) {
clickEnd = true ;
} ) ;
window . addEventListener ( 'mousedown' , function ( e ) {
if ( e . target . tagName !== 'CANVAS' ) return ;
2017-08-13 04:24:56 +00:00
e . target . focus ( ) ;
2017-08-12 22:50:51 +00:00
click = getMousePos ( e , resolution ) ;
clickEnd = false ;
} ) ;
window . addEventListener ( 'mousemove' , function ( e ) {
if ( e . target . tagName !== 'CANVAS' ) return ;
var pos = getMousePos ( e , resolution ) ;
if ( click !== null ) {
click = pos ;
}
hover = pos ;
} ) ;
window . addEventListener ( 'mouseup' , function ( e ) {
clickEnd = true ;
hover = null ;
lastHover = null ;
} ) ;
window . addEventListener ( 'mouseleave' , function ( e ) {
clickEnd = true ;
hover = null ;
lastHover = null ;
2017-08-13 06:46:51 +00:00
clearAffectedPoints ( polygonPoints ) ;
2017-08-12 22:50:51 +00:00
} ) ;
document . addEventListener ( 'mouseleave' , function ( e ) {
clickEnd = true ;
hover = null ;
lastHover = null ;
2017-08-13 06:46:51 +00:00
clearAffectedPoints ( polygonPoints ) ;
2017-08-12 22:50:51 +00:00
} ) ;
/* KEYBOARD EVENTS */
window . addEventListener ( 'keydown' , function ( e ) {
var i ;
2017-08-13 01:33:53 +00:00
if ( e . target . tagName === 'INPUT' ) return ;
2017-08-12 22:50:51 +00:00
if ( e . keyCode === 37 ) { // left
pointShiftBiasX = - 1 ;
} else if ( e . keyCode === 38 ) { // up
pointShiftBiasY = - 1 ;
} else if ( e . keyCode === 39 ) { // right
pointShiftBiasX = 1 ;
} else if ( e . keyCode === 40 ) { // down
pointShiftBiasY = 1 ;
} else if ( e . keyCode === 49 ) { // 1
tweeningFns = tweeningSets . linear ;
tweeningInputs [ 0 ] . checked = true ;
} else if ( e . keyCode === 50 ) { // 2
tweeningFns = tweeningSets . meandering ;
tweeningInputs [ 1 ] . checked = true ;
} else if ( e . keyCode === 51 ) { // 3
tweeningFns = tweeningSets . snappy ;
tweeningInputs [ 2 ] . checked = true ;
} else if ( e . keyCode === 52 ) { // 4
tweeningFns = tweeningSets . bouncy ;
tweeningInputs [ 3 ] . checked = true ;
} else if ( e . keyCode === 53 ) { // 5
tweeningFns = tweeningSets . elastic ;
tweeningInputs [ 4 ] . checked = true ;
} else if ( e . keyCode === 54 ) { // 6
tweeningFns = tweeningSets . back ;
tweeningInputs [ 5 ] . checked = true ;
} else if ( e . keyCode === 70 ) { // f
2017-08-13 03:15:24 +00:00
toggleFPS ( ) ;
2017-08-12 22:50:51 +00:00
} else if ( e . keyCode === 68 ) { // d
2017-08-13 03:15:24 +00:00
toggleDebug ( ) ;
2017-08-12 22:50:51 +00:00
} else if ( e . keyCode === 78 ) { // n
2017-08-13 04:24:56 +00:00
toggleNodes ( ) ;
2017-08-12 22:50:51 +00:00
} else if ( e . keyCode === 76 ) { // l
2017-08-13 04:24:56 +00:00
toggleLines ( ) ;
2017-08-12 22:50:51 +00:00
} else if ( e . keyCode === 191 ) { // ?
toggleHelp ( ) ;
2017-08-11 05:24:18 +00:00
}
2017-08-12 22:50:51 +00:00
} ) ;
2017-08-13 01:33:53 +00:00
/* BUTTON & INPUT EVENTS */
2017-08-12 22:50:51 +00:00
document . getElementById ( 'toggle-help' ) . addEventListener ( 'click' , function ( ) {
toggleHelp ( ) ;
} , false ) ;
document . getElementById ( 'toggle-controls' ) . addEventListener ( 'click' , function ( ) {
toggleControls ( ) ;
} , false ) ;
2017-08-13 04:56:47 +00:00
document . getElementById ( 'close-help' ) . addEventListener ( 'click' , function ( ) {
toggleHelp ( ) ;
} , false ) ;
document . getElementById ( 'close-controls' ) . addEventListener ( 'click' , function ( ) {
toggleControls ( ) ;
} , false ) ;
document . getElementById ( 'synchronize-cycles' ) . addEventListener ( 'click' , function ( ) {
synchronizeCycles ( polygonPoints , cycleDuration ) ;
} , false ) ;
2017-08-13 02:41:13 +00:00
document . getElementById ( 'randomize-cycles' ) . addEventListener ( 'click' , function ( ) {
randomizeCycles ( polygonPoints , cycleDuration ) ;
2017-08-13 05:15:24 +00:00
} , false ) ;
document . getElementById ( 'reset' ) . addEventListener ( 'click' , function ( ) {
reset = true ;
2017-08-13 02:41:13 +00:00
} , false ) ;
2017-08-13 06:46:51 +00:00
var connectDistRange = document . getElementsByName ( 'connectDistRange' ) [ 0 ] ;
connectDistRange . value = connectionDistance ;
connectDistRange . addEventListener ( 'input' , function ( e ) {
connectionDistance = parseInt ( this . value , 10 ) ;
} ) ;
var connectDistInput = document . getElementsByName ( 'connectDistInput' ) [ 0 ] ;
connectDistInput . value = connectionDistance ;
connectDistInput . addEventListener ( 'input' , function ( e ) {
connectionDistance = parseInt ( this . value , 10 ) ;
} ) ;
var connectLimitRange = document . getElementsByName ( 'connectLimitRange' ) [ 0 ] ;
connectLimitRange . value = connectionLimit ;
connectLimitRange . addEventListener ( 'input' , function ( e ) {
connectionLimit = parseInt ( this . value , 10 ) ;
} ) ;
var connectLimitInput = document . getElementsByName ( 'connectLimitInput' ) [ 0 ] ;
connectLimitInput . value = connectionLimit ;
connectLimitInput . addEventListener ( 'input' , function ( e ) {
connectionLimit = parseInt ( this . value , 10 ) ;
} ) ;
var pointsNumRange = document . getElementsByName ( 'pointsNumRange' ) [ 0 ] ;
pointsNumRange . value = numPoints ;
pointsNumRange . addEventListener ( 'input' , function ( e ) {
numPoints = parseInt ( this . value , 10 ) ;
2017-08-13 19:11:44 +00:00
polygonPoints = addOrRemovePoints ( numPoints , polygonPoints ) ;
2017-08-13 06:46:51 +00:00
} ) ;
var pointsNumInput = document . getElementsByName ( 'pointsNumInput' ) [ 0 ] ;
pointsNumInput . value = numPoints ;
pointsNumInput . addEventListener ( 'input' , function ( e ) {
numPoints = parseInt ( this . value , 10 ) ;
2017-08-13 19:11:44 +00:00
polygonPoints = addOrRemovePoints ( numPoints , polygonPoints ) ;
2017-08-13 06:46:51 +00:00
} ) ;
2017-08-13 07:09:19 +00:00
var maxTravelRange = document . getElementsByName ( 'maxTravelRange' ) [ 0 ] ;
maxTravelRange . value = pointShiftDistance ;
maxTravelRange . addEventListener ( 'input' , function ( e ) {
pointShiftDistance = parseInt ( this . value , 10 ) ;
} ) ;
var maxTravelInput = document . getElementsByName ( 'maxTravelInput' ) [ 0 ] ;
maxTravelInput . value = pointShiftDistance ;
maxTravelInput . addEventListener ( 'input' , function ( e ) {
pointShiftDistance = parseInt ( this . value , 10 ) ;
} ) ;
2017-08-13 06:46:51 +00:00
var timeRange = document . getElementsByName ( 'timeRange' ) [ 0 ] ;
2017-08-12 22:50:51 +00:00
timeRange . value = cycleDuration ;
timeRange . addEventListener ( 'input' , function ( e ) {
var oldCycleDuration = cycleDuration ;
cycleDuration = parseInt ( this . value , 10 ) ;
polygonPoints = redistributeCycles ( polygonPoints , oldCycleDuration , cycleDuration ) ;
} ) ;
2017-08-13 06:46:51 +00:00
var timeInput = document . getElementsByName ( 'timeInput' ) [ 0 ] ;
2017-08-12 22:50:51 +00:00
timeInput . value = cycleDuration ;
timeInput . addEventListener ( 'input' , function ( e ) {
var oldCycleDuration = cycleDuration ;
2017-08-13 01:33:53 +00:00
if ( this . value === '' || this . value === '0' ) return ;
2017-08-12 22:50:51 +00:00
cycleDuration = parseInt ( this . value , 10 ) ;
polygonPoints = redistributeCycles ( polygonPoints , oldCycleDuration , cycleDuration ) ;
} ) ;
var i ;
for ( i = 0 ; i < tweeningInputs . length ; i ++ ) {
tweeningInputs [ i ] . addEventListener ( 'change' , function ( e ) {
tweeningFns = tweeningSets [ this . value ] ;
} ) ;
2017-08-01 03:24:09 +00:00
}
2017-08-13 03:15:24 +00:00
2017-08-13 06:46:51 +00:00
var pointSizeRange = document . getElementsByName ( 'pointSizeRange' ) [ 0 ] ;
2017-08-13 04:24:56 +00:00
pointSizeRange . value = nodeSize ;
pointSizeRange . addEventListener ( 'input' , function ( e ) {
nodeSize = parseInt ( this . value , 10 ) ;
} ) ;
2017-08-13 06:46:51 +00:00
var pointSizeInput = document . getElementsByName ( 'pointSizeInput' ) [ 0 ] ;
2017-08-13 04:24:56 +00:00
pointSizeInput . value = nodeSize ;
pointSizeInput . addEventListener ( 'input' , function ( e ) {
nodeSize = parseInt ( this . value , 10 ) ;
} ) ;
2017-08-13 06:46:51 +00:00
var lineSizeRange = document . getElementsByName ( 'lineSizeRange' ) [ 0 ] ;
2017-08-13 04:24:56 +00:00
lineSizeRange . value = lineSize ;
lineSizeRange . addEventListener ( 'input' , function ( e ) {
lineSize = parseInt ( this . value , 10 ) ;
} ) ;
2017-08-13 07:09:19 +00:00
var lineSizeInput = document . getElementsByName ( 'lineSizeInput' ) [ 0 ] ;
lineSizeInput . value = lineSize ;
lineSizeInput . addEventListener ( 'input' , function ( e ) {
2017-08-13 04:24:56 +00:00
lineSize = parseInt ( this . value , 10 ) ;
} ) ;
2017-08-13 06:46:51 +00:00
var colorShiftRange = document . getElementsByName ( 'colorShiftRange' ) [ 0 ] ;
2017-08-13 04:56:47 +00:00
colorShiftRange . value = disconnectedColorShiftAmt ;
colorShiftRange . addEventListener ( 'input' , function ( e ) {
disconnectedColorShiftAmt = parseInt ( this . value , 10 ) ;
} ) ;
2017-08-13 07:09:19 +00:00
var colorShiftInput = document . getElementsByName ( 'colorShiftInput' ) [ 0 ] ;
colorShiftInput . value = disconnectedColorShiftAmt ;
colorShiftInput . addEventListener ( 'input' , function ( e ) {
2017-08-13 04:56:47 +00:00
disconnectedColorShiftAmt = parseInt ( this . value , 10 ) ;
} ) ;
2017-08-13 04:24:56 +00:00
2017-08-13 03:15:24 +00:00
debugCheckbox . addEventListener ( 'change' , function ( e ) {
toggleDebug ( ) ;
} ) ;
fpsCheckbox . addEventListener ( 'change' , function ( e ) {
toggleFPS ( ) ;
} ) ;
2017-08-13 04:24:56 +00:00
nodesCheckbox . addEventListener ( 'change' , function ( e ) {
toggleNodes ( ) ;
} ) ;
linesCheckbox . addEventListener ( 'change' , function ( e ) {
toggleLines ( ) ;
} ) ;
2017-08-19 17:39:10 +00:00
} , false ) ;