This is my open source project that allows creating some incredible wallpapers/web site graphics, feel free to improve on it and use it for commercial or free projects
A Pen by Maksim Surguy on CodePen.
<div id="container" class="container"> | |
<div id="output" class="container"> | |
</div> | |
<ul id="links" class="links"> | |
<p>Press space to drop or pick up the light. Enter key to add another one</p> | |
<li><a class="who" href="https://twitter.com/msurguy" target="_blank">by msurguy</a></li> | |
<li><a class="blog" href="http://maxoffsky.com" target="_blank">Blog post</a></li> | |
<li><a class="source" href="https://github.com/msurguy/triangles" target="_blank">GitHub</a></li> | |
</ul> | |
</div> | |
<div id="controls" class="controls"> | |
</div> |
/** | |
The MIT License (MIT) | |
Copyright (c) 2014 Maksim Surguy | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all | |
copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
SOFTWARE. | |
**/ | |
var Delaunay; | |
(function() { | |
"use strict"; | |
var EPSILON = 1.0 / 1048576.0; | |
function supertriangle(vertices) { | |
var xmin = Number.POSITIVE_INFINITY, | |
ymin = Number.POSITIVE_INFINITY, | |
xmax = Number.NEGATIVE_INFINITY, | |
ymax = Number.NEGATIVE_INFINITY, | |
i, dx, dy, dmax, xmid, ymid; | |
for(i = vertices.length; i--; ) { | |
if(vertices[i][0] < xmin) xmin = vertices[i][0]; | |
if(vertices[i][0] > xmax) xmax = vertices[i][0]; | |
if(vertices[i][1] < ymin) ymin = vertices[i][1]; | |
if(vertices[i][1] > ymax) ymax = vertices[i][1]; | |
} | |
dx = xmax - xmin; | |
dy = ymax - ymin; | |
dmax = Math.max(dx, dy); | |
xmid = xmin + dx * 0.5; | |
ymid = ymin + dy * 0.5; | |
return [ | |
[xmid - 20 * dmax, ymid - dmax], | |
[xmid , ymid + 20 * dmax], | |
[xmid + 20 * dmax, ymid - dmax] | |
]; | |
} | |
function circumcircle(vertices, i, j, k) { | |
var x1 = vertices[i][0], | |
y1 = vertices[i][1], | |
x2 = vertices[j][0], | |
y2 = vertices[j][1], | |
x3 = vertices[k][0], | |
y3 = vertices[k][1], | |
fabsy1y2 = Math.abs(y1 - y2), | |
fabsy2y3 = Math.abs(y2 - y3), | |
xc, yc, m1, m2, mx1, mx2, my1, my2, dx, dy; | |
/* Check for coincident points */ | |
if(fabsy1y2 < EPSILON && fabsy2y3 < EPSILON) | |
throw new Error("Eek! Coincident points!"); | |
if(fabsy1y2 < EPSILON) { | |
m2 = -((x3 - x2) / (y3 - y2)); | |
mx2 = (x2 + x3) / 2.0; | |
my2 = (y2 + y3) / 2.0; | |
xc = (x2 + x1) / 2.0; | |
yc = m2 * (xc - mx2) + my2; | |
} | |
else if(fabsy2y3 < EPSILON) { | |
m1 = -((x2 - x1) / (y2 - y1)); | |
mx1 = (x1 + x2) / 2.0; | |
my1 = (y1 + y2) / 2.0; | |
xc = (x3 + x2) / 2.0; | |
yc = m1 * (xc - mx1) + my1; | |
} | |
else { | |
m1 = -((x2 - x1) / (y2 - y1)); | |
m2 = -((x3 - x2) / (y3 - y2)); | |
mx1 = (x1 + x2) / 2.0; | |
mx2 = (x2 + x3) / 2.0; | |
my1 = (y1 + y2) / 2.0; | |
my2 = (y2 + y3) / 2.0; | |
xc = (m1 * mx1 - m2 * mx2 + my2 - my1) / (m1 - m2); | |
yc = (fabsy1y2 > fabsy2y3) ? | |
m1 * (xc - mx1) + my1 : | |
m2 * (xc - mx2) + my2; | |
} | |
dx = x2 - xc; | |
dy = y2 - yc; | |
return {i: i, j: j, k: k, x: xc, y: yc, r: dx * dx + dy * dy}; | |
} | |
function dedup(edges) { | |
var i, j, a, b, m, n; | |
for(j = edges.length; j; ) { | |
b = edges[--j]; | |
a = edges[--j]; | |
for(i = j; i; ) { | |
n = edges[--i]; | |
m = edges[--i]; | |
if((a === m && b === n) || (a === n && b === m)) { | |
edges.splice(j, 2); | |
edges.splice(i, 2); | |
break; | |
} | |
} | |
} | |
} | |
Delaunay = { | |
triangulate: function(vertices, key) { | |
var n = vertices.length, | |
i, j, indices, st, open, closed, edges, dx, dy, a, b, c; | |
/* Bail if there aren't enough vertices to form any triangles. */ | |
if(n < 3) | |
return []; | |
/* Slice out the actual vertices from the passed objects. (Duplicate the | |
* array even if we don't, though, since we need to make a supertriangle | |
* later on!) */ | |
vertices = vertices.slice(0); | |
if(key) | |
for(i = n; i--; ) | |
vertices[i] = vertices[i][key]; | |
/* Make an array of indices into the vertex array, sorted by the | |
* vertices' x-position. */ | |
indices = new Array(n); | |
for(i = n; i--; ) | |
indices[i] = i; | |
indices.sort(function(i, j) { | |
return vertices[j][0] - vertices[i][0]; | |
}); | |
/* Next, find the vertices of the supertriangle (which contains all other | |
* triangles), and append them onto the end of a (copy of) the vertex | |
* array. */ | |
st = supertriangle(vertices); | |
vertices.push(st[0], st[1], st[2]); | |
/* Initialize the open list (containing the supertriangle and nothing | |
* else) and the closed list (which is empty since we havn't processed | |
* any triangles yet). */ | |
open = [circumcircle(vertices, n + 0, n + 1, n + 2)]; | |
closed = []; | |
edges = []; | |
/* Incrementally add each vertex to the mesh. */ | |
for(i = indices.length; i--; edges.length = 0) { | |
c = indices[i]; | |
/* For each open triangle, check to see if the current point is | |
* inside it's circumcircle. If it is, remove the triangle and add | |
* it's edges to an edge list. */ | |
for(j = open.length; j--; ) { | |
/* If this point is to the right of this triangle's circumcircle, | |
* then this triangle should never get checked again. Remove it | |
* from the open list, add it to the closed list, and skip. */ | |
dx = vertices[c][0] - open[j].x; | |
if(dx > 0.0 && dx * dx > open[j].r) { | |
closed.push(open[j]); | |
open.splice(j, 1); | |
continue; | |
} | |
/* If we're outside the circumcircle, skip this triangle. */ | |
dy = vertices[c][1] - open[j].y; | |
if(dx * dx + dy * dy - open[j].r > EPSILON) | |
continue; | |
/* Remove the triangle and add it's edges to the edge list. */ | |
edges.push( | |
open[j].i, open[j].j, | |
open[j].j, open[j].k, | |
open[j].k, open[j].i | |
); | |
open.splice(j, 1); | |
} | |
/* Remove any doubled edges. */ | |
dedup(edges); | |
/* Add a new triangle for each edge. */ | |
for(j = edges.length; j; ) { | |
b = edges[--j]; | |
a = edges[--j]; | |
open.push(circumcircle(vertices, a, b, c)); | |
} | |
} | |
/* Copy any remaining open triangles to the closed list, and then | |
* remove any triangles that share a vertex with the supertriangle, | |
* building a list of triplets that represent triangles. */ | |
for(i = open.length; i--; ) | |
closed.push(open[i]); | |
open.length = 0; | |
for(i = closed.length; i--; ) | |
if(closed[i].i < n && closed[i].j < n && closed[i].k < n) | |
open.push(closed[i].i, closed[i].j, closed[i].k); | |
/* Yay, we're done! */ | |
return open; | |
}, | |
contains: function(tri, p) { | |
/* Bounding box test first, for quick rejections. */ | |
if((p[0] < tri[0][0] && p[0] < tri[1][0] && p[0] < tri[2][0]) || | |
(p[0] > tri[0][0] && p[0] > tri[1][0] && p[0] > tri[2][0]) || | |
(p[1] < tri[0][1] && p[1] < tri[1][1] && p[1] < tri[2][1]) || | |
(p[1] > tri[0][1] && p[1] > tri[1][1] && p[1] > tri[2][1])) | |
return null; | |
var a = tri[1][0] - tri[0][0], | |
b = tri[2][0] - tri[0][0], | |
c = tri[1][1] - tri[0][1], | |
d = tri[2][1] - tri[0][1], | |
i = a * d - b * c; | |
/* Degenerate tri. */ | |
if(i === 0.0) | |
return null; | |
var u = (d * (p[0] - tri[0][0]) - b * (p[1] - tri[0][1])) / i, | |
v = (a * (p[1] - tri[0][1]) - c * (p[0] - tri[0][0])) / i; | |
/* If we're outside the tri, fail. */ | |
if(u < 0.0 || v < 0.0 || (u + v) > 1.0) | |
return null; | |
return [u, v]; | |
} | |
}; | |
if(typeof module !== "undefined") | |
module.exports = Delaunay; | |
})(); | |
/* mousetrap v1.4.6 craig.is/killing/mice */ | |
(function(J,r,f){function s(a,b,d){a.addEventListener?a.addEventListener(b,d,!1):a.attachEvent("on"+b,d)}function A(a){if("keypress"==a.type){var b=String.fromCharCode(a.which);a.shiftKey||(b=b.toLowerCase());return b}return h[a.which]?h[a.which]:B[a.which]?B[a.which]:String.fromCharCode(a.which).toLowerCase()}function t(a){a=a||{};var b=!1,d;for(d in n)a[d]?b=!0:n[d]=0;b||(u=!1)}function C(a,b,d,c,e,v){var g,k,f=[],h=d.type;if(!l[a])return[];"keyup"==h&&w(a)&&(b=[a]);for(g=0;g<l[a].length;++g)if(k= | |
l[a][g],!(!c&&k.seq&&n[k.seq]!=k.level||h!=k.action||("keypress"!=h||d.metaKey||d.ctrlKey)&&b.sort().join(",")!==k.modifiers.sort().join(","))){var m=c&&k.seq==c&&k.level==v;(!c&&k.combo==e||m)&&l[a].splice(g,1);f.push(k)}return f}function K(a){var b=[];a.shiftKey&&b.push("shift");a.altKey&&b.push("alt");a.ctrlKey&&b.push("ctrl");a.metaKey&&b.push("meta");return b}function x(a,b,d,c){m.stopCallback(b,b.target||b.srcElement,d,c)||!1!==a(b,d)||(b.preventDefault?b.preventDefault():b.returnValue=!1,b.stopPropagation? | |
b.stopPropagation():b.cancelBubble=!0)}function y(a){"number"!==typeof a.which&&(a.which=a.keyCode);var b=A(a);b&&("keyup"==a.type&&z===b?z=!1:m.handleKey(b,K(a),a))}function w(a){return"shift"==a||"ctrl"==a||"alt"==a||"meta"==a}function L(a,b,d,c){function e(b){return function(){u=b;++n[a];clearTimeout(D);D=setTimeout(t,1E3)}}function v(b){x(d,b,a);"keyup"!==c&&(z=A(b));setTimeout(t,10)}for(var g=n[a]=0;g<b.length;++g){var f=g+1===b.length?v:e(c||E(b[g+1]).action);F(b[g],f,c,a,g)}}function E(a,b){var d, | |
c,e,f=[];d="+"===a?["+"]:a.split("+");for(e=0;e<d.length;++e)c=d[e],G[c]&&(c=G[c]),b&&"keypress"!=b&&H[c]&&(c=H[c],f.push("shift")),w(c)&&f.push(c);d=c;e=b;if(!e){if(!p){p={};for(var g in h)95<g&&112>g||h.hasOwnProperty(g)&&(p[h[g]]=g)}e=p[d]?"keydown":"keypress"}"keypress"==e&&f.length&&(e="keydown");return{key:c,modifiers:f,action:e}}function F(a,b,d,c,e){q[a+":"+d]=b;a=a.replace(/\s+/g," ");var f=a.split(" ");1<f.length?L(a,f,b,d):(d=E(a,d),l[d.key]=l[d.key]||[],C(d.key,d.modifiers,{type:d.action}, | |
c,a,e),l[d.key][c?"unshift":"push"]({callback:b,modifiers:d.modifiers,action:d.action,seq:c,level:e,combo:a}))}var h={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"ins",46:"del",91:"meta",93:"meta",224:"meta"},B={106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},H={"~":"`","!":"1", | |
"@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"},G={option:"alt",command:"meta","return":"enter",escape:"esc",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},p,l={},q={},n={},D,z=!1,I=!1,u=!1;for(f=1;20>f;++f)h[111+f]="f"+f;for(f=0;9>=f;++f)h[f+96]=f;s(r,"keypress",y);s(r,"keydown",y);s(r,"keyup",y);var m={bind:function(a,b,d){a=a instanceof Array?a:[a];for(var c=0;c<a.length;++c)F(a[c],b,d);return this}, | |
unbind:function(a,b){return m.bind(a,function(){},b)},trigger:function(a,b){if(q[a+":"+b])q[a+":"+b]({},a);return this},reset:function(){l={};q={};return this},stopCallback:function(a,b){return-1<(" "+b.className+" ").indexOf(" mousetrap ")?!1:"INPUT"==b.tagName||"SELECT"==b.tagName||"TEXTAREA"==b.tagName||b.isContentEditable},handleKey:function(a,b,d){var c=C(a,b,d),e;b={};var f=0,g=!1;for(e=0;e<c.length;++e)c[e].seq&&(f=Math.max(f,c[e].level));for(e=0;e<c.length;++e)c[e].seq?c[e].level==f&&(g=!0, | |
b[c[e].seq]=1,x(c[e].callback,d,c[e].combo,c[e].seq)):g||x(c[e].callback,d,c[e].combo);c="keypress"==d.type&&I;d.type!=u||w(a)||c||t(b);I=g&&"keydown"==d.type}};J.Mousetrap=m;"function"===typeof define&&define.amd&&define(m)})(window,document); | |
/** | |
* StyleFix 1.0.3 & PrefixFree 1.0.7 | |
* @author Lea Verou | |
* MIT license | |
*/(function(){function t(e,t){return[].slice.call((t||document).querySelectorAll(e))}if(!window.addEventListener)return;var e=window.StyleFix={link:function(t){try{if(t.rel!=="stylesheet"||t.hasAttribute("data-noprefix"))return}catch(n){return}var r=t.href||t.getAttribute("data-href"),i=r.replace(/[^\/]+$/,""),s=(/^[a-z]{3,10}:/.exec(i)||[""])[0],o=(/^[a-z]{3,10}:\/\/[^\/]+/.exec(i)||[""])[0],u=/^([^?]*)\??/.exec(r)[1],a=t.parentNode,f=new XMLHttpRequest,l;f.onreadystatechange=function(){f.readyState===4&&l()};l=function(){var n=f.responseText;if(n&&t.parentNode&&(!f.status||f.status<400||f.status>600)){n=e.fix(n,!0,t);if(i){n=n.replace(/url\(\s*?((?:"|')?)(.+?)\1\s*?\)/gi,function(e,t,n){return/^([a-z]{3,10}:|#)/i.test(n)?e:/^\/\//.test(n)?'url("'+s+n+'")':/^\//.test(n)?'url("'+o+n+'")':/^\?/.test(n)?'url("'+u+n+'")':'url("'+i+n+'")'});var r=i.replace(/([\\\^\$*+[\]?{}.=!:(|)])/g,"\\$1");n=n.replace(RegExp("\\b(behavior:\\s*?url\\('?\"?)"+r,"gi"),"$1")}var l=document.createElement("style");l.textContent=n;l.media=t.media;l.disabled=t.disabled;l.setAttribute("data-href",t.getAttribute("href"));a.insertBefore(l,t);a.removeChild(t);l.media=t.media}};try{f.open("GET",r);f.send(null)}catch(n){if(typeof XDomainRequest!="undefined"){f=new XDomainRequest;f.onerror=f.onprogress=function(){};f.onload=l;f.open("GET",r);f.send(null)}}t.setAttribute("data-inprogress","")},styleElement:function(t){if(t.hasAttribute("data-noprefix"))return;var n=t.disabled;t.textContent=e.fix(t.textContent,!0,t);t.disabled=n},styleAttribute:function(t){var n=t.getAttribute("style");n=e.fix(n,!1,t);t.setAttribute("style",n)},process:function(){t('link[rel="stylesheet"]:not([data-inprogress])').forEach(StyleFix.link);t("style").forEach(StyleFix.styleElement);t("[style]").forEach(StyleFix.styleAttribute)},register:function(t,n){(e.fixers=e.fixers||[]).splice(n===undefined?e.fixers.length:n,0,t)},fix:function(t,n,r){for(var i=0;i<e.fixers.length;i++)t=e.fixers[i](t,n,r)||t;return t},camelCase:function(e){return e.replace(/-([a-z])/g,function(e,t){return t.toUpperCase()}).replace("-","")},deCamelCase:function(e){return e.replace(/[A-Z]/g,function(e){return"-"+e.toLowerCase()})}};(function(){setTimeout(function(){t('link[rel="stylesheet"]').forEach(StyleFix.link)},10);document.addEventListener("DOMContentLoaded",StyleFix.process,!1)})()})();(function(e){function t(e,t,r,i,s){e=n[e];if(e.length){var o=RegExp(t+"("+e.join("|")+")"+r,"gi");s=s.replace(o,i)}return s}if(!window.StyleFix||!window.getComputedStyle)return;var n=window.PrefixFree={prefixCSS:function(e,r,i){var s=n.prefix;n.functions.indexOf("linear-gradient")>-1&&(e=e.replace(/(\s|:|,)(repeating-)?linear-gradient\(\s*(-?\d*\.?\d*)deg/ig,function(e,t,n,r){return t+(n||"")+"linear-gradient("+(90-r)+"deg"}));e=t("functions","(\\s|:|,)","\\s*\\(","$1"+s+"$2(",e);e=t("keywords","(\\s|:)","(\\s|;|\\}|$)","$1"+s+"$2$3",e);e=t("properties","(^|\\{|\\s|;)","\\s*:","$1"+s+"$2:",e);if(n.properties.length){var o=RegExp("\\b("+n.properties.join("|")+")(?!:)","gi");e=t("valueProperties","\\b",":(.+?);",function(e){return e.replace(o,s+"$1")},e)}if(r){e=t("selectors","","\\b",n.prefixSelector,e);e=t("atrules","@","\\b","@"+s+"$1",e)}e=e.replace(RegExp("-"+s,"g"),"-");e=e.replace(/-\*-(?=[a-z]+)/gi,n.prefix);return e},property:function(e){return(n.properties.indexOf(e)?n.prefix:"")+e},value:function(e,r){e=t("functions","(^|\\s|,)","\\s*\\(","$1"+n.prefix+"$2(",e);e=t("keywords","(^|\\s)","(\\s|$)","$1"+n.prefix+"$2$3",e);return e},prefixSelector:function(e){return e.replace(/^:{1,2}/,function(e){return e+n.prefix})},prefixProperty:function(e,t){var r=n.prefix+e;return t?StyleFix.camelCase(r):r}};(function(){var e={},t=[],r={},i=getComputedStyle(document.documentElement,null),s=document.createElement("div").style,o=function(n){if(n.charAt(0)==="-"){t.push(n);var r=n.split("-"),i=r[1];e[i]=++e[i]||1;while(r.length>3){r.pop();var s=r.join("-");u(s)&&t.indexOf(s)===-1&&t.push(s)}}},u=function(e){return StyleFix.camelCase(e)in s};if(i.length>0)for(var a=0;a<i.length;a++)o(i[a]);else for(var f in i)o(StyleFix.deCamelCase(f));var l={uses:0};for(var c in e){var h=e[c];l.uses<h&&(l={prefix:c,uses:h})}n.prefix="-"+l.prefix+"-";n.Prefix=StyleFix.camelCase(n.prefix);n.properties=[];for(var a=0;a<t.length;a++){var f=t[a];if(f.indexOf(n.prefix)===0){var p=f.slice(n.prefix.length);u(p)||n.properties.push(p)}}n.Prefix=="Ms"&&!("transform"in s)&&!("MsTransform"in s)&&"msTransform"in s&&n.properties.push("transform","transform-origin");n.properties.sort()})();(function(){function i(e,t){r[t]="";r[t]=e;return!!r[t]}var e={"linear-gradient":{property:"backgroundImage",params:"red, teal"},calc:{property:"width",params:"1px + 5%"},element:{property:"backgroundImage",params:"#foo"},"cross-fade":{property:"backgroundImage",params:"url(a.png), url(b.png), 50%"}};e["repeating-linear-gradient"]=e["repeating-radial-gradient"]=e["radial-gradient"]=e["linear-gradient"];var t={initial:"color","zoom-in":"cursor","zoom-out":"cursor",box:"display",flexbox:"display","inline-flexbox":"display",flex:"display","inline-flex":"display",grid:"display","inline-grid":"display","min-content":"width"};n.functions=[];n.keywords=[];var r=document.createElement("div").style;for(var s in e){var o=e[s],u=o.property,a=s+"("+o.params+")";!i(a,u)&&i(n.prefix+a,u)&&n.functions.push(s)}for(var f in t){var u=t[f];!i(f,u)&&i(n.prefix+f,u)&&n.keywords.push(f)}})();(function(){function s(e){i.textContent=e+"{}";return!!i.sheet.cssRules.length}var t={":read-only":null,":read-write":null,":any-link":null,"::selection":null},r={keyframes:"name",viewport:null,document:'regexp(".")'};n.selectors=[];n.atrules=[];var i=e.appendChild(document.createElement("style"));for(var o in t){var u=o+(t[o]?"("+t[o]+")":"");!s(u)&&s(n.prefixSelector(u))&&n.selectors.push(o)}for(var a in r){var u=a+" "+(r[a]||"");!s("@"+u)&&s("@"+n.prefix+u)&&n.atrules.push(a)}e.removeChild(i)})();n.valueProperties=["transition","transition-property"];e.className+=" "+n.prefix;StyleFix.register(n.prefixCSS)})(document.documentElement); | |
/** | |
* Defines the Flat Surface Shader namespace for all the awesomeness to exist upon. | |
* @author Matthew Wagerfield | |
*/ | |
FSS = { | |
FRONT : 0, | |
BACK : 1, | |
DOUBLE : 2, | |
SVGNS : 'http://www.w3.org/2000/svg' | |
}; | |
/** | |
* @class Array | |
* @author Matthew Wagerfield | |
*/ | |
FSS.Array = typeof Float32Array === 'function' ? Float32Array : Array; | |
/** | |
* @class Utils | |
* @author Matthew Wagerfield | |
*/ | |
FSS.Utils = { | |
isNumber: function(value) { | |
return !isNaN(parseFloat(value)) && isFinite(value); | |
} | |
}; | |
/** | |
* Request Animation Frame Polyfill. | |
* @author Paul Irish | |
* @see https://gist.github.com/paulirish/1579671 | |
*/ | |
(function() { | |
var lastTime = 0; | |
var vendors = ['ms', 'moz', 'webkit', 'o']; | |
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { | |
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; | |
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; | |
} | |
if (!window.requestAnimationFrame) { | |
window.requestAnimationFrame = function(callback, element) { | |
var currentTime = new Date().getTime(); | |
var timeToCall = Math.max(0, 16 - (currentTime - lastTime)); | |
var id = window.setTimeout(function() { | |
callback(currentTime + timeToCall); | |
}, timeToCall); | |
lastTime = currentTime + timeToCall; | |
return id; | |
}; | |
} | |
if (!window.cancelAnimationFrame) { | |
window.cancelAnimationFrame = function(id) { | |
clearTimeout(id); | |
}; | |
} | |
}()); | |
/** | |
* @object Math Augmentation | |
* @author Matthew Wagerfield | |
*/ | |
Math.PIM2 = Math.PI*2; | |
Math.PID2 = Math.PI/2; | |
Math.randomInRange = function(min, max) { | |
return min + (max - min) * Math.random(); | |
}; | |
Math.clamp = function(value, min, max) { | |
value = Math.max(value, min); | |
value = Math.min(value, max); | |
return value; | |
}; | |
/** | |
* @object Vector3 | |
* @author Matthew Wagerfield | |
*/ | |
FSS.Vector3 = { | |
create: function(x, y, z) { | |
var vector = new FSS.Array(3); | |
this.set(vector, x, y, z); | |
return vector; | |
}, | |
clone: function(a) { | |
var vector = this.create(); | |
this.copy(vector, a); | |
return vector; | |
}, | |
set: function(target, x, y, z) { | |
target[0] = x || 0; | |
target[1] = y || 0; | |
target[2] = z || 0; | |
return this; | |
}, | |
setX: function(target, x) { | |
target[0] = x || 0; | |
return this; | |
}, | |
setY: function(target, y) { | |
target[1] = y || 0; | |
return this; | |
}, | |
setZ: function(target, z) { | |
target[2] = z || 0; | |
return this; | |
}, | |
copy: function(target, a) { | |
target[0] = a[0]; | |
target[1] = a[1]; | |
target[2] = a[2]; | |
return this; | |
}, | |
add: function(target, a) { | |
target[0] += a[0]; | |
target[1] += a[1]; | |
target[2] += a[2]; | |
return this; | |
}, | |
addVectors: function(target, a, b) { | |
target[0] = a[0] + b[0]; | |
target[1] = a[1] + b[1]; | |
target[2] = a[2] + b[2]; | |
return this; | |
}, | |
addScalar: function(target, s) { | |
target[0] += s; | |
target[1] += s; | |
target[2] += s; | |
return this; | |
}, | |
subtract: function(target, a) { | |
target[0] -= a[0]; | |
target[1] -= a[1]; | |
target[2] -= a[2]; | |
return this; | |
}, | |
subtractVectors: function(target, a, b) { | |
target[0] = a[0] - b[0]; | |
target[1] = a[1] - b[1]; | |
target[2] = a[2] - b[2]; | |
return this; | |
}, | |
subtractScalar: function(target, s) { | |
target[0] -= s; | |
target[1] -= s; | |
target[2] -= s; | |
return this; | |
}, | |
multiply: function(target, a) { | |
target[0] *= a[0]; | |
target[1] *= a[1]; | |
target[2] *= a[2]; | |
return this; | |
}, | |
multiplyVectors: function(target, a, b) { | |
target[0] = a[0] * b[0]; | |
target[1] = a[1] * b[1]; | |
target[2] = a[2] * b[2]; | |
return this; | |
}, | |
multiplyScalar: function(target, s) { | |
target[0] *= s; | |
target[1] *= s; | |
target[2] *= s; | |
return this; | |
}, | |
divide: function(target, a) { | |
target[0] /= a[0]; | |
target[1] /= a[1]; | |
target[2] /= a[2]; | |
return this; | |
}, | |
divideVectors: function(target, a, b) { | |
target[0] = a[0] / b[0]; | |
target[1] = a[1] / b[1]; | |
target[2] = a[2] / b[2]; | |
return this; | |
}, | |
divideScalar: function(target, s) { | |
if (s !== 0) { | |
target[0] /= s; | |
target[1] /= s; | |
target[2] /= s; | |
} else { | |
target[0] = 0; | |
target[1] = 0; | |
target[2] = 0; | |
} | |
return this; | |
}, | |
cross: function(target, a) { | |
var x = target[0]; | |
var y = target[1]; | |
var z = target[2]; | |
target[0] = y*a[2] - z*a[1]; | |
target[1] = z*a[0] - x*a[2]; | |
target[2] = x*a[1] - y*a[0]; | |
return this; | |
}, | |
crossVectors: function(target, a, b) { | |
target[0] = a[1]*b[2] - a[2]*b[1]; | |
target[1] = a[2]*b[0] - a[0]*b[2]; | |
target[2] = a[0]*b[1] - a[1]*b[0]; | |
return this; | |
}, | |
min: function(target, value) { | |
if (target[0] < value) { target[0] = value; } | |
if (target[1] < value) { target[1] = value; } | |
if (target[2] < value) { target[2] = value; } | |
return this; | |
}, | |
max: function(target, value) { | |
if (target[0] > value) { target[0] = value; } | |
if (target[1] > value) { target[1] = value; } | |
if (target[2] > value) { target[2] = value; } | |
return this; | |
}, | |
clamp: function(target, min, max) { | |
this.min(target, min); | |
this.max(target, max); | |
return this; | |
}, | |
limit: function(target, min, max) { | |
var length = this.length(target); | |
if (min !== null && length < min) { | |
this.setLength(target, min); | |
} else if (max !== null && length > max) { | |
this.setLength(target, max); | |
} | |
return this; | |
}, | |
dot: function(a, b) { | |
return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; | |
}, | |
normalise: function(target) { | |
return this.divideScalar(target, this.length(target)); | |
}, | |
negate: function(target) { | |
return this.multiplyScalar(target, -1); | |
}, | |
distanceSquared: function(a, b) { | |
var dx = a[0] - b[0]; | |
var dy = a[1] - b[1]; | |
var dz = a[2] - b[2]; | |
return dx*dx + dy*dy + dz*dz; | |
}, | |
distance: function(a, b) { | |
return Math.sqrt(this.distanceSquared(a, b)); | |
}, | |
lengthSquared: function(a) { | |
return a[0]*a[0] + a[1]*a[1] + a[2]*a[2]; | |
}, | |
length: function(a) { | |
return Math.sqrt(this.lengthSquared(a)); | |
}, | |
setLength: function(target, l) { | |
var length = this.length(target); | |
if (length !== 0 && l !== length) { | |
this.multiplyScalar(target, l / length); | |
} | |
return this; | |
} | |
}; | |
/** | |
* @object Vector4 | |
* @author Matthew Wagerfield | |
*/ | |
FSS.Vector4 = { | |
create: function(x, y, z, w) { | |
var vector = new FSS.Array(4); | |
this.set(vector, x, y, z); | |
return vector; | |
}, | |
set: function(target, x, y, z, w) { | |
target[0] = x || 0; | |
target[1] = y || 0; | |
target[2] = z || 0; | |
target[3] = w || 0; | |
return this; | |
}, | |
setX: function(target, x) { | |
target[0] = x || 0; | |
return this; | |
}, | |
setY: function(target, y) { | |
target[1] = y || 0; | |
return this; | |
}, | |
setZ: function(target, z) { | |
target[2] = z || 0; | |
return this; | |
}, | |
setW: function(target, w) { | |
target[3] = w || 0; | |
return this; | |
}, | |
add: function(target, a) { | |
target[0] += a[0]; | |
target[1] += a[1]; | |
target[2] += a[2]; | |
target[3] += a[3]; | |
return this; | |
}, | |
multiplyVectors: function(target, a, b) { | |
target[0] = a[0] * b[0]; | |
target[1] = a[1] * b[1]; | |
target[2] = a[2] * b[2]; | |
target[3] = a[3] * b[3]; | |
return this; | |
}, | |
multiplyScalar: function(target, s) { | |
target[0] *= s; | |
target[1] *= s; | |
target[2] *= s; | |
target[3] *= s; | |
return this; | |
}, | |
min: function(target, value) { | |
if (target[0] < value) { target[0] = value; } | |
if (target[1] < value) { target[1] = value; } | |
if (target[2] < value) { target[2] = value; } | |
if (target[3] < value) { target[3] = value; } | |
return this; | |
}, | |
max: function(target, value) { | |
if (target[0] > value) { target[0] = value; } | |
if (target[1] > value) { target[1] = value; } | |
if (target[2] > value) { target[2] = value; } | |
if (target[3] > value) { target[3] = value; } | |
return this; | |
}, | |
clamp: function(target, min, max) { | |
this.min(target, min); | |
this.max(target, max); | |
return this; | |
} | |
}; | |
/** | |
* @class Color | |
* @author Matthew Wagerfield | |
*/ | |
FSS.Color = function(hex, opacity) { | |
this.rgba = FSS.Vector4.create(); | |
this.hex = hex || '#000000'; | |
this.opacity = FSS.Utils.isNumber(opacity) ? opacity : 1; | |
this.set(this.hex, this.opacity); | |
}; | |
FSS.Color.prototype = { | |
set: function(hex, opacity) { | |
hex = hex.replace('#', ''); | |
var size = hex.length / 3; | |
this.rgba[0] = parseInt(hex.substring(size*0, size*1), 16) / 255; | |
this.rgba[1] = parseInt(hex.substring(size*1, size*2), 16) / 255; | |
this.rgba[2] = parseInt(hex.substring(size*2, size*3), 16) / 255; | |
this.rgba[3] = FSS.Utils.isNumber(opacity) ? opacity : this.rgba[3]; | |
return this; | |
}, | |
hexify: function(channel) { | |
var hex = Math.ceil(channel*255).toString(16); | |
if (hex.length === 1) { hex = '0' + hex; } | |
return hex; | |
}, | |
format: function() { | |
var r = this.hexify(this.rgba[0]); | |
var g = this.hexify(this.rgba[1]); | |
var b = this.hexify(this.rgba[2]); | |
this.hex = '#' + r + g + b; | |
return this.hex; | |
} | |
}; | |
/** | |
* @class Object | |
* @author Matthew Wagerfield | |
*/ | |
FSS.Object = function() { | |
this.position = FSS.Vector3.create(); | |
}; | |
FSS.Object.prototype = { | |
setPosition: function(x, y, z) { | |
FSS.Vector3.set(this.position, x, y, z); | |
return this; | |
} | |
}; | |
/** | |
* @class Light | |
* @author Matthew Wagerfield | |
*/ | |
FSS.Light = function(ambient, diffuse) { | |
FSS.Object.call(this); | |
this.ambient = new FSS.Color(ambient || '#FFFFFF'); | |
this.diffuse = new FSS.Color(diffuse || '#FFFFFF'); | |
this.ray = FSS.Vector3.create(); | |
}; | |
FSS.Light.prototype = Object.create(FSS.Object.prototype); | |
/** | |
* @class Vertex | |
* @author Matthew Wagerfield | |
*/ | |
FSS.Vertex = function(x, y, z) { | |
this.position = FSS.Vector3.create(x, y, z); | |
}; | |
FSS.Vertex.prototype = { | |
setPosition: function(x, y, z) { | |
FSS.Vector3.set(this.position, x, y, z); | |
return this; | |
} | |
}; | |
/** | |
* @class Triangle | |
* @author Matthew Wagerfield | |
*/ | |
FSS.Triangle = function(a, b, c) { | |
this.a = a || new FSS.Vertex(); | |
this.b = b || new FSS.Vertex(); | |
this.c = c || new FSS.Vertex(); | |
this.vertices = [this.a, this.b, this.c]; | |
this.u = FSS.Vector3.create(); | |
this.v = FSS.Vector3.create(); | |
this.centroid = FSS.Vector3.create(); | |
this.normal = FSS.Vector3.create(); | |
this.color = new FSS.Color(); | |
this.polygon = document.createElementNS(FSS.SVGNS, 'polygon'); | |
this.polygon.setAttributeNS(null, 'stroke-linejoin', 'round'); | |
this.polygon.setAttributeNS(null, 'stroke-miterlimit', '1'); | |
this.polygon.setAttributeNS(null, 'stroke-width', '1'); | |
this.computeCentroid(); | |
this.computeNormal(); | |
}; | |
FSS.Triangle.prototype = { | |
computeCentroid: function() { | |
this.centroid[0] = this.a.position[0] + this.b.position[0] + this.c.position[0]; | |
this.centroid[1] = this.a.position[1] + this.b.position[1] + this.c.position[1]; | |
this.centroid[2] = this.a.position[2] + this.b.position[2] + this.c.position[2]; | |
FSS.Vector3.divideScalar(this.centroid, 3); | |
return this; | |
}, | |
computeNormal: function() { | |
FSS.Vector3.subtractVectors(this.u, this.b.position, this.a.position); | |
FSS.Vector3.subtractVectors(this.v, this.c.position, this.a.position); | |
FSS.Vector3.crossVectors(this.normal, this.u, this.v); | |
FSS.Vector3.normalise(this.normal); | |
return this; | |
} | |
}; | |
/** | |
* @class Geometry | |
* @author Matthew Wagerfield | |
*/ | |
FSS.Geometry = function() { | |
this.vertices = []; | |
this.triangles = []; | |
this.dirty = false; | |
}; | |
FSS.Geometry.prototype = { | |
update: function() { | |
if (this.dirty) { | |
var t,triangle; | |
for (t = this.triangles.length - 1; t >= 0; t--) { | |
triangle = this.triangles[t]; | |
triangle.computeCentroid(); | |
triangle.computeNormal(); | |
} | |
this.dirty = false; | |
} | |
return this; | |
} | |
}; | |
/** | |
* @class Plane | |
* @author Matthew Wagerfield, modified by Maksim Surguy to implement Delaunay triangulation | |
*/ | |
FSS.Plane = function(width, height, howmany) { | |
FSS.Geometry.call(this); | |
this.width = width || 100; | |
this.height = height || 100; | |
// Cache Variables | |
var x, y, vertices = new Array(howmany); | |
offsetX = this.width * -0.5, | |
offsetY = this.height * 0.5; | |
for(i = vertices.length; i--; ) { | |
x = offsetX + Math.random()*width; | |
y = offsetY - Math.random()*height; | |
vertices[i] = [x, y]; | |
} | |
// Generate additional points on the perimeter so that there are no holes in the pattern | |
vertices.push([offsetX, offsetY]); | |
vertices.push([offsetX + width/2, offsetY]); | |
vertices.push([offsetX + width, offsetY]); | |
vertices.push([offsetX + width, offsetY - height/2]); | |
vertices.push([offsetX + width, offsetY - height]); | |
vertices.push([offsetX + width/2, offsetY - height]); | |
vertices.push([offsetX, offsetY - height]); | |
vertices.push([offsetX, offsetY - height/2]); | |
// Generate additional randomly placed points on the perimeter | |
for (var i = 6; i >= 0; i--) { | |
vertices.push([ offsetX + Math.random()*width, offsetY]); | |
vertices.push([ offsetX, offsetY - Math.random()*height]); | |
vertices.push([ offsetX + width, offsetY - Math.random()*height]); | |
vertices.push([ offsetX + Math.random()*width, offsetY-height]); | |
} | |
// Create an array of triangulated coordinates from our vertices | |
var triangles = Delaunay.triangulate(vertices); | |
for(i = triangles.length; i; ) { | |
--i; | |
var p1 = [Math.ceil(vertices[triangles[i]][0]), Math.ceil(vertices[triangles[i]][1])]; | |
--i; | |
var p2 = [Math.ceil(vertices[triangles[i]][0]), Math.ceil(vertices[triangles[i]][1])]; | |
--i; | |
var p3 = [Math.ceil(vertices[triangles[i]][0]), Math.ceil(vertices[triangles[i]][1])]; | |
t1 = new FSS.Triangle(new FSS.Vertex(p1[0],p1[1]), new FSS.Vertex(p2[0],p2[1]), new FSS.Vertex(p3[0],p3[1])); | |
this.triangles.push(t1); | |
} | |
}; | |
FSS.Plane.prototype = Object.create(FSS.Geometry.prototype); | |
/** | |
* @class Material | |
* @author Matthew Wagerfield | |
*/ | |
FSS.Material = function(ambient, diffuse) { | |
this.ambient = new FSS.Color(ambient || '#444444'); | |
this.diffuse = new FSS.Color(diffuse || '#FFFFFF'); | |
this.slave = new FSS.Color(); | |
}; | |
/** | |
* @class Mesh | |
* @author Matthew Wagerfield | |
*/ | |
FSS.Mesh = function(geometry, material) { | |
FSS.Object.call(this); | |
this.geometry = geometry || new FSS.Geometry(); | |
this.material = material || new FSS.Material(); | |
this.side = FSS.FRONT; | |
this.visible = true; | |
}; | |
FSS.Mesh.prototype = Object.create(FSS.Object.prototype); | |
FSS.Mesh.prototype.update = function(lights, calculate) { | |
var t,triangle, l,light, illuminance; | |
// Update Geometry | |
this.geometry.update(); | |
// Calculate the triangle colors | |
if (calculate) { | |
// Iterate through Triangles | |
for (t = this.geometry.triangles.length - 1; t >= 0; t--) { | |
triangle = this.geometry.triangles[t]; | |
// Reset Triangle Color | |
FSS.Vector4.set(triangle.color.rgba); | |
// Iterate through Lights | |
for (l = lights.length - 1; l >= 0; l--) { | |
light = lights[l]; | |
// Calculate Illuminance | |
FSS.Vector3.subtractVectors(light.ray, light.position, triangle.centroid); | |
FSS.Vector3.normalise(light.ray); | |
illuminance = FSS.Vector3.dot(triangle.normal, light.ray); | |
if (this.side === FSS.FRONT) { | |
illuminance = Math.max(illuminance, 0); | |
} else if (this.side === FSS.BACK) { | |
illuminance = Math.abs(Math.min(illuminance, 0)); | |
} else if (this.side === FSS.DOUBLE) { | |
illuminance = Math.max(Math.abs(illuminance), 0); | |
} | |
// Calculate Ambient Light | |
FSS.Vector4.multiplyVectors(this.material.slave.rgba, this.material.ambient.rgba, light.ambient.rgba); | |
FSS.Vector4.add(triangle.color.rgba, this.material.slave.rgba); | |
// Calculate Diffuse Light | |
FSS.Vector4.multiplyVectors(this.material.slave.rgba, this.material.diffuse.rgba, light.diffuse.rgba); | |
FSS.Vector4.multiplyScalar(this.material.slave.rgba, illuminance); | |
FSS.Vector4.add(triangle.color.rgba, this.material.slave.rgba); | |
} | |
// Clamp & Format Color | |
FSS.Vector4.clamp(triangle.color.rgba, 0, 1); | |
} | |
} | |
return this; | |
}; | |
/** | |
* @class Scene | |
* @author Matthew Wagerfield | |
*/ | |
FSS.Scene = function() { | |
this.meshes = []; | |
this.lights = []; | |
}; | |
FSS.Scene.prototype = { | |
add: function(object) { | |
if (object instanceof FSS.Mesh && !~this.meshes.indexOf(object)) { | |
this.meshes.push(object); | |
} else if (object instanceof FSS.Light && !~this.lights.indexOf(object)) { | |
this.lights.push(object); | |
} | |
return this; | |
}, | |
remove: function(object) { | |
if (object instanceof FSS.Mesh && ~this.meshes.indexOf(object)) { | |
this.meshes.splice(this.meshes.indexOf(object), 1); | |
} else if (object instanceof FSS.Light && ~this.lights.indexOf(object)) { | |
this.lights.splice(this.lights.indexOf(object), 1); | |
} | |
return this; | |
} | |
}; | |
/** | |
* @class Renderer | |
* @author Matthew Wagerfield | |
*/ | |
FSS.Renderer = function() { | |
this.width = 0; | |
this.height = 0; | |
this.halfWidth = 0; | |
this.halfHeight = 0; | |
}; | |
FSS.Renderer.prototype = { | |
setSize: function(width, height) { | |
if (this.width === width && this.height === height) return; | |
this.width = width; | |
this.height = height; | |
this.halfWidth = this.width * 0.5; | |
this.halfHeight = this.height * 0.5; | |
return this; | |
}, | |
clear: function() { | |
return this; | |
}, | |
render: function(scene) { | |
return this; | |
} | |
}; | |
/** | |
* @class Canvas Renderer | |
* @author Matthew Wagerfield | |
*/ | |
FSS.CanvasRenderer = function() { | |
FSS.Renderer.call(this); | |
this.element = document.createElement('canvas'); | |
this.element.style.display = 'block'; | |
this.context = this.element.getContext('2d'); | |
this.setSize(this.element.width, this.element.height); | |
}; | |
FSS.CanvasRenderer.prototype = Object.create(FSS.Renderer.prototype); | |
FSS.CanvasRenderer.prototype.setSize = function(width, height) { | |
FSS.Renderer.prototype.setSize.call(this, width, height); | |
this.element.width = width; | |
this.element.height = height; | |
this.context.setTransform(1, 0, 0, -1, this.halfWidth, this.halfHeight); | |
return this; | |
}; | |
FSS.CanvasRenderer.prototype.clear = function() { | |
FSS.Renderer.prototype.clear.call(this); | |
this.context.clearRect(-this.halfWidth, -this.halfHeight, this.width, this.height); | |
return this; | |
}; | |
FSS.CanvasRenderer.prototype.render = function(scene) { | |
FSS.Renderer.prototype.render.call(this, scene); | |
var m,mesh, t,triangle, color; | |
// Clear Context | |
this.clear(); | |
// Configure Context | |
this.context.lineJoin = 'round'; | |
this.context.lineWidth = 1; | |
// Update Meshes | |
for (m = scene.meshes.length - 1; m >= 0; m--) { | |
mesh = scene.meshes[m]; | |
if (mesh.visible) { | |
mesh.update(scene.lights, true); | |
// Render Triangles | |
for (t = mesh.geometry.triangles.length - 1; t >= 0; t--) { | |
triangle = mesh.geometry.triangles[t]; | |
color = triangle.color.format(); | |
this.context.beginPath(); | |
this.context.moveTo(triangle.a.position[0], triangle.a.position[1]); | |
this.context.lineTo(triangle.b.position[0], triangle.b.position[1]); | |
this.context.lineTo(triangle.c.position[0], triangle.c.position[1]); | |
this.context.closePath(); | |
this.context.strokeStyle = color; | |
this.context.fillStyle = color; | |
this.context.stroke(); | |
this.context.fill(); | |
} | |
} | |
} | |
return this; | |
}; | |
/** | |
* @class WebGL Renderer | |
* @author Matthew Wagerfield | |
*/ | |
FSS.WebGLRenderer = function() { | |
FSS.Renderer.call(this); | |
this.element = document.createElement('canvas'); | |
this.element.style.display = 'block'; | |
// Set initial vertex and light count | |
this.vertices = null; | |
this.lights = null; | |
// Create parameters object | |
var parameters = { | |
preserveDrawingBuffer: false, | |
premultipliedAlpha: true, | |
antialias: true, | |
stencil: true, | |
alpha: true | |
}; | |
// Create and configure the gl context | |
this.gl = this.getContext(this.element, parameters); | |
// Set the internal support flag | |
this.unsupported = !this.gl; | |
// Setup renderer | |
if (this.unsupported) { | |
return 'WebGL is not supported by your browser.'; | |
} else { | |
this.gl.clearColor(0.0, 0.0, 0.0, 0.0); | |
this.gl.enable(this.gl.DEPTH_TEST); | |
this.setSize(this.element.width, this.element.height); | |
} | |
}; | |
FSS.WebGLRenderer.prototype = Object.create(FSS.Renderer.prototype); | |
FSS.WebGLRenderer.prototype.getContext = function(canvas, parameters) { | |
var context = false; | |
try { | |
if (!(context = canvas.getContext('experimental-webgl', parameters))) { | |
throw 'Error creating WebGL context.'; | |
} | |
} catch (error) { | |
console.error(error); | |
} | |
return context; | |
}; | |
FSS.WebGLRenderer.prototype.setSize = function(width, height) { | |
FSS.Renderer.prototype.setSize.call(this, width, height); | |
if (this.unsupported) return; | |
// Set the size of the canvas element | |
this.element.width = width; | |
this.element.height = height; | |
// Set the size of the gl viewport | |
this.gl.viewport(0, 0, width, height); | |
return this; | |
}; | |
FSS.WebGLRenderer.prototype.clear = function() { | |
FSS.Renderer.prototype.clear.call(this); | |
if (this.unsupported) return; | |
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); | |
return this; | |
}; | |
FSS.WebGLRenderer.prototype.render = function(scene) { | |
FSS.Renderer.prototype.render.call(this, scene); | |
if (this.unsupported) return; | |
var m,mesh, t,tl,triangle, l,light, | |
attribute, uniform, buffer, data, location, | |
update = false, lights = scene.lights.length, | |
index, v,vl,vetex,vertices = 0; | |
// Clear context | |
this.clear(); | |
// Build the shader program | |
if (this.lights !== lights) { | |
this.lights = lights; | |
if (this.lights > 0) { | |
this.buildProgram(lights); | |
} else { | |
return; | |
} | |
} | |
// Update program | |
if (!!this.program) { | |
// Increment vertex counter | |
for (m = scene.meshes.length - 1; m >= 0; m--) { | |
mesh = scene.meshes[m]; | |
if (mesh.geometry.dirty) update = true; | |
mesh.update(scene.lights, false); | |
vertices += mesh.geometry.triangles.length*3; | |
} | |
// Compare vertex counter | |
if (update || this.vertices !== vertices) { | |
this.vertices = vertices; | |
// Build buffers | |
for (attribute in this.program.attributes) { | |
buffer = this.program.attributes[attribute]; | |
buffer.data = new FSS.Array(vertices*buffer.size); | |
// Reset vertex index | |
index = 0; | |
// Update attribute buffer data | |
for (m = scene.meshes.length - 1; m >= 0; m--) { | |
mesh = scene.meshes[m]; | |
for (t = 0, tl = mesh.geometry.triangles.length; t < tl; t++) { | |
triangle = mesh.geometry.triangles[t]; | |
for (v = 0, vl = triangle.vertices.length; v < vl; v++) { | |
vertex = triangle.vertices[v]; | |
switch (attribute) { | |
case 'side': | |
this.setBufferData(index, buffer, mesh.side); | |
break; | |
case 'position': | |
this.setBufferData(index, buffer, vertex.position); | |
break; | |
case 'centroid': | |
this.setBufferData(index, buffer, triangle.centroid); | |
break; | |
case 'normal': | |
this.setBufferData(index, buffer, triangle.normal); | |
break; | |
case 'ambient': | |
this.setBufferData(index, buffer, mesh.material.ambient.rgba); | |
break; | |
case 'diffuse': | |
this.setBufferData(index, buffer, mesh.material.diffuse.rgba); | |
break; | |
} | |
index++; | |
} | |
} | |
} | |
// Upload attribute buffer data | |
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer.buffer); | |
this.gl.bufferData(this.gl.ARRAY_BUFFER, buffer.data, this.gl.DYNAMIC_DRAW); | |
this.gl.enableVertexAttribArray(buffer.location); | |
this.gl.vertexAttribPointer(buffer.location, buffer.size, this.gl.FLOAT, false, 0, 0); | |
} | |
} | |
// Build uniform buffers | |
this.setBufferData(0, this.program.uniforms.resolution, [this.width, this.height, this.width]); | |
for (l = lights-1; l >= 0; l--) { | |
light = scene.lights[l]; | |
this.setBufferData(l, this.program.uniforms.lightPosition, light.position); | |
this.setBufferData(l, this.program.uniforms.lightAmbient, light.ambient.rgba); | |
this.setBufferData(l, this.program.uniforms.lightDiffuse, light.diffuse.rgba); | |
} | |
// Update uniforms | |
for (uniform in this.program.uniforms) { | |
buffer = this.program.uniforms[uniform]; | |
location = buffer.location; | |
data = buffer.data; | |
switch (buffer.structure) { | |
case '3f': | |
this.gl.uniform3f(location, data[0], data[1], data[2]); | |
break; | |
case '3fv': | |
this.gl.uniform3fv(location, data); | |
break; | |
case '4fv': | |
this.gl.uniform4fv(location, data); | |
break; | |
} | |
} | |
} | |
// Draw those lovely triangles | |
this.gl.drawArrays(this.gl.TRIANGLES, 0, this.vertices); | |
return this; | |
}; | |
FSS.WebGLRenderer.prototype.setBufferData = function(index, buffer, value) { | |
if (FSS.Utils.isNumber(value)) { | |
buffer.data[index*buffer.size] = value; | |
} else { | |
for (var i = value.length - 1; i >= 0; i--) { | |
buffer.data[index*buffer.size+i] = value[i]; | |
} | |
} | |
}; | |
/** | |
* Concepts taken from three.js WebGLRenderer | |
* @see https://github.com/mrdoob/three.js/blob/master/src/renderers/WebGLRenderer.js | |
*/ | |
FSS.WebGLRenderer.prototype.buildProgram = function(lights) { | |
if (this.unsupported) return; | |
// Create shader source | |
var vs = FSS.WebGLRenderer.VS(lights); | |
var fs = FSS.WebGLRenderer.FS(lights); | |
// Derive the shader fingerprint | |
var code = vs + fs; | |
// Check if the program has already been compiled | |
if (!!this.program && this.program.code === code) return; | |
// Create the program and shaders | |
var program = this.gl.createProgram(); | |
var vertexShader = this.buildShader(this.gl.VERTEX_SHADER, vs); | |
var fragmentShader = this.buildShader(this.gl.FRAGMENT_SHADER, fs); | |
// Attach an link the shader | |
this.gl.attachShader(program, vertexShader); | |
this.gl.attachShader(program, fragmentShader); | |
this.gl.linkProgram(program); | |
// Add error handling | |
if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) { | |
var error = this.gl.getError(); | |
var status = this.gl.getProgramParameter(program, this.gl.VALIDATE_STATUS); | |
console.error('Could not initialise shader.\nVALIDATE_STATUS: '+status+'\nERROR: '+error); | |
return null; | |
} | |
// Delete the shader | |
this.gl.deleteShader(fragmentShader); | |
this.gl.deleteShader(vertexShader); | |
// Set the program code | |
program.code = code; | |
// Add the program attributes | |
program.attributes = { | |
side: this.buildBuffer(program, 'attribute', 'aSide', 1, 'f' ), | |
position: this.buildBuffer(program, 'attribute', 'aPosition', 3, 'v3'), | |
centroid: this.buildBuffer(program, 'attribute', 'aCentroid', 3, 'v3'), | |
normal: this.buildBuffer(program, 'attribute', 'aNormal', 3, 'v3'), | |
ambient: this.buildBuffer(program, 'attribute', 'aAmbient', 4, 'v4'), | |
diffuse: this.buildBuffer(program, 'attribute', 'aDiffuse', 4, 'v4') | |
}; | |
// Add the program uniforms | |
program.uniforms = { | |
resolution: this.buildBuffer(program, 'uniform', 'uResolution', 3, '3f', 1 ), | |
lightPosition: this.buildBuffer(program, 'uniform', 'uLightPosition', 3, '3fv', lights), | |
lightAmbient: this.buildBuffer(program, 'uniform', 'uLightAmbient', 4, '4fv', lights), | |
lightDiffuse: this.buildBuffer(program, 'uniform', 'uLightDiffuse', 4, '4fv', lights) | |
}; | |
// Set the renderer program | |
this.program = program; | |
// Enable program | |
this.gl.useProgram(this.program); | |
// Return the program | |
return program; | |
}; | |
FSS.WebGLRenderer.prototype.buildShader = function(type, source) { | |
if (this.unsupported) return; | |
// Create and compile shader | |
var shader = this.gl.createShader(type); | |
this.gl.shaderSource(shader, source); | |
this.gl.compileShader(shader); | |
// Add error handling | |
if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { | |
console.error(this.gl.getShaderInfoLog(shader)); | |
return null; | |
} | |
// Return the shader | |
return shader; | |
}; | |
FSS.WebGLRenderer.prototype.buildBuffer = function(program, type, identifier, size, structure, count) { | |
var buffer = {buffer:this.gl.createBuffer(), size:size, structure:structure, data:null}; | |
// Set the location | |
switch (type) { | |
case 'attribute': | |
buffer.location = this.gl.getAttribLocation(program, identifier); | |
break; | |
case 'uniform': | |
buffer.location = this.gl.getUniformLocation(program, identifier); | |
break; | |
} | |
// Create the buffer if count is provided | |
if (!!count) { | |
buffer.data = new FSS.Array(count*size); | |
} | |
// Return the buffer | |
return buffer; | |
}; | |
FSS.WebGLRenderer.VS = function(lights) { | |
var shader = [ | |
// Precision | |
'precision mediump float;', | |
// Lights | |
'#define LIGHTS ' + lights, | |
// Attributes | |
'attribute float aSide;', | |
'attribute vec3 aPosition;', | |
'attribute vec3 aCentroid;', | |
'attribute vec3 aNormal;', | |
'attribute vec4 aAmbient;', | |
'attribute vec4 aDiffuse;', | |
// Uniforms | |
'uniform vec3 uResolution;', | |
'uniform vec3 uLightPosition[LIGHTS];', | |
'uniform vec4 uLightAmbient[LIGHTS];', | |
'uniform vec4 uLightDiffuse[LIGHTS];', | |
// Varyings | |
'varying vec4 vColor;', | |
// Main | |
'void main() {', | |
// Create color | |
'vColor = vec4(0.0);', | |
// Calculate the vertex position | |
'vec3 position = aPosition / uResolution * 2.0;', | |
// Iterate through lights | |
'for (int i = 0; i < LIGHTS; i++) {', | |
'vec3 lightPosition = uLightPosition[i];', | |
'vec4 lightAmbient = uLightAmbient[i];', | |
'vec4 lightDiffuse = uLightDiffuse[i];', | |
// Calculate illuminance | |
'vec3 ray = normalize(lightPosition - aCentroid);', | |
'float illuminance = dot(aNormal, ray);', | |
'if (aSide == 0.0) {', | |
'illuminance = max(illuminance, 0.0);', | |
'} else if (aSide == 1.0) {', | |
'illuminance = abs(min(illuminance, 0.0));', | |
'} else if (aSide == 2.0) {', | |
'illuminance = max(abs(illuminance), 0.0);', | |
'}', | |
// Calculate ambient light | |
'vColor += aAmbient * lightAmbient;', | |
// Calculate diffuse light | |
'vColor += aDiffuse * lightDiffuse * illuminance;', | |
'}', | |
// Clamp color | |
'vColor = clamp(vColor, 0.0, 1.0);', | |
// Set gl_Position | |
'gl_Position = vec4(position, 1.0);', | |
'}' | |
// Return the shader | |
].join('\n'); | |
return shader; | |
}; | |
FSS.WebGLRenderer.FS = function(lights) { | |
var shader = [ | |
// Precision | |
'precision mediump float;', | |
// Varyings | |
'varying vec4 vColor;', | |
// Main | |
'void main() {', | |
// Set gl_FragColor | |
'gl_FragColor = vColor;', | |
'}' | |
// Return the shader | |
].join('\n'); | |
return shader; | |
}; | |
/** | |
* @class SVG Renderer | |
* @author Matthew Wagerfield | |
*/ | |
FSS.SVGRenderer = function() { | |
FSS.Renderer.call(this); | |
this.element = document.createElementNS(FSS.SVGNS, 'svg'); | |
this.element.setAttribute('xmlns', FSS.SVGNS); | |
this.element.setAttribute('version', '1.1'); | |
this.element.style.display = 'block'; | |
this.setSize(300, 150); | |
}; | |
FSS.SVGRenderer.prototype = Object.create(FSS.Renderer.prototype); | |
FSS.SVGRenderer.prototype.setSize = function(width, height) { | |
FSS.Renderer.prototype.setSize.call(this, width, height); | |
this.element.setAttribute('width', width); | |
this.element.setAttribute('height', height); | |
return this; | |
}; | |
FSS.SVGRenderer.prototype.clear = function() { | |
FSS.Renderer.prototype.clear.call(this); | |
for (var i = this.element.childNodes.length - 1; i >= 0; i--) { | |
this.element.removeChild(this.element.childNodes[i]); | |
} | |
return this; | |
}; | |
FSS.SVGRenderer.prototype.render = function(scene) { | |
FSS.Renderer.prototype.render.call(this, scene); | |
var m,mesh, t,triangle, points, style; | |
// Update Meshes | |
for (m = scene.meshes.length - 1; m >= 0; m--) { | |
mesh = scene.meshes[m]; | |
if (mesh.visible) { | |
mesh.update(scene.lights, true); | |
// Render Triangles | |
for (t = mesh.geometry.triangles.length - 1; t >= 0; t--) { | |
triangle = mesh.geometry.triangles[t]; | |
if (triangle.polygon.parentNode !== this.element) { | |
this.element.appendChild(triangle.polygon); | |
} | |
points = this.formatPoint(triangle.a)+' '; | |
points += this.formatPoint(triangle.b)+' '; | |
points += this.formatPoint(triangle.c); | |
style = this.formatStyle(triangle.color.format()); | |
triangle.polygon.setAttributeNS(null, 'points', points); | |
triangle.polygon.setAttributeNS(null, 'style', style); | |
} | |
} | |
} | |
return this; | |
}; | |
FSS.SVGRenderer.prototype.formatPoint = function(vertex) { | |
return (this.halfWidth+vertex.position[0])+','+(this.halfHeight-vertex.position[1]); | |
}; | |
FSS.SVGRenderer.prototype.formatStyle = function(color) { | |
var style = 'fill:'+color+';'; | |
style += 'stroke:'+color+';'; | |
return style; | |
}; | |
/** | |
* dat-gui JavaScript Controller Library | |
* https://code.google.com/p/dat-gui | |
* | |
* Copyright 2011 Data Arts Team, Google Creative Lab | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
*/ | |
var dat=dat||{};dat.gui=dat.gui||{};dat.utils=dat.utils||{};dat.controllers=dat.controllers||{};dat.dom=dat.dom||{};dat.color=dat.color||{};dat.utils.css=function(){return{load:function(e,a){var a=a||document,c=a.createElement("link");c.type="text/css";c.rel="stylesheet";c.href=e;a.getElementsByTagName("head")[0].appendChild(c)},inject:function(e,a){var a=a||document,c=document.createElement("style");c.type="text/css";c.innerHTML=e;a.getElementsByTagName("head")[0].appendChild(c)}}}(); | |
dat.utils.common=function(){var e=Array.prototype.forEach,a=Array.prototype.slice;return{BREAK:{},extend:function(c){this.each(a.call(arguments,1),function(a){for(var f in a)this.isUndefined(a[f])||(c[f]=a[f])},this);return c},defaults:function(c){this.each(a.call(arguments,1),function(a){for(var f in a)this.isUndefined(c[f])&&(c[f]=a[f])},this);return c},compose:function(){var c=a.call(arguments);return function(){for(var d=a.call(arguments),f=c.length-1;f>=0;f--)d=[c[f].apply(this,d)];return d[0]}}, | |
each:function(a,d,f){if(e&&a.forEach===e)a.forEach(d,f);else if(a.length===a.length+0)for(var b=0,n=a.length;b<n;b++){if(b in a&&d.call(f,a[b],b)===this.BREAK)break}else for(b in a)if(d.call(f,a[b],b)===this.BREAK)break},defer:function(a){setTimeout(a,0)},toArray:function(c){return c.toArray?c.toArray():a.call(c)},isUndefined:function(a){return a===void 0},isNull:function(a){return a===null},isNaN:function(a){return a!==a},isArray:Array.isArray||function(a){return a.constructor===Array},isObject:function(a){return a=== | |
Object(a)},isNumber:function(a){return a===a+0},isString:function(a){return a===a+""},isBoolean:function(a){return a===false||a===true},isFunction:function(a){return Object.prototype.toString.call(a)==="[object Function]"}}}(); | |
dat.controllers.Controller=function(e){var a=function(a,d){this.initialValue=a[d];this.domElement=document.createElement("div");this.object=a;this.property=d;this.__onFinishChange=this.__onChange=void 0};e.extend(a.prototype,{onChange:function(a){this.__onChange=a;return this},onFinishChange:function(a){this.__onFinishChange=a;return this},setValue:function(a){this.object[this.property]=a;this.__onChange&&this.__onChange.call(this,a);this.updateDisplay();return this},getValue:function(){return this.object[this.property]}, | |
updateDisplay:function(){return this},isModified:function(){return this.initialValue!==this.getValue()}});return a}(dat.utils.common); | |
dat.dom.dom=function(e){function a(b){if(b==="0"||e.isUndefined(b))return 0;b=b.match(d);return!e.isNull(b)?parseFloat(b[1]):0}var c={};e.each({HTMLEvents:["change"],MouseEvents:["click","mousemove","mousedown","mouseup","mouseover"],KeyboardEvents:["keydown"]},function(b,a){e.each(b,function(b){c[b]=a})});var d=/(\d+(\.\d+)?)px/,f={makeSelectable:function(b,a){if(!(b===void 0||b.style===void 0))b.onselectstart=a?function(){return false}:function(){},b.style.MozUserSelect=a?"auto":"none",b.style.KhtmlUserSelect= | |
a?"auto":"none",b.unselectable=a?"on":"off"},makeFullscreen:function(b,a,d){e.isUndefined(a)&&(a=true);e.isUndefined(d)&&(d=true);b.style.position="absolute";if(a)b.style.left=0,b.style.right=0;if(d)b.style.top=0,b.style.bottom=0},fakeEvent:function(b,a,d,f){var d=d||{},m=c[a];if(!m)throw Error("Event type "+a+" not supported.");var l=document.createEvent(m);switch(m){case "MouseEvents":l.initMouseEvent(a,d.bubbles||false,d.cancelable||true,window,d.clickCount||1,0,0,d.x||d.clientX||0,d.y||d.clientY|| | |
0,false,false,false,false,0,null);break;case "KeyboardEvents":m=l.initKeyboardEvent||l.initKeyEvent;e.defaults(d,{cancelable:true,ctrlKey:false,altKey:false,shiftKey:false,metaKey:false,keyCode:void 0,charCode:void 0});m(a,d.bubbles||false,d.cancelable,window,d.ctrlKey,d.altKey,d.shiftKey,d.metaKey,d.keyCode,d.charCode);break;default:l.initEvent(a,d.bubbles||false,d.cancelable||true)}e.defaults(l,f);b.dispatchEvent(l)},bind:function(b,a,d,c){b.addEventListener?b.addEventListener(a,d,c||false):b.attachEvent&& | |
b.attachEvent("on"+a,d);return f},unbind:function(b,a,d,c){b.removeEventListener?b.removeEventListener(a,d,c||false):b.detachEvent&&b.detachEvent("on"+a,d);return f},addClass:function(b,a){if(b.className===void 0)b.className=a;else if(b.className!==a){var d=b.className.split(/ +/);if(d.indexOf(a)==-1)d.push(a),b.className=d.join(" ").replace(/^\s+/,"").replace(/\s+$/,"")}return f},removeClass:function(b,a){if(a){if(b.className!==void 0)if(b.className===a)b.removeAttribute("class");else{var d=b.className.split(/ +/), | |
c=d.indexOf(a);if(c!=-1)d.splice(c,1),b.className=d.join(" ")}}else b.className=void 0;return f},hasClass:function(a,d){return RegExp("(?:^|\\s+)"+d+"(?:\\s+|$)").test(a.className)||false},getWidth:function(b){b=getComputedStyle(b);return a(b["border-left-width"])+a(b["border-right-width"])+a(b["padding-left"])+a(b["padding-right"])+a(b.width)},getHeight:function(b){b=getComputedStyle(b);return a(b["border-top-width"])+a(b["border-bottom-width"])+a(b["padding-top"])+a(b["padding-bottom"])+a(b.height)}, | |
getOffset:function(a){var d={left:0,top:0};if(a.offsetParent){do d.left+=a.offsetLeft,d.top+=a.offsetTop;while(a=a.offsetParent)}return d},isActive:function(a){return a===document.activeElement&&(a.type||a.href)}};return f}(dat.utils.common); | |
dat.controllers.OptionController=function(e,a,c){var d=function(f,b,e){d.superclass.call(this,f,b);var h=this;this.__select=document.createElement("select");if(c.isArray(e)){var j={};c.each(e,function(a){j[a]=a});e=j}c.each(e,function(a,b){var d=document.createElement("option");d.innerHTML=b;d.setAttribute("value",a);h.__select.appendChild(d)});this.updateDisplay();a.bind(this.__select,"change",function(){h.setValue(this.options[this.selectedIndex].value)});this.domElement.appendChild(this.__select)}; | |
d.superclass=e;c.extend(d.prototype,e.prototype,{setValue:function(a){a=d.superclass.prototype.setValue.call(this,a);this.__onFinishChange&&this.__onFinishChange.call(this,this.getValue());return a},updateDisplay:function(){this.__select.value=this.getValue();return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common); | |
dat.controllers.NumberController=function(e,a){var c=function(d,f,b){c.superclass.call(this,d,f);b=b||{};this.__min=b.min;this.__max=b.max;this.__step=b.step;d=this.__impliedStep=a.isUndefined(this.__step)?this.initialValue==0?1:Math.pow(10,Math.floor(Math.log(this.initialValue)/Math.LN10))/10:this.__step;d=d.toString();this.__precision=d.indexOf(".")>-1?d.length-d.indexOf(".")-1:0};c.superclass=e;a.extend(c.prototype,e.prototype,{setValue:function(a){if(this.__min!==void 0&&a<this.__min)a=this.__min; | |
else if(this.__max!==void 0&&a>this.__max)a=this.__max;this.__step!==void 0&&a%this.__step!=0&&(a=Math.round(a/this.__step)*this.__step);return c.superclass.prototype.setValue.call(this,a)},min:function(a){this.__min=a;return this},max:function(a){this.__max=a;return this},step:function(a){this.__step=a;return this}});return c}(dat.controllers.Controller,dat.utils.common); | |
dat.controllers.NumberControllerBox=function(e,a,c){var d=function(f,b,e){function h(){var a=parseFloat(l.__input.value);c.isNaN(a)||l.setValue(a)}function j(a){var b=o-a.clientY;l.setValue(l.getValue()+b*l.__impliedStep);o=a.clientY}function m(){a.unbind(window,"mousemove",j);a.unbind(window,"mouseup",m)}this.__truncationSuspended=false;d.superclass.call(this,f,b,e);var l=this,o;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"change",h); | |
a.bind(this.__input,"blur",function(){h();l.__onFinishChange&&l.__onFinishChange.call(l,l.getValue())});a.bind(this.__input,"mousedown",function(b){a.bind(window,"mousemove",j);a.bind(window,"mouseup",m);o=b.clientY});a.bind(this.__input,"keydown",function(a){if(a.keyCode===13)l.__truncationSuspended=true,this.blur(),l.__truncationSuspended=false});this.updateDisplay();this.domElement.appendChild(this.__input)};d.superclass=e;c.extend(d.prototype,e.prototype,{updateDisplay:function(){var a=this.__input, | |
b;if(this.__truncationSuspended)b=this.getValue();else{b=this.getValue();var c=Math.pow(10,this.__precision);b=Math.round(b*c)/c}a.value=b;return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.NumberController,dat.dom.dom,dat.utils.common); | |
dat.controllers.NumberControllerSlider=function(e,a,c,d,f){var b=function(d,c,f,e,l){function o(b){b.preventDefault();var d=a.getOffset(g.__background),c=a.getWidth(g.__background);g.setValue(g.__min+(g.__max-g.__min)*((b.clientX-d.left)/(d.left+c-d.left)));return false}function y(){a.unbind(window,"mousemove",o);a.unbind(window,"mouseup",y);g.__onFinishChange&&g.__onFinishChange.call(g,g.getValue())}b.superclass.call(this,d,c,{min:f,max:e,step:l});var g=this;this.__background=document.createElement("div"); | |
this.__foreground=document.createElement("div");a.bind(this.__background,"mousedown",function(b){a.bind(window,"mousemove",o);a.bind(window,"mouseup",y);o(b)});a.addClass(this.__background,"slider");a.addClass(this.__foreground,"slider-fg");this.updateDisplay();this.__background.appendChild(this.__foreground);this.domElement.appendChild(this.__background)};b.superclass=e;b.useDefaultStyles=function(){c.inject(f)};d.extend(b.prototype,e.prototype,{updateDisplay:function(){this.__foreground.style.width= | |
(this.getValue()-this.__min)/(this.__max-this.__min)*100+"%";return b.superclass.prototype.updateDisplay.call(this)}});return b}(dat.controllers.NumberController,dat.dom.dom,dat.utils.css,dat.utils.common,".slider {\n box-shadow: inset 0 2px 4px rgba(0,0,0,0.15);\n height: 1em;\n border-radius: 1em;\n background-color: #eee;\n padding: 0 0.5em;\n overflow: hidden;\n}\n\n.slider-fg {\n padding: 1px 0 2px 0;\n background-color: #aaa;\n height: 1em;\n margin-left: -0.5em;\n padding-right: 0.5em;\n border-radius: 1em 0 0 1em;\n}\n\n.slider-fg:after {\n display: inline-block;\n border-radius: 1em;\n background-color: #fff;\n border: 1px solid #aaa;\n content: '';\n float: right;\n margin-right: -1em;\n margin-top: -1px;\n height: 0.9em;\n width: 0.9em;\n}"); | |
dat.controllers.FunctionController=function(e,a,c){var d=function(c,b,e){d.superclass.call(this,c,b);var h=this;this.__button=document.createElement("div");this.__button.innerHTML=e===void 0?"Fire":e;a.bind(this.__button,"click",function(a){a.preventDefault();h.fire();return false});a.addClass(this.__button,"button");this.domElement.appendChild(this.__button)};d.superclass=e;c.extend(d.prototype,e.prototype,{fire:function(){this.__onChange&&this.__onChange.call(this);this.__onFinishChange&&this.__onFinishChange.call(this, | |
this.getValue());this.getValue().call(this.object)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common); | |
dat.controllers.BooleanController=function(e,a,c){var d=function(c,b){d.superclass.call(this,c,b);var e=this;this.__prev=this.getValue();this.__checkbox=document.createElement("input");this.__checkbox.setAttribute("type","checkbox");a.bind(this.__checkbox,"change",function(){e.setValue(!e.__prev)},false);this.domElement.appendChild(this.__checkbox);this.updateDisplay()};d.superclass=e;c.extend(d.prototype,e.prototype,{setValue:function(a){a=d.superclass.prototype.setValue.call(this,a);this.__onFinishChange&& | |
this.__onFinishChange.call(this,this.getValue());this.__prev=this.getValue();return a},updateDisplay:function(){this.getValue()===true?(this.__checkbox.setAttribute("checked","checked"),this.__checkbox.checked=true):this.__checkbox.checked=false;return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common); | |
dat.color.toString=function(e){return function(a){if(a.a==1||e.isUndefined(a.a)){for(a=a.hex.toString(16);a.length<6;)a="0"+a;return"#"+a}else return"rgba("+Math.round(a.r)+","+Math.round(a.g)+","+Math.round(a.b)+","+a.a+")"}}(dat.utils.common); | |
dat.color.interpret=function(e,a){var c,d,f=[{litmus:a.isString,conversions:{THREE_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i);return a===null?false:{space:"HEX",hex:parseInt("0x"+a[1].toString()+a[1].toString()+a[2].toString()+a[2].toString()+a[3].toString()+a[3].toString())}},write:e},SIX_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9]{6})$/i);return a===null?false:{space:"HEX",hex:parseInt("0x"+a[1].toString())}},write:e},CSS_RGB:{read:function(a){a=a.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/); | |
return a===null?false:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3])}},write:e},CSS_RGBA:{read:function(a){a=a.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/);return a===null?false:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3]),a:parseFloat(a[4])}},write:e}}},{litmus:a.isNumber,conversions:{HEX:{read:function(a){return{space:"HEX",hex:a,conversionName:"HEX"}},write:function(a){return a.hex}}}},{litmus:a.isArray,conversions:{RGB_ARRAY:{read:function(a){return a.length!= | |
3?false:{space:"RGB",r:a[0],g:a[1],b:a[2]}},write:function(a){return[a.r,a.g,a.b]}},RGBA_ARRAY:{read:function(a){return a.length!=4?false:{space:"RGB",r:a[0],g:a[1],b:a[2],a:a[3]}},write:function(a){return[a.r,a.g,a.b,a.a]}}}},{litmus:a.isObject,conversions:{RGBA_OBJ:{read:function(b){return a.isNumber(b.r)&&a.isNumber(b.g)&&a.isNumber(b.b)&&a.isNumber(b.a)?{space:"RGB",r:b.r,g:b.g,b:b.b,a:b.a}:false},write:function(a){return{r:a.r,g:a.g,b:a.b,a:a.a}}},RGB_OBJ:{read:function(b){return a.isNumber(b.r)&& | |
a.isNumber(b.g)&&a.isNumber(b.b)?{space:"RGB",r:b.r,g:b.g,b:b.b}:false},write:function(a){return{r:a.r,g:a.g,b:a.b}}},HSVA_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)&&a.isNumber(b.a)?{space:"HSV",h:b.h,s:b.s,v:b.v,a:b.a}:false},write:function(a){return{h:a.h,s:a.s,v:a.v,a:a.a}}},HSV_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)?{space:"HSV",h:b.h,s:b.s,v:b.v}:false},write:function(a){return{h:a.h,s:a.s,v:a.v}}}}}];return function(){d= | |
false;var b=arguments.length>1?a.toArray(arguments):arguments[0];a.each(f,function(e){if(e.litmus(b))return a.each(e.conversions,function(e,f){c=e.read(b);if(d===false&&c!==false)return d=c,c.conversionName=f,c.conversion=e,a.BREAK}),a.BREAK});return d}}(dat.color.toString,dat.utils.common); | |
dat.GUI=dat.gui.GUI=function(e,a,c,d,f,b,n,h,j,m,l,o,y,g,i){function q(a,b,r,c){if(b[r]===void 0)throw Error("Object "+b+' has no property "'+r+'"');c.color?b=new l(b,r):(b=[b,r].concat(c.factoryArgs),b=d.apply(a,b));if(c.before instanceof f)c.before=c.before.__li;t(a,b);g.addClass(b.domElement,"c");r=document.createElement("span");g.addClass(r,"property-name");r.innerHTML=b.property;var e=document.createElement("div");e.appendChild(r);e.appendChild(b.domElement);c=s(a,e,c.before);g.addClass(c,k.CLASS_CONTROLLER_ROW); | |
g.addClass(c,typeof b.getValue());p(a,c,b);a.__controllers.push(b);return b}function s(a,b,d){var c=document.createElement("li");b&&c.appendChild(b);d?a.__ul.insertBefore(c,params.before):a.__ul.appendChild(c);a.onResize();return c}function p(a,d,c){c.__li=d;c.__gui=a;i.extend(c,{options:function(b){if(arguments.length>1)return c.remove(),q(a,c.object,c.property,{before:c.__li.nextElementSibling,factoryArgs:[i.toArray(arguments)]});if(i.isArray(b)||i.isObject(b))return c.remove(),q(a,c.object,c.property, | |
{before:c.__li.nextElementSibling,factoryArgs:[b]})},name:function(a){c.__li.firstElementChild.firstElementChild.innerHTML=a;return c},listen:function(){c.__gui.listen(c);return c},remove:function(){c.__gui.remove(c);return c}});if(c instanceof j){var e=new h(c.object,c.property,{min:c.__min,max:c.__max,step:c.__step});i.each(["updateDisplay","onChange","onFinishChange"],function(a){var b=c[a],H=e[a];c[a]=e[a]=function(){var a=Array.prototype.slice.call(arguments);b.apply(c,a);return H.apply(e,a)}}); | |
g.addClass(d,"has-slider");c.domElement.insertBefore(e.domElement,c.domElement.firstElementChild)}else if(c instanceof h){var f=function(b){return i.isNumber(c.__min)&&i.isNumber(c.__max)?(c.remove(),q(a,c.object,c.property,{before:c.__li.nextElementSibling,factoryArgs:[c.__min,c.__max,c.__step]})):b};c.min=i.compose(f,c.min);c.max=i.compose(f,c.max)}else if(c instanceof b)g.bind(d,"click",function(){g.fakeEvent(c.__checkbox,"click")}),g.bind(c.__checkbox,"click",function(a){a.stopPropagation()}); | |
else if(c instanceof n)g.bind(d,"click",function(){g.fakeEvent(c.__button,"click")}),g.bind(d,"mouseover",function(){g.addClass(c.__button,"hover")}),g.bind(d,"mouseout",function(){g.removeClass(c.__button,"hover")});else if(c instanceof l)g.addClass(d,"color"),c.updateDisplay=i.compose(function(a){d.style.borderLeftColor=c.__color.toString();return a},c.updateDisplay),c.updateDisplay();c.setValue=i.compose(function(b){a.getRoot().__preset_select&&c.isModified()&&B(a.getRoot(),true);return b},c.setValue)} | |
function t(a,b){var c=a.getRoot(),d=c.__rememberedObjects.indexOf(b.object);if(d!=-1){var e=c.__rememberedObjectIndecesToControllers[d];e===void 0&&(e={},c.__rememberedObjectIndecesToControllers[d]=e);e[b.property]=b;if(c.load&&c.load.remembered){c=c.load.remembered;if(c[a.preset])c=c[a.preset];else if(c[w])c=c[w];else return;if(c[d]&&c[d][b.property]!==void 0)d=c[d][b.property],b.initialValue=d,b.setValue(d)}}}function I(a){var b=a.__save_row=document.createElement("li");g.addClass(a.domElement, | |
"has-save");a.__ul.insertBefore(b,a.__ul.firstChild);g.addClass(b,"save-row");var c=document.createElement("span");c.innerHTML=" ";g.addClass(c,"button gears");var d=document.createElement("span");d.innerHTML="Save";g.addClass(d,"button");g.addClass(d,"save");var e=document.createElement("span");e.innerHTML="New";g.addClass(e,"button");g.addClass(e,"save-as");var f=document.createElement("span");f.innerHTML="Revert";g.addClass(f,"button");g.addClass(f,"revert");var m=a.__preset_select=document.createElement("select"); | |
a.load&&a.load.remembered?i.each(a.load.remembered,function(b,c){C(a,c,c==a.preset)}):C(a,w,false);g.bind(m,"change",function(){for(var b=0;b<a.__preset_select.length;b++)a.__preset_select[b].innerHTML=a.__preset_select[b].value;a.preset=this.value});b.appendChild(m);b.appendChild(c);b.appendChild(d);b.appendChild(e);b.appendChild(f);if(u){var b=document.getElementById("dg-save-locally"),l=document.getElementById("dg-local-explain");b.style.display="block";b=document.getElementById("dg-local-storage"); | |
localStorage.getItem(document.location.href+".isLocal")==="true"&&b.setAttribute("checked","checked");var o=function(){l.style.display=a.useLocalStorage?"block":"none"};o();g.bind(b,"change",function(){a.useLocalStorage=!a.useLocalStorage;o()})}var h=document.getElementById("dg-new-constructor");g.bind(h,"keydown",function(a){a.metaKey&&(a.which===67||a.keyCode==67)&&x.hide()});g.bind(c,"click",function(){h.innerHTML=JSON.stringify(a.getSaveObject(),void 0,2);x.show();h.focus();h.select()});g.bind(d, | |
"click",function(){a.save()});g.bind(e,"click",function(){var b=prompt("Enter a new preset name.");b&&a.saveAs(b)});g.bind(f,"click",function(){a.revert()})}function J(a){function b(f){f.preventDefault();e=f.clientX;g.addClass(a.__closeButton,k.CLASS_DRAG);g.bind(window,"mousemove",c);g.bind(window,"mouseup",d);return false}function c(b){b.preventDefault();a.width+=e-b.clientX;a.onResize();e=b.clientX;return false}function d(){g.removeClass(a.__closeButton,k.CLASS_DRAG);g.unbind(window,"mousemove", | |
c);g.unbind(window,"mouseup",d)}a.__resize_handle=document.createElement("div");i.extend(a.__resize_handle.style,{width:"6px",marginLeft:"-3px",height:"200px",cursor:"ew-resize",position:"absolute"});var e;g.bind(a.__resize_handle,"mousedown",b);g.bind(a.__closeButton,"mousedown",b);a.domElement.insertBefore(a.__resize_handle,a.domElement.firstElementChild)}function D(a,b){a.domElement.style.width=b+"px";if(a.__save_row&&a.autoPlace)a.__save_row.style.width=b+"px";if(a.__closeButton)a.__closeButton.style.width= | |
b+"px"}function z(a,b){var c={};i.each(a.__rememberedObjects,function(d,e){var f={};i.each(a.__rememberedObjectIndecesToControllers[e],function(a,c){f[c]=b?a.initialValue:a.getValue()});c[e]=f});return c}function C(a,b,c){var d=document.createElement("option");d.innerHTML=b;d.value=b;a.__preset_select.appendChild(d);if(c)a.__preset_select.selectedIndex=a.__preset_select.length-1}function B(a,b){var c=a.__preset_select[a.__preset_select.selectedIndex];c.innerHTML=b?c.value+"*":c.value}function E(a){a.length!= | |
0&&o(function(){E(a)});i.each(a,function(a){a.updateDisplay()})}e.inject(c);var w="Default",u;try{u="localStorage"in window&&window.localStorage!==null}catch(K){u=false}var x,F=true,v,A=false,G=[],k=function(a){function b(){localStorage.setItem(document.location.href+".gui",JSON.stringify(d.getSaveObject()))}function c(){var a=d.getRoot();a.width+=1;i.defer(function(){a.width-=1})}var d=this;this.domElement=document.createElement("div");this.__ul=document.createElement("ul");this.domElement.appendChild(this.__ul); | |
g.addClass(this.domElement,"dg");this.__folders={};this.__controllers=[];this.__rememberedObjects=[];this.__rememberedObjectIndecesToControllers=[];this.__listening=[];a=a||{};a=i.defaults(a,{autoPlace:true,width:k.DEFAULT_WIDTH});a=i.defaults(a,{resizable:a.autoPlace,hideable:a.autoPlace});if(i.isUndefined(a.load))a.load={preset:w};else if(a.preset)a.load.preset=a.preset;i.isUndefined(a.parent)&&a.hideable&&G.push(this);a.resizable=i.isUndefined(a.parent)&&a.resizable;if(a.autoPlace&&i.isUndefined(a.scrollable))a.scrollable= | |
true;var e=u&&localStorage.getItem(document.location.href+".isLocal")==="true";Object.defineProperties(this,{parent:{get:function(){return a.parent}},scrollable:{get:function(){return a.scrollable}},autoPlace:{get:function(){return a.autoPlace}},preset:{get:function(){return d.parent?d.getRoot().preset:a.load.preset},set:function(b){d.parent?d.getRoot().preset=b:a.load.preset=b;for(b=0;b<this.__preset_select.length;b++)if(this.__preset_select[b].value==this.preset)this.__preset_select.selectedIndex= | |
b;d.revert()}},width:{get:function(){return a.width},set:function(b){a.width=b;D(d,b)}},name:{get:function(){return a.name},set:function(b){a.name=b;if(m)m.innerHTML=a.name}},closed:{get:function(){return a.closed},set:function(b){a.closed=b;a.closed?g.addClass(d.__ul,k.CLASS_CLOSED):g.removeClass(d.__ul,k.CLASS_CLOSED);this.onResize();if(d.__closeButton)d.__closeButton.innerHTML=b?k.TEXT_OPEN:k.TEXT_CLOSED}},load:{get:function(){return a.load}},useLocalStorage:{get:function(){return e},set:function(a){u&& | |
((e=a)?g.bind(window,"unload",b):g.unbind(window,"unload",b),localStorage.setItem(document.location.href+".isLocal",a))}}});if(i.isUndefined(a.parent)){a.closed=false;g.addClass(this.domElement,k.CLASS_MAIN);g.makeSelectable(this.domElement,false);if(u&&e){d.useLocalStorage=true;var f=localStorage.getItem(document.location.href+".gui");if(f)a.load=JSON.parse(f)}this.__closeButton=document.createElement("div");this.__closeButton.innerHTML=k.TEXT_CLOSED;g.addClass(this.__closeButton,k.CLASS_CLOSE_BUTTON); | |
this.domElement.appendChild(this.__closeButton);g.bind(this.__closeButton,"click",function(){d.closed=!d.closed})}else{if(a.closed===void 0)a.closed=true;var m=document.createTextNode(a.name);g.addClass(m,"controller-name");f=s(d,m);g.addClass(this.__ul,k.CLASS_CLOSED);g.addClass(f,"title");g.bind(f,"click",function(a){a.preventDefault();d.closed=!d.closed;return false});if(!a.closed)this.closed=false}a.autoPlace&&(i.isUndefined(a.parent)&&(F&&(v=document.createElement("div"),g.addClass(v,"dg"),g.addClass(v, | |
k.CLASS_AUTO_PLACE_CONTAINER),document.body.appendChild(v),F=false),v.appendChild(this.domElement),g.addClass(this.domElement,k.CLASS_AUTO_PLACE)),this.parent||D(d,a.width));g.bind(window,"resize",function(){d.onResize()});g.bind(this.__ul,"webkitTransitionEnd",function(){d.onResize()});g.bind(this.__ul,"transitionend",function(){d.onResize()});g.bind(this.__ul,"oTransitionEnd",function(){d.onResize()});this.onResize();a.resizable&&J(this);d.getRoot();a.parent||c()};k.toggleHide=function(){A=!A;i.each(G, | |
function(a){a.domElement.style.zIndex=A?-999:999;a.domElement.style.opacity=A?0:1})};k.CLASS_AUTO_PLACE="a";k.CLASS_AUTO_PLACE_CONTAINER="ac";k.CLASS_MAIN="main";k.CLASS_CONTROLLER_ROW="cr";k.CLASS_TOO_TALL="taller-than-window";k.CLASS_CLOSED="closed";k.CLASS_CLOSE_BUTTON="close-button";k.CLASS_DRAG="drag";k.DEFAULT_WIDTH=245;k.TEXT_CLOSED="Close Controls";k.TEXT_OPEN="Open Controls";g.bind(window,"keydown",function(a){document.activeElement.type!=="text"&&(a.which===72||a.keyCode==72)&&k.toggleHide()}, | |
false);i.extend(k.prototype,{add:function(a,b){return q(this,a,b,{factoryArgs:Array.prototype.slice.call(arguments,2)})},addColor:function(a,b){return q(this,a,b,{color:true})},remove:function(a){this.__ul.removeChild(a.__li);this.__controllers.slice(this.__controllers.indexOf(a),1);var b=this;i.defer(function(){b.onResize()})},destroy:function(){this.autoPlace&&v.removeChild(this.domElement)},addFolder:function(a){if(this.__folders[a]!==void 0)throw Error('You already have a folder in this GUI by the name "'+ | |
a+'"');var b={name:a,parent:this};b.autoPlace=this.autoPlace;if(this.load&&this.load.folders&&this.load.folders[a])b.closed=this.load.folders[a].closed,b.load=this.load.folders[a];b=new k(b);this.__folders[a]=b;a=s(this,b.domElement);g.addClass(a,"folder");return b},open:function(){this.closed=false},close:function(){this.closed=true},onResize:function(){var a=this.getRoot();if(a.scrollable){var b=g.getOffset(a.__ul).top,c=0;i.each(a.__ul.childNodes,function(b){a.autoPlace&&b===a.__save_row||(c+= | |
g.getHeight(b))});window.innerHeight-b-20<c?(g.addClass(a.domElement,k.CLASS_TOO_TALL),a.__ul.style.height=window.innerHeight-b-20+"px"):(g.removeClass(a.domElement,k.CLASS_TOO_TALL),a.__ul.style.height="auto")}a.__resize_handle&&i.defer(function(){a.__resize_handle.style.height=a.__ul.offsetHeight+"px"});if(a.__closeButton)a.__closeButton.style.width=a.width+"px"},remember:function(){if(i.isUndefined(x))x=new y,x.domElement.innerHTML=a;if(this.parent)throw Error("You can only call remember on a top level GUI."); | |
var b=this;i.each(Array.prototype.slice.call(arguments),function(a){b.__rememberedObjects.length==0&&I(b);b.__rememberedObjects.indexOf(a)==-1&&b.__rememberedObjects.push(a)});this.autoPlace&&D(this,this.width)},getRoot:function(){for(var a=this;a.parent;)a=a.parent;return a},getSaveObject:function(){var a=this.load;a.closed=this.closed;if(this.__rememberedObjects.length>0){a.preset=this.preset;if(!a.remembered)a.remembered={};a.remembered[this.preset]=z(this)}a.folders={};i.each(this.__folders,function(b, | |
c){a.folders[c]=b.getSaveObject()});return a},save:function(){if(!this.load.remembered)this.load.remembered={};this.load.remembered[this.preset]=z(this);B(this,false)},saveAs:function(a){if(!this.load.remembered)this.load.remembered={},this.load.remembered[w]=z(this,true);this.load.remembered[a]=z(this);this.preset=a;C(this,a,true)},revert:function(a){i.each(this.__controllers,function(b){this.getRoot().load.remembered?t(a||this.getRoot(),b):b.setValue(b.initialValue)},this);i.each(this.__folders, | |
function(a){a.revert(a)});a||B(this.getRoot(),false)},listen:function(a){var b=this.__listening.length==0;this.__listening.push(a);b&&E(this.__listening)}});return k}(dat.utils.css,'<div id="dg-save" class="dg dialogue">\n\n Here\'s the new load parameter for your <code>GUI</code>\'s constructor:\n\n <textarea id="dg-new-constructor"></textarea>\n\n <div id="dg-save-locally">\n\n <input id="dg-local-storage" type="checkbox"/> Automatically save\n values to <code>localStorage</code> on exit.\n\n <div id="dg-local-explain">The values saved to <code>localStorage</code> will\n override those passed to <code>dat.GUI</code>\'s constructor. This makes it\n easier to work incrementally, but <code>localStorage</code> is fragile,\n and your friends may not see the same values you do.\n \n </div>\n \n </div>\n\n</div>', | |
".dg ul{list-style:none;margin:0;padding:0;width:100%;clear:both}.dg.ac{position:fixed;top:0;left:0;right:0;height:0;z-index:0}.dg:not(.ac) .main{overflow:hidden}.dg.main{-webkit-transition:opacity 0.1s linear;-o-transition:opacity 0.1s linear;-moz-transition:opacity 0.1s linear;transition:opacity 0.1s linear}.dg.main.taller-than-window{overflow-y:auto}.dg.main.taller-than-window .close-button{opacity:1;margin-top:-1px;border-top:1px solid #2c2c2c}.dg.main ul.closed .close-button{opacity:1 !important}.dg.main:hover .close-button,.dg.main .close-button.drag{opacity:1}.dg.main .close-button{-webkit-transition:opacity 0.1s linear;-o-transition:opacity 0.1s linear;-moz-transition:opacity 0.1s linear;transition:opacity 0.1s linear;border:0;position:absolute;line-height:19px;height:20px;cursor:pointer;text-align:center;background-color:#000}.dg.main .close-button:hover{background-color:#111}.dg.a{float:right;margin-right:15px;overflow-x:hidden}.dg.a.has-save ul{margin-top:27px}.dg.a.has-save ul.closed{margin-top:0}.dg.a .save-row{position:fixed;top:0;z-index:1002}.dg li{-webkit-transition:height 0.1s ease-out;-o-transition:height 0.1s ease-out;-moz-transition:height 0.1s ease-out;transition:height 0.1s ease-out}.dg li:not(.folder){cursor:auto;height:27px;line-height:27px;overflow:hidden;padding:0 4px 0 5px}.dg li.folder{padding:0;border-left:4px solid rgba(0,0,0,0)}.dg li.title{cursor:pointer;margin-left:-4px}.dg .closed li:not(.title),.dg .closed ul li,.dg .closed ul li > *{height:0;overflow:hidden;border:0}.dg .cr{clear:both;padding-left:3px;height:27px}.dg .property-name{cursor:default;float:left;clear:left;width:40%;overflow:hidden;text-overflow:ellipsis}.dg .c{float:left;width:60%}.dg .c input[type=text]{border:0;margin-top:4px;padding:3px;width:100%;float:right}.dg .has-slider input[type=text]{width:30%;margin-left:0}.dg .slider{float:left;width:66%;margin-left:-5px;margin-right:0;height:19px;margin-top:4px}.dg .slider-fg{height:100%}.dg .c input[type=checkbox]{margin-top:9px}.dg .c select{margin-top:5px}.dg .cr.function,.dg .cr.function .property-name,.dg .cr.function *,.dg .cr.boolean,.dg .cr.boolean *{cursor:pointer}.dg .selector{display:none;position:absolute;margin-left:-9px;margin-top:23px;z-index:10}.dg .c:hover .selector,.dg .selector.drag{display:block}.dg li.save-row{padding:0}.dg li.save-row .button{display:inline-block;padding:0px 6px}.dg.dialogue{background-color:#222;width:460px;padding:15px;font-size:13px;line-height:15px}#dg-new-constructor{padding:10px;color:#222;font-family:Monaco, monospace;font-size:10px;border:0;resize:none;box-shadow:inset 1px 1px 1px #888;word-wrap:break-word;margin:12px 0;display:block;width:440px;overflow-y:scroll;height:100px;position:relative}#dg-local-explain{display:none;font-size:11px;line-height:17px;border-radius:3px;background-color:#333;padding:8px;margin-top:10px}#dg-local-explain code{font-size:10px}#dat-gui-save-locally{display:none}.dg{color:#eee;font:11px 'Lucida Grande', sans-serif;text-shadow:0 -1px 0 #111}.dg.main::-webkit-scrollbar{width:5px;background:#1a1a1a}.dg.main::-webkit-scrollbar-corner{height:0;display:none}.dg.main::-webkit-scrollbar-thumb{border-radius:5px;background:#676767}.dg li:not(.folder){background:#1a1a1a;border-bottom:1px solid #2c2c2c}.dg li.save-row{line-height:25px;background:#dad5cb;border:0}.dg li.save-row select{margin-left:5px;width:108px}.dg li.save-row .button{margin-left:5px;margin-top:1px;border-radius:2px;font-size:9px;line-height:7px;padding:4px 4px 5px 4px;background:#c5bdad;color:#fff;text-shadow:0 1px 0 #b0a58f;box-shadow:0 -1px 0 #b0a58f;cursor:pointer}.dg li.save-row .button.gears{background:#c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;height:7px;width:8px}.dg li.save-row .button:hover{background-color:#bab19e;box-shadow:0 -1px 0 #b0a58f}.dg li.folder{border-bottom:0}.dg li.title{padding-left:16px;background:#000 url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;cursor:pointer;border-bottom:1px solid rgba(255,255,255,0.2)}.dg .closed li.title{background-image:url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==)}.dg .cr.boolean{border-left:3px solid #806787}.dg .cr.function{border-left:3px solid #e61d5f}.dg .cr.number{border-left:3px solid #2fa1d6}.dg .cr.number input[type=text]{color:#2fa1d6}.dg .cr.string{border-left:3px solid #1ed36f}.dg .cr.string input[type=text]{color:#1ed36f}.dg .cr.function:hover,.dg .cr.boolean:hover{background:#111}.dg .c input[type=text]{background:#303030;outline:none}.dg .c input[type=text]:hover{background:#3c3c3c}.dg .c input[type=text]:focus{background:#494949;color:#fff}.dg .c .slider{background:#303030;cursor:ew-resize}.dg .c .slider-fg{background:#2fa1d6}.dg .c .slider:hover{background:#3c3c3c}.dg .c .slider:hover .slider-fg{background:#44abda}\n", | |
dat.controllers.factory=function(e,a,c,d,f,b,n){return function(h,j,m,l){var o=h[j];if(n.isArray(m)||n.isObject(m))return new e(h,j,m);if(n.isNumber(o))return n.isNumber(m)&&n.isNumber(l)?new c(h,j,m,l):new a(h,j,{min:m,max:l});if(n.isString(o))return new d(h,j);if(n.isFunction(o))return new f(h,j,"");if(n.isBoolean(o))return new b(h,j)}}(dat.controllers.OptionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.StringController=function(e,a,c){var d= | |
function(c,b){function e(){h.setValue(h.__input.value)}d.superclass.call(this,c,b);var h=this;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"keyup",e);a.bind(this.__input,"change",e);a.bind(this.__input,"blur",function(){h.__onFinishChange&&h.__onFinishChange.call(h,h.getValue())});a.bind(this.__input,"keydown",function(a){a.keyCode===13&&this.blur()});this.updateDisplay();this.domElement.appendChild(this.__input)};d.superclass=e;c.extend(d.prototype, | |
e.prototype,{updateDisplay:function(){if(!a.isActive(this.__input))this.__input.value=this.getValue();return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common),dat.controllers.FunctionController,dat.controllers.BooleanController,dat.utils.common),dat.controllers.Controller,dat.controllers.BooleanController,dat.controllers.FunctionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.OptionController, | |
dat.controllers.ColorController=function(e,a,c,d,f){function b(a,b,c,d){a.style.background="";f.each(j,function(e){a.style.cssText+="background: "+e+"linear-gradient("+b+", "+c+" 0%, "+d+" 100%); "})}function n(a){a.style.background="";a.style.cssText+="background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);";a.style.cssText+="background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"; | |
a.style.cssText+="background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"}var h=function(e,l){function o(b){q(b);a.bind(window,"mousemove",q);a.bind(window, | |
"mouseup",j)}function j(){a.unbind(window,"mousemove",q);a.unbind(window,"mouseup",j)}function g(){var a=d(this.value);a!==false?(p.__color.__state=a,p.setValue(p.__color.toOriginal())):this.value=p.__color.toString()}function i(){a.unbind(window,"mousemove",s);a.unbind(window,"mouseup",i)}function q(b){b.preventDefault();var c=a.getWidth(p.__saturation_field),d=a.getOffset(p.__saturation_field),e=(b.clientX-d.left+document.body.scrollLeft)/c,b=1-(b.clientY-d.top+document.body.scrollTop)/c;b>1?b= | |
1:b<0&&(b=0);e>1?e=1:e<0&&(e=0);p.__color.v=b;p.__color.s=e;p.setValue(p.__color.toOriginal());return false}function s(b){b.preventDefault();var c=a.getHeight(p.__hue_field),d=a.getOffset(p.__hue_field),b=1-(b.clientY-d.top+document.body.scrollTop)/c;b>1?b=1:b<0&&(b=0);p.__color.h=b*360;p.setValue(p.__color.toOriginal());return false}h.superclass.call(this,e,l);this.__color=new c(this.getValue());this.__temp=new c(0);var p=this;this.domElement=document.createElement("div");a.makeSelectable(this.domElement, | |
false);this.__selector=document.createElement("div");this.__selector.className="selector";this.__saturation_field=document.createElement("div");this.__saturation_field.className="saturation-field";this.__field_knob=document.createElement("div");this.__field_knob.className="field-knob";this.__field_knob_border="2px solid ";this.__hue_knob=document.createElement("div");this.__hue_knob.className="hue-knob";this.__hue_field=document.createElement("div");this.__hue_field.className="hue-field";this.__input= | |
document.createElement("input");this.__input.type="text";this.__input_textShadow="0 1px 1px ";a.bind(this.__input,"keydown",function(a){a.keyCode===13&&g.call(this)});a.bind(this.__input,"blur",g);a.bind(this.__selector,"mousedown",function(){a.addClass(this,"drag").bind(window,"mouseup",function(){a.removeClass(p.__selector,"drag")})});var t=document.createElement("div");f.extend(this.__selector.style,{width:"122px",height:"102px",padding:"3px",backgroundColor:"#222",boxShadow:"0px 1px 3px rgba(0,0,0,0.3)"}); | |
f.extend(this.__field_knob.style,{position:"absolute",width:"12px",height:"12px",border:this.__field_knob_border+(this.__color.v<0.5?"#fff":"#000"),boxShadow:"0px 1px 3px rgba(0,0,0,0.5)",borderRadius:"12px",zIndex:1});f.extend(this.__hue_knob.style,{position:"absolute",width:"15px",height:"2px",borderRight:"4px solid #fff",zIndex:1});f.extend(this.__saturation_field.style,{width:"100px",height:"100px",border:"1px solid #555",marginRight:"3px",display:"inline-block",cursor:"pointer"});f.extend(t.style, | |
{width:"100%",height:"100%",background:"none"});b(t,"top","rgba(0,0,0,0)","#000");f.extend(this.__hue_field.style,{width:"15px",height:"100px",display:"inline-block",border:"1px solid #555",cursor:"ns-resize"});n(this.__hue_field);f.extend(this.__input.style,{outline:"none",textAlign:"center",color:"#fff",border:0,fontWeight:"bold",textShadow:this.__input_textShadow+"rgba(0,0,0,0.7)"});a.bind(this.__saturation_field,"mousedown",o);a.bind(this.__field_knob,"mousedown",o);a.bind(this.__hue_field,"mousedown", | |
function(b){s(b);a.bind(window,"mousemove",s);a.bind(window,"mouseup",i)});this.__saturation_field.appendChild(t);this.__selector.appendChild(this.__field_knob);this.__selector.appendChild(this.__saturation_field);this.__selector.appendChild(this.__hue_field);this.__hue_field.appendChild(this.__hue_knob);this.domElement.appendChild(this.__input);this.domElement.appendChild(this.__selector);this.updateDisplay()};h.superclass=e;f.extend(h.prototype,e.prototype,{updateDisplay:function(){var a=d(this.getValue()); | |
if(a!==false){var e=false;f.each(c.COMPONENTS,function(b){if(!f.isUndefined(a[b])&&!f.isUndefined(this.__color.__state[b])&&a[b]!==this.__color.__state[b])return e=true,{}},this);e&&f.extend(this.__color.__state,a)}f.extend(this.__temp.__state,this.__color.__state);this.__temp.a=1;var h=this.__color.v<0.5||this.__color.s>0.5?255:0,j=255-h;f.extend(this.__field_knob.style,{marginLeft:100*this.__color.s-7+"px",marginTop:100*(1-this.__color.v)-7+"px",backgroundColor:this.__temp.toString(),border:this.__field_knob_border+ | |
"rgb("+h+","+h+","+h+")"});this.__hue_knob.style.marginTop=(1-this.__color.h/360)*100+"px";this.__temp.s=1;this.__temp.v=1;b(this.__saturation_field,"left","#fff",this.__temp.toString());f.extend(this.__input.style,{backgroundColor:this.__input.value=this.__color.toString(),color:"rgb("+h+","+h+","+h+")",textShadow:this.__input_textShadow+"rgba("+j+","+j+","+j+",.7)"})}});var j=["-moz-","-o-","-webkit-","-ms-",""];return h}(dat.controllers.Controller,dat.dom.dom,dat.color.Color=function(e,a,c,d){function f(a, | |
b,c){Object.defineProperty(a,b,{get:function(){if(this.__state.space==="RGB")return this.__state[b];n(this,b,c);return this.__state[b]},set:function(a){if(this.__state.space!=="RGB")n(this,b,c),this.__state.space="RGB";this.__state[b]=a}})}function b(a,b){Object.defineProperty(a,b,{get:function(){if(this.__state.space==="HSV")return this.__state[b];h(this);return this.__state[b]},set:function(a){if(this.__state.space!=="HSV")h(this),this.__state.space="HSV";this.__state[b]=a}})}function n(b,c,e){if(b.__state.space=== | |
"HEX")b.__state[c]=a.component_from_hex(b.__state.hex,e);else if(b.__state.space==="HSV")d.extend(b.__state,a.hsv_to_rgb(b.__state.h,b.__state.s,b.__state.v));else throw"Corrupted color state";}function h(b){var c=a.rgb_to_hsv(b.r,b.g,b.b);d.extend(b.__state,{s:c.s,v:c.v});if(d.isNaN(c.h)){if(d.isUndefined(b.__state.h))b.__state.h=0}else b.__state.h=c.h}var j=function(){this.__state=e.apply(this,arguments);if(this.__state===false)throw"Failed to interpret color arguments";this.__state.a=this.__state.a|| | |
1};j.COMPONENTS="r,g,b,h,s,v,hex,a".split(",");d.extend(j.prototype,{toString:function(){return c(this)},toOriginal:function(){return this.__state.conversion.write(this)}});f(j.prototype,"r",2);f(j.prototype,"g",1);f(j.prototype,"b",0);b(j.prototype,"h");b(j.prototype,"s");b(j.prototype,"v");Object.defineProperty(j.prototype,"a",{get:function(){return this.__state.a},set:function(a){this.__state.a=a}});Object.defineProperty(j.prototype,"hex",{get:function(){if(!this.__state.space!=="HEX")this.__state.hex= | |
a.rgb_to_hex(this.r,this.g,this.b);return this.__state.hex},set:function(a){this.__state.space="HEX";this.__state.hex=a}});return j}(dat.color.interpret,dat.color.math=function(){var e;return{hsv_to_rgb:function(a,c,d){var e=a/60-Math.floor(a/60),b=d*(1-c),n=d*(1-e*c),c=d*(1-(1-e)*c),a=[[d,c,b],[n,d,b],[b,d,c],[b,n,d],[c,b,d],[d,b,n]][Math.floor(a/60)%6];return{r:a[0]*255,g:a[1]*255,b:a[2]*255}},rgb_to_hsv:function(a,c,d){var e=Math.min(a,c,d),b=Math.max(a,c,d),e=b-e;if(b==0)return{h:NaN,s:0,v:0}; | |
a=a==b?(c-d)/e:c==b?2+(d-a)/e:4+(a-c)/e;a/=6;a<0&&(a+=1);return{h:a*360,s:e/b,v:b/255}},rgb_to_hex:function(a,c,d){a=this.hex_with_component(0,2,a);a=this.hex_with_component(a,1,c);return a=this.hex_with_component(a,0,d)},component_from_hex:function(a,c){return a>>c*8&255},hex_with_component:function(a,c,d){return d<<(e=c*8)|a&~(255<<e)}}}(),dat.color.toString,dat.utils.common),dat.color.interpret,dat.utils.common),dat.utils.requestAnimationFrame=function(){return window.webkitRequestAnimationFrame|| | |
window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(e){window.setTimeout(e,1E3/60)}}(),dat.dom.CenteredDiv=function(e,a){var c=function(){this.backgroundElement=document.createElement("div");a.extend(this.backgroundElement.style,{backgroundColor:"rgba(0,0,0,0.8)",top:0,left:0,display:"none",zIndex:"1000",opacity:0,WebkitTransition:"opacity 0.2s linear"});e.makeFullscreen(this.backgroundElement);this.backgroundElement.style.position="fixed";this.domElement= | |
document.createElement("div");a.extend(this.domElement.style,{position:"fixed",display:"none",zIndex:"1001",opacity:0,WebkitTransition:"-webkit-transform 0.2s ease-out, opacity 0.2s linear"});document.body.appendChild(this.backgroundElement);document.body.appendChild(this.domElement);var c=this;e.bind(this.backgroundElement,"click",function(){c.hide()})};c.prototype.show=function(){var c=this;this.backgroundElement.style.display="block";this.domElement.style.display="block";this.domElement.style.opacity= | |
0;this.domElement.style.webkitTransform="scale(1.1)";this.layout();a.defer(function(){c.backgroundElement.style.opacity=1;c.domElement.style.opacity=1;c.domElement.style.webkitTransform="scale(1)"})};c.prototype.hide=function(){var a=this,c=function(){a.domElement.style.display="none";a.backgroundElement.style.display="none";e.unbind(a.domElement,"webkitTransitionEnd",c);e.unbind(a.domElement,"transitionend",c);e.unbind(a.domElement,"oTransitionEnd",c)};e.bind(this.domElement,"webkitTransitionEnd", | |
c);e.bind(this.domElement,"transitionend",c);e.bind(this.domElement,"oTransitionEnd",c);this.backgroundElement.style.opacity=0;this.domElement.style.opacity=0;this.domElement.style.webkitTransform="scale(1.1)"};c.prototype.layout=function(){this.domElement.style.left=window.innerWidth/2-e.getWidth(this.domElement)/2+"px";this.domElement.style.top=window.innerHeight/2-e.getHeight(this.domElement)/2+"px"};return c}(dat.dom.dom,dat.utils.common),dat.dom.dom,dat.utils.common); | |
(function(){ | |
//------------------------------ | |
// Mesh Properties | |
//------------------------------ | |
var MESH = { | |
width: 1.2, | |
height: 1.2, | |
slices: 250, | |
ambient: '#555555', | |
diffuse: '#FFFFFF' | |
}; | |
//------------------------------ | |
// Light Properties | |
//------------------------------ | |
var LIGHT = { | |
count: 1, | |
xPos : 0, | |
yPos : 200, | |
zOffset: 100, | |
ambient: '#880066', | |
diffuse: '#FF8800', | |
pickedup :true, | |
proxy : false, | |
currIndex : 0 | |
}; | |
//------------------------------ | |
// Render Properties | |
//------------------------------ | |
var WEBGL = 'webgl'; | |
var CANVAS = 'canvas'; | |
var SVG = 'svg'; | |
var RENDER = { | |
renderer: CANVAS | |
}; | |
//------------------------------ | |
// Export Properties | |
//------------------------------ | |
var EXPORT = { | |
width: 2000, | |
height: 1000, | |
exportCurrent: function(){ | |
switch(RENDER.renderer) { | |
case WEBGL: | |
window.open(webglRenderer.element.toDataURL(), '_blank'); | |
break; | |
case CANVAS: | |
window.open(canvasRenderer.element.toDataURL(), '_blank'); | |
break; | |
case SVG: | |
var data = encodeURIComponent(output.innerHTML); | |
var url = "data:image/svg+xml," + data; | |
window.open(url, '_blank'); | |
break; | |
} | |
}, | |
export: function() { | |
var l, x, y, light, | |
scalarX = this.width / renderer.width, | |
scalarY = this.height / renderer.height; | |
// store a temp value of the slices | |
var slices = MESH.slices; | |
// Increase or decrease number of slices depending on the size of the canvas | |
MESH.slices = Math.ceil(slices*scalarX*1.3); | |
// Regenerate the whole canvas | |
resize(this.width, this.height); | |
// restore the number of slices | |
MESH.slices = slices; | |
// Move the lights on the plane to accomodate the size of the canvas | |
for (l = scene.lights.length - 1; l >= 0; l--) { | |
light = scene.lights[l]; | |
x = light.position[0]; | |
y = light.position[1]; | |
z = light.position[2]; | |
FSS.Vector3.set(light.position, x*scalarX, y*scalarY, z*scalarX); | |
} | |
// Render the canvas | |
render(); | |
switch(RENDER.renderer) { | |
case WEBGL: | |
window.open(webglRenderer.element.toDataURL(), '_blank'); | |
break; | |
case CANVAS: | |
window.open(canvasRenderer.element.toDataURL(), '_blank'); | |
break; | |
case SVG: | |
var data = encodeURIComponent(output.innerHTML); | |
var url = "data:image/svg+xml," + data; | |
window.open(url, '_blank'); | |
break; | |
} | |
resize(container.offsetWidth, container.offsetHeight); | |
for (l = scene.lights.length - 1; l >= 0; l--) { | |
light = scene.lights[l]; | |
x = light.position[0]; | |
y = light.position[1]; | |
z = light.position[2]; | |
FSS.Vector3.set(light.position, x/scalarX, y/scalarY, z/scalarX); | |
} | |
} | |
}; | |
//------------------------------ | |
// Global Properties | |
//------------------------------ | |
var center = FSS.Vector3.create(); | |
var container = document.getElementById('container'); | |
var controls = document.getElementById('controls'); | |
var output = document.getElementById('output'); | |
var renderer, scene, mesh, geometry, material; | |
var webglRenderer, canvasRenderer, svgRenderer; | |
var gui; | |
//------------------------------ | |
// Methods | |
//------------------------------ | |
function initialise() { | |
createRenderer(); | |
createScene(); | |
createMesh(); | |
addLight(); | |
addEventListeners(); | |
addControls(); | |
resize(container.offsetWidth, container.offsetHeight); | |
animate(); | |
} | |
function createRenderer() { | |
webglRenderer = new FSS.WebGLRenderer(); | |
canvasRenderer = new FSS.CanvasRenderer(); | |
svgRenderer = new FSS.SVGRenderer(); | |
setRenderer(RENDER.renderer); | |
} | |
function setRenderer(index) { | |
if (renderer) { | |
output.removeChild(renderer.element); | |
} | |
switch(index) { | |
case WEBGL: | |
renderer = webglRenderer; | |
break; | |
case CANVAS: | |
renderer = canvasRenderer; | |
break; | |
case SVG: | |
renderer = svgRenderer; | |
break; | |
} | |
renderer.setSize(container.offsetWidth, container.offsetHeight); | |
output.appendChild(renderer.element); | |
} | |
function createScene() { | |
scene = new FSS.Scene(); | |
} | |
function createMesh() { | |
scene.remove(mesh); | |
renderer.clear(); | |
geometry = new FSS.Plane(MESH.width * renderer.width, MESH.height * renderer.height, MESH.slices); | |
material = new FSS.Material(MESH.ambient, MESH.diffuse); | |
mesh = new FSS.Mesh(geometry, material); | |
scene.add(mesh); | |
} | |
// Add a single light | |
function addLight() { | |
renderer.clear(); | |
light = new FSS.Light(LIGHT.ambient, LIGHT.diffuse); | |
light.ambientHex = light.ambient.format(); | |
light.diffuseHex = light.diffuse.format(); | |
light.setPosition(LIGHT.xPos, LIGHT.yPos, LIGHT.zOffset); | |
scene.add(light); | |
LIGHT.proxy = light; | |
LIGHT.pickedup = true; | |
LIGHT.currIndex++; | |
} | |
// Remove lights | |
function trimLights(value) { | |
LIGHT.proxy = scene.lights[value]; | |
for (l = value; l >= scene.lights.length - 1; l--) { | |
light = scene.lights[l]; | |
scene.remove(light); | |
} | |
renderer.clear(); | |
} | |
// Resize canvas | |
function resize(width, height) { | |
renderer.setSize(width, height); | |
FSS.Vector3.set(center, renderer.halfWidth, renderer.halfHeight); | |
createMesh(); | |
} | |
function animate() { | |
render(); | |
requestAnimationFrame(animate); | |
} | |
function render() { | |
renderer.render(scene); | |
} | |
function addEventListeners() { | |
window.addEventListener('resize', onWindowResize); | |
container.addEventListener('mousemove', onMouseMove); | |
} | |
function addControls() { | |
var i, l, light, folder, controller; | |
// Create GUI | |
gui = new dat.GUI({autoPlace:false}); | |
controls.appendChild(gui.domElement); | |
// Create folders | |
renderFolder = gui.addFolder('Render'); | |
meshFolder = gui.addFolder('Mesh'); | |
lightFolder = gui.addFolder('Light'); | |
exportFolder = gui.addFolder('Export'); | |
// Open folders | |
lightFolder.open(); | |
// Add Render Controls | |
controller = renderFolder.add(RENDER, 'renderer', {webgl:WEBGL, canvas:CANVAS, svg:SVG}); | |
controller.onChange(function(value) { | |
setRenderer(value); | |
}); | |
// Add Mesh Controls | |
controller = meshFolder.addColor(MESH, 'ambient'); | |
controller.onChange(function(value) { | |
for (i = 0, l = scene.meshes.length; i < l; i++) { | |
scene.meshes[i].material.ambient.set(value); | |
} | |
}); | |
controller = meshFolder.addColor(MESH, 'diffuse'); | |
controller.onChange(function(value) { | |
for (i = 0, l = scene.meshes.length; i < l; i++) { | |
scene.meshes[i].material.diffuse.set(value); | |
} | |
}); | |
controller = meshFolder.add(MESH, 'width', 0.05, 2); | |
controller.onChange(function(value) { | |
if (geometry.width !== value * renderer.width) { createMesh(); } | |
}); | |
controller = meshFolder.add(MESH, 'height', 0.05, 2); | |
controller.onChange(function(value) { | |
if (geometry.height !== value * renderer.height) { createMesh(); } | |
}); | |
controller = meshFolder.add(MESH, 'slices', 1, 800); | |
controller.step(1); | |
controller.onChange(function(value) { | |
if (geometry.slices !== value) { createMesh(); } | |
}); | |
// Add Light Controls | |
// TODO: add the number of lights dynamically | |
controller = lightFolder.add(LIGHT, 'currIndex', {1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7}).name('Current light').listen(); | |
controller.onChange(function(value) { | |
LIGHT.proxy = scene.lights[value-1]; | |
LIGHT.ambient = LIGHT.proxy.ambient.hex; | |
LIGHT.diffuse = LIGHT.proxy.diffuse.hex; | |
LIGHT.xPos = LIGHT.proxy.position[0]; | |
LIGHT.yPos = LIGHT.proxy.position[1]; | |
LIGHT.zOffset = LIGHT.proxy.position[2]; | |
}); | |
controller = lightFolder.addColor(LIGHT, 'ambient').listen(); | |
controller.onChange(function(value) { | |
LIGHT.proxy.ambient.set(value); | |
LIGHT.proxy.ambientHex = LIGHT.proxy.ambient.format(); | |
}); | |
controller = lightFolder.addColor(LIGHT, 'diffuse').listen(); | |
controller.onChange(function(value) { | |
console.log(value); | |
LIGHT.proxy.diffuse.set(value); | |
LIGHT.proxy.diffuseHex = LIGHT.proxy.ambient.format(); | |
}); | |
controller = lightFolder.add(LIGHT, 'count', 1, 7).listen(); | |
controller.step(1); | |
controller.onChange(function(value) { | |
if (scene.lights.length !== value) { | |
// If the value is more then the number of lights, add lights, otherwise delete lights | |
if (value > scene.lights.length) { | |
addLight(); | |
} else { | |
trimLights(value); | |
} | |
} | |
}); | |
controller = lightFolder.add(LIGHT, 'xPos', -mesh.geometry.width/2, mesh.geometry.width/2).listen(); | |
controller.step(1); | |
controller.onChange(function(value) { | |
LIGHT.proxy.setPosition(value, LIGHT.proxy.position[1], LIGHT.proxy.position[2]); | |
}); | |
controller = lightFolder.add(LIGHT, 'yPos', -mesh.geometry.height/2, mesh.geometry.height/2).listen(); | |
controller.step(1); | |
controller.onChange(function(value) { | |
LIGHT.proxy.setPosition(LIGHT.proxy.position[0], value, LIGHT.proxy.position[2]); | |
}); | |
controller = lightFolder.add(LIGHT, 'zOffset', 0, 1000).name('Distance').listen(); | |
controller.step(1); | |
controller.onChange(function(value) { | |
LIGHT.proxy.setPosition(LIGHT.proxy.position[0], LIGHT.proxy.position[1], value); | |
}); | |
// Add Export Controls | |
controller = exportFolder.add(EXPORT, 'width', 100, 3000); | |
controller.step(100); | |
controller = exportFolder.add(EXPORT, 'height', 100, 3000); | |
controller.step(100); | |
controller = exportFolder.add(EXPORT, 'export').name('export big'); | |
controller = exportFolder.add(EXPORT, 'exportCurrent').name('export this'); | |
} | |
function toggleEl(id) { | |
var e = document.getElementById(id); | |
if(e.style.display == 'block') | |
e.style.display = 'none'; | |
else | |
e.style.display = 'block'; | |
} | |
//------------------------------ | |
// Callbacks | |
//------------------------------ | |
function onWindowResize(event) { | |
resize(container.offsetWidth, container.offsetHeight); | |
render(); | |
} | |
function onMouseMove(event) { | |
if(LIGHT.pickedup){ | |
LIGHT.xPos = event.x - renderer.width/2; | |
LIGHT.yPos = renderer.height/2 -event.y; | |
LIGHT.proxy.setPosition(LIGHT.xPos, LIGHT.yPos, LIGHT.proxy.position[2]); | |
} | |
} | |
// Hide the controls completely on pressing H | |
Mousetrap.bind('H', function() { | |
toggleEl('controls') | |
}); | |
// Add a light on ENTER key | |
Mousetrap.bind('enter', function() { | |
LIGHT.count++; | |
addLight(); | |
}); | |
// Pick up the light when a space is pressed | |
Mousetrap.bind('space', function() { | |
LIGHT.pickedup = !LIGHT.pickedup; | |
}); | |
// Let there be light! | |
initialise(); | |
})(); |
@import url(https://fonts.googleapis.com/css?family=Gabriela|Dosis:200); | |
body { | |
font-family: "Vidaloka", serif; | |
font-smoothing: antialiased; | |
font-weight: normal; | |
background: #151618; | |
color: #FFF; | |
margin: 0; | |
} | |
h1 { | |
text-transform: uppercase; | |
letter-spacing: 4px; | |
font-weight: normal; | |
font-size: 16px; | |
} | |
p { | |
line-height: 1.6em; | |
text-align: center; | |
font-size: 14px; | |
} | |
p + p { | |
margin-top: 1.0em; | |
} | |
a { | |
transition: color 0.2s ease-out; | |
text-decoration: none; | |
color: #FBB; | |
} | |
a:hover { | |
color: #444; | |
} | |
.overlay { | |
pointer-events: none; | |
position: absolute; | |
height: 100%; | |
width: 100%; | |
left: 0; | |
top: 0; | |
} | |
.container { | |
position: absolute; | |
height: 100%; | |
width: 100%; | |
} | |
.controls { | |
font-smoothing: subpixel-antialiased; | |
position: absolute; | |
right: 20px; | |
top: 0; | |
} | |
.links { | |
position: absolute; | |
list-style: none; | |
width: 120px; | |
bottom: 40px; | |
right: 20px; | |
padding: 0; | |
margin: 0; | |
} | |
.links p{ | |
font-smoothing: subpixel-antialiased; | |
font-family: 'Dosis', sans-serif; | |
text-transform: uppercase; | |
text-align: center; | |
padding: 0.25em 0; | |
letter-spacing: 2px; | |
font-weight: 200; | |
font-size: 10px; | |
display: block; | |
width: 100%; | |
opacity: 0.6; | |
color: #FFF; | |
} | |
.links li { | |
border-bottom: #222 1px solid; | |
border-bottom: rgba(255,255,255,0.1) 1px solid; | |
} | |
.links li a { | |
transition: opacity 0.2s ease-out; | |
font-smoothing: subpixel-antialiased; | |
font-family: 'Dosis', sans-serif; | |
text-transform: uppercase; | |
text-align: right; | |
padding: 0.25em 0; | |
letter-spacing: 2px; | |
font-weight: 200; | |
font-size: 12px; | |
display: block; | |
width: 100%; | |
opacity: 0.4; | |
color: #FFF; | |
} | |
.links li a:hover { | |
opacity: 0.8; | |
} | |
.links li .who:before { | |
opacity: 0.5; | |
content: '@'; | |
float: left; | |
} | |
.links li .source:before { | |
opacity: 0.5; | |
content: '{}'; | |
float: left; | |
} | |
.links li .blog:before { | |
opacity: 0.5; | |
content: '&'; | |
float: left; | |
} | |
.hide { | |
opacity: 0; | |
} |
This is my open source project that allows creating some incredible wallpapers/web site graphics, feel free to improve on it and use it for commercial or free projects
A Pen by Maksim Surguy on CodePen.