summaryrefslogtreecommitdiff
path: root/js/lib
diff options
context:
space:
mode:
authorJules <jules@asdf.us>2016-09-11 10:19:07 -0400
committerJules <jules@asdf.us>2016-09-11 10:19:07 -0400
commitfbf05115c17163d91e9f649a348e3e6800a22d5e (patch)
tree14920be071bce59aa633be8dc47f8281a66a85db /js/lib
init latest version of harp
Diffstat (limited to 'js/lib')
-rw-r--r--js/lib/app.js69
-rw-r--r--js/lib/canvas_loader.js150
-rw-r--r--js/lib/canvasmx.js35
-rw-r--r--js/lib/env.js123
-rw-r--r--js/lib/grid.js183
-rw-r--r--js/lib/snapmx.js33
-rw-r--r--js/lib/wavegrid.js136
7 files changed, 729 insertions, 0 deletions
diff --git a/js/lib/app.js b/js/lib/app.js
new file mode 100644
index 0000000..2138025
--- /dev/null
+++ b/js/lib/app.js
@@ -0,0 +1,69 @@
+
+var scene, cam, controls
+
+var app = (function() {
+
+ var app = {}
+ var last_t = 0, initial_t = 0
+
+ app.init = function() {
+ app.bind()
+ app.build()
+ // check if we're in an iframe
+ if (window.self === window.top) {
+ app.ready()
+ }
+ }
+
+ app.bind = function() {
+ $(window).resize(app.resize)
+ }
+
+ app.build = function() {
+ if ($.browser.msie || ! has3d()) { return app.fallback() }
+
+ scene = new MX.Scene().addTo('#scene')
+ cam = scene.camera
+
+ app.resize()
+
+ window.scrollTo(0,0)
+ environment.init()
+ }
+
+ app.ready = function() {
+ if (last_t) return
+ setTimeout(function () {
+ $("html").removeClass("loading")
+ }, 100)
+ app.animate(0)
+ }
+
+ app.animate = function (t) {
+ requestAnimationFrame(app.animate)
+ if (! initial_t) {
+ initial_t = t
+ return
+ }
+ t -= initial_t
+ var dt = t - last_t
+ last_t = t
+ environment.update(t)
+ }
+
+ app.resize = function () {
+ scene.width = window.innerWidth
+ scene.height = window.innerHeight
+ scene.perspective = Math.min(window.innerWidth, scene.height)
+ scene.update()
+ }
+
+ app.fallback = function(){
+ $('body').addClass('fallback')
+ }
+
+ return app
+
+})()
+
+document.addEventListener('DOMContentLoaded', app.init)
diff --git a/js/lib/canvas_loader.js b/js/lib/canvas_loader.js
new file mode 100644
index 0000000..c2ed31a
--- /dev/null
+++ b/js/lib/canvas_loader.js
@@ -0,0 +1,150 @@
+var CanvasLoader = (function(){
+
+ var loader = new Loader (function(){})
+
+ function CanvasLoader (opt){
+ this.opt = defaults(opt, {
+ rotate: true,
+ })
+ this.canvas = document.createElement("canvas")
+ this.ctx = this.canvas.getContext('2d')
+ }
+ CanvasLoader.prototype.load_image = function(url, cb){
+ loader.preloadImage(url, function(img){
+ var w = this.w = img.naturalWidth
+ var h = this.h = img.naturalHeight
+ this.img = img
+ cb(img, w, h)
+ }.bind(this))
+ }
+ CanvasLoader.prototype.scale_image_data = function(opt){
+ var w, h
+ if (opt.scale) {
+ w = this.w * opt.scale
+ h = this.h * opt.scale
+ }
+ if (opt.w) {
+ w = opt.w
+ h = opt.w * this.h / this.w
+ }
+ else if (opt.h) {
+ w = opt.h * this.w / this.h
+ h = opt.h
+ }
+ this.canvas.width = w
+ this.canvas.height = h
+ this.ctx.save()
+
+ if (this.opt.rotate) {
+ this.ctx.translate(w/2, h/2)
+ this.ctx.rotate( Math.PI )
+ this.ctx.translate(-w/2, -h/2)
+ }
+
+ this.ctx.drawImage(this.img, 0, 0, w, h)
+ this.ctx.restore()
+ return this.ctx.getImageData(0, 0, w, h)
+ }
+ CanvasLoader.prototype.gray_image_data = function(opt){
+ var data = this.scale_image_data(opt)
+ var pixels = data.data
+ var len = data.width * data.height
+ var gray_data = new Uint8Array( len )
+ var invert = this.opt.invert
+
+ var gray, ii
+ for (var i = 0; i < len; i++) {
+ ii = i * 4
+ gray = (255 - (pixels[ii] + pixels[ii+1] + pixels[ii+2]) / 3 ) * (pixels[ii+3]/255)
+ gray_data[i] = (invert) ? 255 - gray : gray
+ }
+
+ return {
+ width: data.width,
+ height: data.height,
+ data: gray_data,
+ }
+ }
+ // in: imagedata [r,g,b,a] and a palette
+ // out: an array strided as [ palette offset, alpha ]
+ CanvasLoader.prototype.palette_image_data = function(opt){
+ var data = this.scale_image_data(opt)
+ var pixels = data.data
+ var len = data.width * data.height
+ var palette_data = new Uint8Array( len*2 )
+ var invert = this.opt.invert
+
+ var palette = this.paletteToNumbers(opt.palette)
+
+ var color, index, ii
+ for (var i = 0; i < len; i++) {
+ ii = i * 2
+ jj = i * 4
+ index = closest_to( palette, pixels.slice(jj, jj+3) )
+ palette_data[ii] = index
+ palette_data[ii+1] = pixels[jj+3]
+ }
+
+ return {
+ width: data.width,
+ height: data.height,
+ data: palette_data,
+ }
+ }
+
+ CanvasLoader.prototype.paletteToStrings = function (palette){
+ if (typeof palette[0] === "string") return palette
+ return palette.map(function(rgb){
+ return "rgb(" + rgb.join(",") + ")"
+ })
+ }
+ CanvasLoader.prototype.paletteToNumbers = function (palette){
+ if (typeof palette[0] !== "string") return palette
+ var colors = palette.map(function(color){
+ if (color[0] == "#") return hexToRgb(color)
+ return rgbStringToRgb(color)
+ }).filter(function(color){ return !! color }).map(function(rgb){
+ return [rgb.r, rgb.g, rgb.b]
+ })
+ return colors
+ }
+
+ function hexToRgb(hex) {
+ // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
+ var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
+ hex = hex.replace(shorthandRegex, function(m, r, g, b) {
+ return r + r + g + g + b + b;
+ });
+
+ var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+ return result ? {
+ r: parseInt(result[1], 16),
+ g: parseInt(result[2], 16),
+ b: parseInt(result[3], 16)
+ } : null;
+ }
+ function rgbStringToRgb(rgb) {
+ var nums = rgb.replace(/rgba?\(/,"").replace(/\)/,"").split(",")
+ return { r: nums[0], g: nums[1], b: nums[2] }
+ }
+ function closest_to(palette, pixel){
+ return palette.reduce(function(prev, curr, index) {
+ var d = distance(pixel, curr)
+ if (prev[0] > d) {
+ prev[0] = d
+ prev[1] = index
+ }
+ return prev
+ }, [Infinity, -1])[1]
+ }
+ function distance(u, v){
+ if (! v) return Math.Infinity
+ var r = u[0] - v[0]
+ var g = u[1] - v[1]
+ var b = u[2] - v[2]
+ return Math.sqrt(r*r+g*g+b*b)
+ }
+
+ return CanvasLoader
+
+})() \ No newline at end of file
diff --git a/js/lib/canvasmx.js b/js/lib/canvasmx.js
new file mode 100644
index 0000000..4b53610
--- /dev/null
+++ b/js/lib/canvasmx.js
@@ -0,0 +1,35 @@
+var CanvasMX = (function(){
+
+ function CanvasMX (w, h) {
+ w = w || 100
+ h = h || 100
+ var canvas = this.canvas = document.createElement("canvas")
+ canvas.width = w
+ canvas.height = h
+ this.ctx = canvas.getContext('2d')
+ var mx = this.mx = new MX.Object3D ()
+ this.el = mx.el
+ mx.el.appendChild(canvas)
+ scene.add(mx)
+ }
+ CanvasMX.prototype.resize = function(w, h){
+ this.canvas.width = w
+ this.canvas.height = h
+ this.mx.width = w
+ this.mx.height = h
+ }
+ CanvasMX.prototype.clear = function(){
+ this.snap.clear()
+ }
+ CanvasMX.prototype.show = function(){
+ this.visible = true
+ this.mx.style.display = "block"
+ }
+ CanvasMX.prototype.hide = function(){
+ this.visible = false
+ this.mx.style.display = "hide"
+ }
+
+ return CanvasMX
+
+})() \ No newline at end of file
diff --git a/js/lib/env.js b/js/lib/env.js
new file mode 100644
index 0000000..aecb61b
--- /dev/null
+++ b/js/lib/env.js
@@ -0,0 +1,123 @@
+var environment = (function(){
+
+ var environment = {}
+ var width = 400
+ var height = 600
+ var grid_side = 50
+ var grid_stroke = 2
+ var sph
+ var left, right, top, bottom
+ var done_animating = false
+
+ environment.init = function(){
+ environment.ready()
+
+ app.resize = function () {
+ scene.width = window.innerWidth
+ scene.height = window.innerHeight
+ scene.perspective = Math.min(window.innerWidth, scene.height)
+ scene.el.style[MX.perspectiveProp] = 200 + 'px'
+ scene.update()
+ }
+ }
+
+ environment.ready = function(){
+ controls = new MX.FollowingOrbitCamera({
+ center: { x: 0, y: 0, z: 0 },
+ radius: 1250,
+ radiusRange: [ 300, 2000 ],
+ rotationXRange: [ -0.05, 0.05 ],
+ rotationYRange: [ -0.05, 0.05 ],
+ wheelEase: 20,
+ ease: 10
+ })
+ controls.init()
+ controls.wheel.lock()
+
+ top = environment.make_side()
+ top.mx.rotationY = Math.PI/2
+ top.mx.x = -width/2
+
+ bottom = environment.make_side()
+ bottom.mx.rotationY = -Math.PI/2
+ bottom.mx.x = width/2
+
+ left = environment.make_side()
+ left.mx.rotationX = Math.PI
+ left.mx.z = -width/2
+
+ right = environment.make_side()
+ right.mx.rotationX = 0 // Math.PI/2
+ right.mx.z = width/2
+
+ var sphere = new Image
+ sphere.src = "http://dumpfm.s3.amazonaws.com/images/20101115/1289880948498-dumpfm-lolumad-wireframesphere.gif"
+ var mx = new MX.Object3D (sphere)
+ mx.width = 600
+ mx.height = 400
+
+ mx.y = 100
+ mx.rotationY = Math.PI
+ mx.rotationX = Math.PI/2
+ mx.rotationZ = Math.PI/2
+ scene.add(mx)
+ sph = mx
+ }
+
+ environment.make_side = function(){
+ var g = new Grid ({
+ side: grid_side - grid_stroke,
+ strokeWidth: grid_stroke,
+ bg: "#ffffff",
+ stroke: [ "#000000" ],
+ duration: 1000,
+ delay: [ 0, 500 ],
+ width: width,
+ height: height,
+ draw_sides: true,
+ })
+ var mx = new MX.Object3D (g.snap.node)
+ mx.width = width
+ mx.height = height
+ scene.add(mx)
+ return { g: g, mx: mx }
+ }
+
+ environment.update = function(t){
+ scene.update()
+ controls.update()
+
+// if (! done_animating) return
+
+ t *= 0.05
+
+ top.g.animate({
+ h: "right",
+ v: "down",
+ duration: 0,
+ v_offset: -t,
+ })
+ bottom.g.animate({
+ h: "left",
+ v: "down",
+ duration: 0,
+ v_offset: -t,
+ })
+ left.g.animate({
+ h: "left",
+ v: "up",
+ duration: 0,
+ v_offset: t,
+ })
+ right.g.animate({
+ h: "left",
+ v: "down",
+ duration: 0,
+ v_offset: -t,
+ })
+ }
+
+ return environment
+
+})()
+
diff --git a/js/lib/grid.js b/js/lib/grid.js
new file mode 100644
index 0000000..aecaed8
--- /dev/null
+++ b/js/lib/grid.js
@@ -0,0 +1,183 @@
+var Grid = (function(){
+
+ var Grid = function(opt){
+ var horizontal = this.horizontal = []
+ var vertical = this.vertical = []
+ var sides = this.sides = []
+ var x, xmin, xmax
+ var y, ymin, ymax
+ var dx = opt.strokeWidth + opt.side
+ var dy = dx
+ var i
+ var s
+
+ opt = this.opt = defaults(opt, {
+ bg: "#ffffff",
+ stroke: "#000000",
+ strokeWidth: 2,
+ side: 10,
+ delay: 0,
+ shuffleDelay: false,
+ randomizeDelay: false,
+ duration: 500,
+ width: 300,
+ height: 300,
+ halign: "left",
+ valign: "top",
+ draw_sides: false,
+ snap: null,
+ })
+ if (typeof opt.stroke == 'string') opt.stroke = [opt.stroke]
+ if (typeof opt.delay == 'string') opt.delay = [opt.delay]
+
+ if (opt.snap) {
+ s = this.snap = opt.snap
+ } else {
+ s = this.snap = Snap(opt.width, opt.height)
+ s.attr({ viewBox: "0 0 " + opt.width + " " + opt.height })
+ }
+
+ switch (opt.halign) {
+ case "right":
+ ymin = opt.height % dy
+ ymax = opt.height + 1
+ break
+ default:
+ case "left":
+ ymin = 0
+ ymax = opt.height
+ break
+ }
+
+ switch (opt.valign) {
+ case "bottom":
+ xmin = opt.width % dx
+ xmax = opt.width + 1
+ break
+ default:
+ case "top":
+ xmin = 0
+ xmax = opt.width
+ break
+ }
+
+ this.bg = s.rect(0, 0, "100%", "100%").attr({ fill: opt.bg })
+
+ this.pos = {
+ dx: dx, xmin: xmin, xmax: xmax,
+ dy: dy, ymin: ymin, ymax: ymax,
+ }
+
+ for (i = 0, x = xmin; x < xmax; x += dx, i++) {
+ vertical.push( s.path().attr({
+ strokeWidth: opt.strokeWidth,
+ stroke: opt.stroke[ i % opt.stroke.length ],
+ }) )
+ }
+ for (i = 0, y = ymin; y < ymax; y += dy, i++) {
+ horizontal.push( s.path().attr({
+ strokeWidth: opt.strokeWidth,
+ stroke: opt.stroke[ i % opt.stroke.length ],
+ }) )
+ }
+ if (opt.draw_sides) {
+ sides.push( s.path().attr({ strokeWidth: opt.strokeWidth, stroke: opt.stroke[ ++i % opt.stroke.length ] }) )
+ sides.push( s.path().attr({ strokeWidth: opt.strokeWidth, stroke: opt.stroke[ ++i % opt.stroke.length ] }) )
+ sides.push( s.path().attr({ strokeWidth: opt.strokeWidth, stroke: opt.stroke[ ++i % opt.stroke.length ] }) )
+ sides.push( s.path().attr({ strokeWidth: opt.strokeWidth, stroke: opt.stroke[ ++i % opt.stroke.length ] }) )
+ }
+ }
+
+ Grid.prototype.animate = function(opt, cb){
+ opt = defaults(opt, {
+ h: "right",
+ v: "down",
+ h_offset: 0,
+ v_offset: 0,
+ duration: this.opt.duration,
+ })
+ var duration = opt.duration
+ var height = this.opt.height, width = this.opt.width
+ var x = mod(this.pos.xmin + opt.h_offset, this.pos.dx)
+ var y = mod(this.pos.ymin + opt.v_offset, this.pos.dy)
+ var right = opt.h == "right"
+ var down = opt.v == "down"
+
+ this.vertical.forEach(function(p, i){
+ var start_path, end_path
+ if (down) {
+ start_path = vpath( x, 0, 0 )
+ end_path = vpath( x, 0, height )
+ }
+ else {
+ start_path = vpath( x, height, 0 )
+ end_path = vpath( x, height, -height )
+ }
+ if (duration == 0) {
+ p.attr({ d: end_path })
+ }
+ else {
+ var delay = this.opt.randomizeDelay
+ ? choice(this.opt.delay)
+ : this.opt.delay[i % this.opt.delay.length]
+ p.attr({ d: start_path })
+ setTimeout(function(){
+ p.animate({ d: end_path }, duration)
+ }, delay)
+ }
+ x += this.pos.dx
+ }.bind(this))
+
+ this.horizontal.forEach(function(p, i){
+ var start_path, end_path
+ if (right) {
+ start_path = hpath( 0, y, 0 )
+ end_path = hpath( 0, y, width )
+ }
+ else {
+ start_path = hpath( width, y, 0 )
+ end_path = hpath( width, y, -width )
+ }
+
+ if (duration == 0) {
+ p.attr({ d: end_path })
+ }
+ else {
+ var delay
+ if (this.opt.shuffleDelay) {
+ delay = randrange.apply(null, this.opt.delay)
+ }
+ else if (this.opt.randomizeDelay) {
+ delay = choice(this.opt.delay)
+ }
+ else {
+ delay = this.opt.delay[i % this.opt.delay.length]
+ }
+ p.attr({ d: start_path })
+ setTimeout(function(){
+ p.animate({ d: end_path }, duration)
+ }, delay)
+ }
+ y += this.pos.dy
+ }.bind(this))
+
+ if (this.opt.draw_sides) {
+ this.sides[0].attr({ d: hpath(0, 0, width) })
+ this.sides[1].attr({ d: hpath(0, height - this.opt.strokeWidth/2, width) })
+ this.sides[2].attr({ d: vpath(0, 0, height) })
+ this.sides[3].attr({ d: vpath(width - this.opt.strokeWidth/2, 0, height) })
+ }
+
+ var max_delay = Math.max.apply(Math, this.opt.delay)
+ cb && setTimeout(cb, this.opt.duration + max_delay + 20)
+ }
+
+ function hpath (x, y, w) {
+ return "M" + x + " " + y + "h" + w
+ }
+ function vpath (x, y, w) {
+ return "M" + x + " " + y + "v" + w
+ }
+
+ return Grid
+})()
diff --git a/js/lib/snapmx.js b/js/lib/snapmx.js
new file mode 100644
index 0000000..4deb729
--- /dev/null
+++ b/js/lib/snapmx.js
@@ -0,0 +1,33 @@
+var SnapMX = (function(){
+
+ function SnapMX (w, h) {
+ w = w || 100
+ h = h || 100
+ var snap = this.snap = new Snap (w, h)
+ var el = this.el = snap.node
+ var mx = this.mx = new MX.Object3D (el)
+ scene.add(mx)
+ }
+ SnapMX.prototype.resize = function(w, h){
+ this.snap.attr({
+ width: w,
+ height: h|0,
+ })
+ this.mx.width = w
+ this.mx.height = h
+ }
+ SnapMX.prototype.clear = function(){
+ this.snap.clear()
+ }
+ SnapMX.prototype.show = function(){
+ this.visible = true
+ this.mx.style.display = "block"
+ }
+ SnapMX.prototype.hide = function(){
+ this.visible = false
+ this.mx.style.display = "hide"
+ }
+
+ return SnapMX
+
+})() \ No newline at end of file
diff --git a/js/lib/wavegrid.js b/js/lib/wavegrid.js
new file mode 100644
index 0000000..532bbfc
--- /dev/null
+++ b/js/lib/wavegrid.js
@@ -0,0 +1,136 @@
+var WaveGrid = (function(){
+
+ function WaveGrid (opt){
+ this.opt = defaults(opt, {
+ stroke_width: 2,
+ stroke: "#ffffff",
+ fill: "#000000",
+ wave_spacing: 20,
+ wave_height: 6,
+ wave_height_scale: 4,
+ speed: 15,
+ wave_stagger: 8,
+ smooth_stagger: true,
+ face: null,
+ waves: true,
+ })
+ if (! this.opt.face) {
+ console.error("WaveGrid: please supply a SnapMX face object")
+ }
+ this.loaded = false
+ }
+ WaveGrid.prototype.load = function(data, w, h){
+ this.waves = []
+ this.data = data
+
+ var dx = this.opt.wave_spacing
+ var dy = this.opt.wave_height
+ var top_padding = dy * this.opt.wave_height_scale + this.opt.stroke_width
+ var wave
+ var x, y
+
+ this.w = w
+ this.h = h
+
+ this.opt.face.resize(w + dx*2, h + top_padding + dy*2)
+ this.group = this.opt.face.snap.g()
+ this.group.attr({ transform: "T" + (2.5*dx) + ","+ -top_padding/2 })
+
+ for (y = 0; y < h; y += dy) {
+ wave = this.group.path().attr({
+ strokeWidth: this.opt.stroke_width,
+ stroke: this.opt.stroke,
+ fill: this.opt.fill,
+ })
+ this.waves.push(wave)
+ }
+ this.loaded = true
+ this.update(0)
+ }
+ WaveGrid.prototype.update = function(t){
+ if (! this.loaded) return
+
+ t *= this.opt.speed / 1000
+
+ var wave_height_scale = this.opt.wave_height_scale
+ var dx = this.opt.wave_spacing
+ var dy = this.opt.wave_height
+ var dt = t % dx
+ var dtr = dt/dx
+
+ var pixels = this.data.data
+ var pw = this.data.width
+ var ph = this.data.height
+
+ var w = this.w
+ var xmin = (dx-dt) - dx
+ var xmax = w + (dx*2)
+
+ var wave, path, x, xx, y, yy
+ var at, bt, ay, by, zt, zy
+ var pi, i, j = this.waves.length-1
+ var xstart = 0
+
+ var smooth_stagger = this.opt.smooth_stagger
+ var stagger = this.opt.wave_stagger
+ var stagger_dtr
+
+ var use_waves = this.opt.waves
+
+ for (y = 0; y < this.h; y += dy, j--) {
+ pi = ((y/this.h) * ph)|0
+ wave = this.waves[j]
+ path = "M" + (-dx) + "," + y
+
+ if (smooth_stagger) {
+ dt = (t/stagger + ((j % stagger)/stagger * dx)) % dx
+ dtr = dt/dx
+ xmin = (dx-dt) - dx
+ }
+ else {
+ xstart = stagger == 1 ? 0 : ((j % stagger)/stagger) * dx
+ }
+
+ for (x = xstart; x <= xmax; x += dx) {
+ xx = x + xmin
+
+ jj = clamp( x / w, 0, 1 ) * (pw)
+ jjj = clamp( (x+dx) / w, 0, 1 ) * (pw)
+
+ if (jjj >= pw) {
+ at = ((pi * pw) + pw-2)|0
+ }
+ else {
+ at = ((pi * pw) + jj)|0
+ }
+
+ if (jjj >= pw) {
+ bt = ((pi * pw) + pw - 1)|0
+ }
+ else {
+ bt = ((pi * pw) + jjj)|0
+ }
+
+ ay = (pixels[at]/255 * dy) * (dtr) * wave_height_scale
+ by = (pixels[bt]/255 * dy) * (1-dtr) * wave_height_scale
+ yy = y + (ay + by)
+
+ if (use_waves) {
+ path += "C"
+ path += (xx).toFixed(3) + "," + (y).toFixed(3) + " "
+ path += (xx).toFixed(3) + "," + (yy + (ay+by)).toFixed(3) + " "
+ path += (xx).toFixed(3) + "," + (yy).toFixed(3) + " "
+ }
+ else {
+ path += "M" + (xx - 0.01).toFixed(3) + "," + (y).toFixed(3)
+ path += "L" + (xx).toFixed(3) + "," + (yy).toFixed(3)
+ path += "L" + (xx + 0.01).toFixed(3) + "," + (y).toFixed(3)
+ }
+ }
+ wave.attr({ d: path })
+ }
+ }
+
+ return WaveGrid
+
+})() \ No newline at end of file