用HTML实现拓扑面,动态4D圆环面,可手动调节,富有创新性的案例。(有源代码)


前言

如果你觉得对代码进行复制粘贴很麻烦的话,你可以直接将资源下载到本地。无需部署,直接可以运行。

一、示例

在这里插入图片描述

二、目录结构

在这里插入图片描述

三、index.html(主页面)

<!DOCTYPE html>
<title>WanderTp</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/style.css">

<script src='swissgl.js'></script>
<script src='audio.js'></script>
<script src="dat.gui.min.js"> </script>


<script src='js/Torus4d.js'></script>
<script src='js/main.js'></script>

<div id="demo">
	<canvas id="c" width="640" height="360"></canvas></div>
<script>
	'use strict';
	const app = new DemoApp([
		Torus4d,
	]);

	
</script>

四、main.js

'use strict';

const $ = s=>document.querySelector(s);
const setDisplay = (el, val)=>{if ($(el)) $(el).style.display = val};


class DemoApp {
    constructor(demos, defaultDemo='ParticleLife3d') {
        this.singleMode = demos.length == 1;
        if (this.singleMode) {
            defaultDemo = demos[0].name;
        }
        this.demos = Object.fromEntries(demos.map(c=>[c.name, c]));

        this.canvas = document.getElementById('c');
        const gl = this.canvas.getContext('webgl2', {alpha:false, antialias:true,
            xrCompatible:true});
        this.glsl = SwissGL(gl);
        this.demo = null;
        this.gui = null;

        this.xrDemos =  Object.values(this.demos).filter(f=>f.Tags&&f.Tags.includes('3d'));
        this.xrSession = null;
        this.xrRefSpace = null;
        this.xrPose = null;
        this.lookUpStartTime = 0;
        this.haveVR = this.haveAR = false;
        if (navigator.xr) {
            navigator.xr.isSessionSupported('immersive-vr').then(supported=>{
                this.haveVR = supported;
                this.updateVRButtons();
            })
            navigator.xr.isSessionSupported('immersive-ar').then(supported=>{
                this.haveAR = supported;
                this.updateVRButtons();
            })
        }

        this.viewParams = {
            DPR: window.devicePixelRatio,
            canvasSize: new Float32Array(2),
            pointer: new Float32Array(3),
            cameraYPD: new Float32Array(3),
            xrRay: new Float32Array(16*2),
            xrRayInv: new Float32Array(16*2),
            xrButton: new Float32Array(4*2),
        };
        this.resetCamera();

        this.glsl_include = `
            uniform bool xrMode;
            uniform mat4 xrProjectionMatrix, xrViewMatrix;
            uniform mat4 xrRay[2], xrRayInv[2];
            uniform vec4 xrButton[2];
            uniform vec3 xrPosition;
            
            uniform vec3 cameraYPD;
            vec3 cameraPos() {
                if (xrMode) return xrPosition;
                vec3 p = vec3(0, 0, cameraYPD.z);
                p.yz *= rot2(-cameraYPD.y);
                p.xy *= rot2(-cameraYPD.x);
                return p;
            }
            vec4 wld2view(vec4 p) {
                if (xrMode) return xrViewMatrix * p;
                p.xy *= rot2(cameraYPD.x);
                p.yz *= rot2(cameraYPD.y);
                p.z -= cameraYPD.z;
                return p;
            }
            vec4 view2proj(vec4 p) {
                if (xrMode) return xrProjectionMatrix*p;
                const float near = 0.1, far = 10.0, fov = 1.0;
                return vec4(p.xy/tan(fov/2.0),
                    (p.z*(near+far)+2.0*near*far)/(near-far), -p.z);
            }
            vec4 wld2proj(vec4 p) {
                return view2proj(wld2view(p));
            }
            vec4 wld2proj(vec3 p) {
                return wld2proj(vec4(p,1.0));
            }
        `;
        const glsl = this.glsl;
        this.withCamera = (params, target)=>{
            params = {...params, Inc:[this.glsl_include].concat(params.Inc||[])};
            if (target || !params.xrMode) {
                return glsl(params, target);
            }
            delete params.Aspect;
            let glLayer = this.xrSession.renderState.baseLayer;
            target = {bindTarget:(gl)=>{
                gl.bindFramebuffer(gl.FRAMEBUFFER, glLayer.framebuffer);
                return [glLayer.framebufferWidth, glLayer.framebufferHeight];
            }}
            for (let view of this.xrPose.views) {
                const vp = glLayer.getViewport(view);
                params.View = [vp.x, vp.y, vp.width, vp.height];
                params.xrProjectionMatrix = view.projectionMatrix;
                params.xrViewMatrix = view.transform.inverse.matrix;
                let {x,y,z} = view.transform.position;
                params.xrPosition = [x, y, z];
                glsl(params, target);
            }
        };

        const setPointer = (e, buttons)=>{
            const [w, h] = this.viewParams.canvasSize;
            const [x, y] = [e.offsetX-w/2, h/2-e.offsetY];
            this.viewParams.pointer.set([x, y, buttons]);
            return [x, y];
        };
        this.canvas.addEventListener('pointerdown', e=>{
            if (!e.isPrimary) return;
            setPointer(e, e.buttons);
        });
        this.canvas.addEventListener('pointerout', e=>setPointer(e, 0));
        this.canvas.addEventListener('pointerup', e=>setPointer(e, 0));
        this.canvas.addEventListener('pointermove', e=>{
            const [px, py, _] = this.viewParams.pointer;
            const [x, y] = setPointer(e, e.buttons);
            if (!e.isPrimary || e.buttons != 1) return;
            let [yaw, pitch, dist] = this.viewParams.cameraYPD;
            yaw -= (x-px)*0.01;
            pitch += (y-py)*0.01;
            pitch = Math.min(Math.max(pitch, 0), Math.PI);
            this.viewParams.cameraYPD.set([yaw, pitch, dist]);
        });

        let name = location.hash.slice(1);
        if (!(name in this.demos)) {
            name = defaultDemo;
        }
        this.runDemo(name);
        this.populatePreviews();

        requestAnimationFrame(this.frame.bind(this));
    }

    resetCamera() {
        this.viewParams.cameraYPD.set([Math.PI*3/4, Math.PI/4, 1.8]);
    }

    frame(t) {
        requestAnimationFrame(this.frame.bind(this));
        if (this.xrSession) return; // skip canvas frames when XR is running
        this.glsl.adjustCanvas();
        this.viewParams.canvasSize.set([this.canvas.clientWidth, this.canvas.clientHeight]);
        this.viewParams.DPR = window.devicePixelRatio;
        
        this.demo.frame(this.withCamera, {
            time:t/1000.0, xrMode: false,
            ...this.viewParams,
        });
    }

    xrFrame(t, xrFrame) {
        this.xrSession.requestAnimationFrame(this.xrFrame.bind(this));
        this.xrPose = xrFrame.getViewerPose(this.xrRefSpace);
        if (!this.xrPose) return;
        this.viewParams.xrRay.fill(0.0);
        this.viewParams.xrRayInv.fill(0.0);
        this.viewParams.xrButton.fill(0.0);
        const params = {time:t/1000.0, xrMode: true, ...this.viewParams};
        for (let i=0; i<2; ++i) {
            const inputSource = this.xrSession.inputSources[i];
            if (inputSource && inputSource.gamepad && inputSource.gamepad.buttons) {
                inputSource.gamepad.buttons.forEach((btn, btnIdx)=>{
                    if (btnIdx<4) this.viewParams.xrButton[i*4+btnIdx] = btn.value || btn.pressed;
                });
            }
            if (!inputSource || !inputSource.targetRaySpace) continue;
            const pose = xrFrame.getPose(inputSource.targetRaySpace, this.xrRefSpace);
            if (!pose) continue;
            this.viewParams.xrRay.set(pose.transform.matrix, i*16);
            this.viewParams.xrRayInv.set(pose.transform.inverse.matrix, i*16);
        }
        
        this.demo.frame(this.withCamera, params);
        this.withCamera({...params, Mesh: [20,20], Grid:[2], DepthTest:1, VP:`
            varying vec3 p = uv2sphere(UV);
            varying vec4 buttons = xrButton[ID.x];
            VPos = wld2proj(xrRay[ID.x]*vec4(p*vec3(0.02, 0.02, 0.1),1));`, FP:`
            vec3 c = p*0.5+0.5;
            FOut = vec4(c*0.5,1);
            float b = c.z*4.0;
            if (b<4.0 && buttons[int(b)]>fract(b)) FOut += 0.5;`});

        const lookUpCoef = -this.xrPose.transform.matrix[10];
        if (!this.singleMode && (lookUpCoef>0.5)) {
            const dt = (t-this.lookUpStartTime) / 1000;
            if (dt > 1) {
                this.lookUpStartTime = t;
                let i = this.xrDemos.indexOf(this.demo.constructor);
                i = (i+1)%this.xrDemos.length;
                this.runDemo(this.xrDemos[i].name);
            } else {
                this.withCamera({...params, Mesh: [20,20], dt, DepthTest:1, VP:`
                vec3 p = uv2sphere(UV)*0.6*clamp(1.0-dt, 0.0, 0.8) + vec3(-2.0, 0.0, 3.0);
                VPos = wld2proj(vec4(p,1));`, FP:`UV,0.5,1`});
            }
        } else {
            this.lookUpStartTime = t;
        }
    }

    toggleXR(xr) {
        if (!this.xrSession) {
            navigator.xr.requestSession(`immersive-${xr}`).then(session=>{
                this.xrSession = session;
                session.addEventListener('end', ()=>{this.xrSession = null;});
                session.updateRenderState({ baseLayer: new XRWebGLLayer(session, this.glsl.gl) });
                session.requestReferenceSpace('local').then((refSpace) => {
                    this.xrRefSpace = refSpace.getOffsetReferenceSpace(
                        new XRRigidTransform({x:0,y:-0.25,z:-1.0,w:1},   // position offset
                                             {x:0.5,y:0.5,z:0.5,w:-0.5}) // rotate z up
                    );
                    session.requestAnimationFrame(this.xrFrame.bind(this));
                  });
            });
        } else {
            this.xrSession.end();
        }
    }

    runDemo(name) {
        if (this.demo) {
            if (this.gui) this.gui.destroy();
            if (this.demo.free) this.demo.free();
            this.glsl.reset();
            this.demo = this.gui = null;
        }
        if (!this.singleMode) location.hash = name;
        if (self.dat) {
            this.gui = new dat.GUI();
            this.gui.domElement.id = 'gui'
            this.gui.hide();
        }
        this.demo = new this.demos[name](this.withCamera, this.gui);
        if (this.gui && (this.gui.__controllers.length == 0)) {
            this.gui.destroy();
            this.gui = null;
        }
        setDisplay('#settingButton', this.gui?'block':'none');
        if ($('#sourceLink')) {
            $('#sourceLink').href = `https://github.com/google/swissgl/blob/main/demo/${name}.js`;
        }
        this.updateVRButtons();
        this.resetCamera();
    }

    updateVRButtons() {
        setDisplay('#vrButton', 'none');
        setDisplay('#arButton', 'none');
        const tags = this.demo && this.demo.constructor.Tags;
        if (tags && tags.includes('3d')) {
            if (this.haveVR ) setDisplay('#vrButton', 'block');
            if (this.haveAR ) setDisplay('#arButton', 'block');
        }
    }

    populatePreviews() {
        const panel = document.getElementById('cards');
        if (!panel) return;
        Object.keys(this.demos).forEach(name=>{
            const el = document.createElement('div');
            el.classList.add('card');
            el.innerHTML = `<img src="demo/preview/${name}.jpg">${name}`;
            el.addEventListener('click', ()=>this.runDemo(name));
            panel.appendChild(el);
        });
    }

    // helper function to render demo preview images
    genPreviews() {
        const panel = document.getElementById('cards');
        panel.innerHTML = '';
        const canvas = document.createElement('canvas');
        canvas.width = 400; canvas.height = 300;
        const glsl = SwissGL(canvas);
        const withCamera = (params, target)=>glsl({...params, 
            Inc:[this.glsl_include].concat(params.Inc||[])}, target);
        Object.keys(this.demos).forEach(name=>{
            if (name == 'Spectrogram') return;
            const dummyGui = new dat.GUI();
            const demo = new this.demos[name](withCamera, dummyGui);
            dummyGui.destroy();
            this.resetCamera();
            for (let i=0; i<60*5; ++i) {
                withCamera({Clear:0}, '')
                demo.frame(withCamera, {time:i/60.0, ...this.viewParams});
            }
            const el = document.createElement('div')
            const data = canvas.toDataURL('image/jpeg', 0.95);
            el.innerHTML = `
             <a href="${data}" download="${name}.jpg"><img src="${data}"></a>
             ${name}`;
            panel.appendChild(el)
            if (demo.free) demo.free();
            glsl.reset();
        })
    }

    toggleGui() {
        if (!this.gui) return;
        const style = this.gui.domElement.style;
        style.display = (style.display == 'none')?'':'none'
    }

    fullscreen() {
        const {canvas} = this;
        const f = canvas.requestFullscreen || canvas.webkitRequestFullscreen;
        if (f) f.apply(canvas);
    }

}

五、Tour4D.js

/** @license 
  * Copyright 2023 Google LLC.
  * SPDX-License-Identifier: Apache-2.0 
  */
class Torus4d {
    static Tags = ['3d'];
    frame(glsl, params) {
        glsl({...params, Mesh:[100,100], Aspect:'fit', 
            AlphaCoverage:1, DepthTest:1, VP:`
            vec4 p = vec4(cos(XY*PI), sin(XY*PI))*0.6;
            p.xw *= rot2(time*0.4);
            VPos = wld2proj(vec4(p.xyz/(1.0-p.w)*0.5, 1));`, FP:`
            vec2 v = UV*rot2(PI/4.)*64.0/sqrt(2.);
            v = smoothstep(0.0, 1.0, (abs(v-round(v))-0.02)/fwidth(v));
            float a = 1.0-v.x*v.y;
            if (a<0.1) discard;
            FOut = vec4(gl_FrontFacing?vec3(.9,.9,.6):vec3(.6,.6,.9), a);`});
    }
}

六、swissgl.js

// Copyright 2023 Google LLC

// 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

//     https://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


// Repeat/Loop?
// fbo:
// - multiple named render targets (Out...?)
// - stencil?
// - mipmaps?
// data texture subimage?
// integer textures
// glsl lib
// - hash (overloads)
// - 3d prim/helpers
// - universal geom (mesh)
// devicePixelRatio
// depth test modes

// pain points:
// - view transform params
// - fragment only aspect
// - tag already exists
// - texture/array uniform compatibility

const Type2Setter = {};
const UniformType2TexTarget = {};
const TextureFormats = {}; {
	const GL = WebGL2RenderingContext;
	for (const t of ['FLOAT', 'INT', 'BOOL']) {
		const suf = t == 'FLOAT' ? 'f' : 'i';
		Type2Setter[GL[t]] = 'uniform1' + suf;
		for (const i of [2, 3, 4]) {
			Type2Setter[GL[`${t}_VEC${i}`]] = `uniform${i}${suf}v`;
			if (suf == 'f') {
				Type2Setter[GL[`${t}_MAT${i}`]] = `uniformMatrix${i}fv`;
			}
		}
	}
	UniformType2TexTarget[GL.SAMPLER_2D] = GL.TEXTURE_2D;
	UniformType2TexTarget[GL.SAMPLER_2D_ARRAY] = GL.TEXTURE_2D_ARRAY;

	for (const [name, internalFormat, glformat, type, CpuArray, chn] of [
			['r8', GL.R8, GL.RED, GL.UNSIGNED_BYTE, Uint8Array, 1],
			['rgba8', GL.RGBA8, GL.RGBA, GL.UNSIGNED_BYTE, Uint8Array, 4],
			['r16f', GL.R16F, GL.RED, GL.HALF_FLOAT, Uint16Array, 1],
			['rgba16f', GL.RGBA16F, GL.RGBA, GL.HALF_FLOAT, Uint16Array, 4],
			['r32f', GL.R32F, GL.RED, GL.FLOAT, Float32Array, 1],
			['rg32f', GL.RG32F, GL.RG, GL.FLOAT, Float32Array, 2],
			['rgba32f', GL.RGBA32F, GL.RGBA, GL.FLOAT, Float32Array, 4],
			['depth', GL.DEPTH_COMPONENT24, GL.DEPTH_COMPONENT, GL.UNSIGNED_INT, Uint32Array, 1],
		]) TextureFormats[name] = {
		internalFormat,
		glformat,
		type,
		CpuArray,
		chn
	};
}

function memoize(f) {
	const cache = {};
	const wrap = k => k in cache ? cache[k] : cache[k] = f(k);
	wrap.cache = cache;
	return wrap;
}

function updateObject(o, updates) {
	for (const s in updates) {
		o[s] = updates[s];
	}
	return o;
}

// Parse strings like 'min(s,d)', 'max(s,d)', 's*d', 's+d*(1-sa)',
// 's*d', 'd*(1-sa) + s*sa', s-d', 'd-s' and so on into
// gl.blendFunc/gl.blendEquation arguments.
function parseBlend(s0) {
	if (!s0) return;
	let s = s0.replace(/\s+/g, '');
	if (!s) return null;
	const GL = WebGL2RenderingContext;
	const func2gl = {
		'min': GL.MIN,
		'max': GL.MAX,
		'+': GL.FUNC_ADD,
		's-d': GL.FUNC_SUBTRACT,
		'd-s': GL.FUNC_REVERSE_SUBTRACT
	};
	const factor2gl = {
		'0': GL.ZERO,
		'1': GL.ONE,
		's': GL.SRC_COLOR,
		'(1-s)': GL.ONE_MINUS_SRC_COLOR,
		'd': GL.DST_COLOR,
		'(1-d)': GL.ONE_MINUS_DST_COLOR,
		'sa': GL.SRC_ALPHA,
		'(1-sa)': GL.ONE_MINUS_SRC_ALPHA,
		'da': GL.DST_ALPHA,
		'(1-da)': GL.ONE_MINUS_DST_ALPHA,
		'c': GL.CONSTANT_COLOR,
		'(1-c)': GL.ONE_MINUS_CONSTANT_COLOR,
		'ca': GL.CONSTANT_ALPHA,
		'(1-ca)': GL.ONE_MINUS_CONSTANT_ALPHA,
	};
	const res = {
		s: GL.ZERO,
		d: GL.ZERO,
		f: null
	};
	s = s.replace(/(s|d)(?:\*(\w+|\(1-\w+\)))?/g, (_, term, factor) => {
		factor = factor || '1';
		if (!(factor in factor2gl)) {
			throw `Unknown blend factor: "${factor}"`;
		}
		res[term] = factor2gl[factor];
		return term;
	});
	let m;
	if (m = s.match(/^(min|max)\((s,d|d,s)\)$/)) {
		res.f = func2gl[m[1]];
	} else if (s.match(/^(s|d|s\+d|d\+s)$/)) {
		res.f = func2gl['+'];
	} else if (s in func2gl) {
		res.f = func2gl[s];
	} else {
		throw `Unable to parse blend spec: "${s0}"`;
	}
	return res;
}
parseBlend = memoize(parseBlend);

function compileShader(gl, code, type, program) {
	code = '#version 300 es\n' + code;
	const shader = gl.createShader(type);
	gl.shaderSource(shader, code);
	gl.compileShader(shader);
	if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
		const withLines = code.split('\n').map(
			(s, i) => `${(i+1+'').padStart(4)}: ${s}`).join('\n')
		throw (withLines + '\n' + '--- GLSL COMPILE ERROR ---\n' + gl.getShaderInfoLog(shader));
	}
	gl.attachShader(program, shader);
	gl.deleteShader(shader);
}

function compileProgram(gl, vs, fs) {
	const program = gl.createProgram();
	compileShader(gl, vs, gl.VERTEX_SHADER, program);
	compileShader(gl, fs, gl.FRAGMENT_SHADER, program);
	gl.linkProgram(program);
	if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
		console.error("shader link error:" + gl.getProgramInfoLog(program));
	}
	gl.useProgram(program);
	program.setters = {};
	let unitCount = 0;
	const numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
	for (let i = 0; i < numUniforms; ++i) {
		const info = gl.getActiveUniform(program, i);
		const loc = gl.getUniformLocation(program, info.name);
		const name = info.name.match(/^\w+/)[0];
		if (info.type in UniformType2TexTarget) {
			const unit = unitCount++;
			const target = UniformType2TexTarget[info.type];
			gl.uniform1i(loc, unit);
			program.setters[name] = tex => {
				gl.activeTexture(gl.TEXTURE0 + unit);
				tex ? tex.bindSampler(unit) : gl.bindTexture(target, null);
			}
		} else {
			const fname = Type2Setter[info.type];
			const setter = fname.startsWith('uniformMatrix') ?
				v => gl[fname](loc, false, v) : v => gl[fname](loc, v);
			program.setters[name] = v => v != undefined ? setter(v) : null;
		}
	}
	gl.useProgram(null);
	console.log('created', program);
	return program;
}

const glsl_template = `
precision highp float;
precision highp int;
precision lowp sampler2DArray;
#ifdef VERT
    #define varying out
    #define VPos gl_Position
    layout(location = 0) in int VertexID;
    layout(location = 1) in int InstanceID;
    ivec2 VID;
    ivec3 ID;
#else
    #define varying in
    layout(location = 0) out vec4 FOut;
    layout(location = 1) out vec4 FOut1;
    layout(location = 2) out vec4 FOut2;
    layout(location = 3) out vec4 FOut3;
    layout(location = 4) out vec4 FOut4;
    layout(location = 5) out vec4 FOut5;
    layout(location = 6) out vec4 FOut6;
    layout(location = 7) out vec4 FOut7;
    ivec2 I;
#endif

uniform ivec3 Grid;
uniform ivec2 Mesh;
uniform ivec4 View;
#define ViewSize (View.zw)
uniform vec2 Aspect;
varying vec2 UV;
#define XY (2.0*UV-1.0)
// #define VertexID gl_VertexID
// #define InstanceID gl_InstanceID


 GLSL Utils 

const float PI  = radians(180.0);
const float TAU = radians(360.0);

// source: https://www.shadertoy.com/view/XlXcW4
// TODO more complete hash library
vec3 hash( ivec3 ix ) {
    uvec3 x = uvec3(ix);
    const uint k = 1103515245U;
    x = ((x>>8U)^x.yzx)*k;
    x = ((x>>8U)^x.yzx)*k;
    x = ((x>>8U)^x.yzx)*k;
    return vec3(x)*(1.0/float(0xffffffffU));
}

mat2 rot2(float a) {
  float s=sin(a), c=cos(a);
  return mat2(c, s, -s, c);
}

// https://suricrasia.online/demoscene/functions/
vec3 erot(vec3 p, vec3 ax, float ro) {
    return mix(dot(ax, p)*ax, p, cos(ro)) + cross(ax,p)*sin(ro);
}

vec3 uv2sphere(vec2 uv) {
  uv *= vec2(-TAU,PI);
  return vec3(vec2(cos(uv.x), sin(uv.x))*sin(uv.y), cos(uv.y));
}

vec3 torus(vec2 uv, float r1, float r2) {
    uv *= TAU;
    vec3 p = vec3(r1+cos(uv.x)*r2, 0, sin(uv.x)*r2);
    return vec3(p.xy * rot2(uv.y), p.z);
}

vec3 cubeVert(vec2 xy, int side) {
    float x=xy.x, y=xy.y;
    switch (side) {
        case 0: return vec3(x,y,1); case 1: return vec3(y,x,-1);
        case 2: return vec3(y,1,x); case 3: return vec3(x,-1,y);
        case 4: return vec3(1,x,y); case 5: return vec3(-1,y,x);
    };
    return vec3(0.0);
}

vec3 _surf_f(vec3 p, vec3 a, vec3 b, out vec3 normal) {
    normal = normalize(cross(a-p, b-p));
    return p;
}
#define SURF(f, uv, out_normal, eps) _surf_f(f(uv), f(uv+vec2(eps,0)), f(uv+vec2(0,eps)), out_normal)

vec4 _sample(sampler2D tex, vec2 uv) {return texture(tex, uv);}
vec4 _sample(sampler2D tex, ivec2 xy) {return texelFetch(tex, xy, 0);}
vec4 _sample(sampler2DArray tex, vec2 uv, int layer) {return texture(tex, vec3(uv, layer));}
vec4 _sample(sampler2DArray tex, ivec2 xy, int layer) {return texelFetch(tex, ivec3(xy, layer), 0);}

#ifdef FRAG
    float isoline(float v) {
        float distToInt = abs(v-round(v));
        return smoothstep(max(fwidth(v), 0.0001), 0.0, distToInt);
    }
    float wireframe() {
        vec2 m = UV*vec2(Mesh);
        float d1 = isoline(m.x-m.y), d2 = isoline(m.x+m.y);
        float d = mix(d1, d2, float(int(m.y)%2));
        return isoline(m.x)+isoline(m.y)+d;
    }
#endif
`;

function guessUniforms(params) {
	const uni = [];
	const len2type = {
		1: 'float',
		2: 'vec2',
		3: 'vec3',
		4: 'vec4',
		9: 'mat3',
		16: 'mat4'
	};
	for (const name in params) {
		const v = params[name];
		let s = null;
		if (v instanceof TextureSampler) {
			const [type, D] = v.layern ? ['sampler2DArray', '3'] : ['sampler2D', '2'];
			const lookupMacro = v.layern ?
				`#define ${name}(p,l) (_sample(${name}, (p), (l)))` :
				`#define ${name}(p) (_sample(${name}, (p)))`;
			s = `uniform ${type} ${name};
            ${lookupMacro}
            ivec${D} ${name}_size() {return textureSize(${name}, 0);}
            vec${D}  ${name}_step() {return 1.0/vec${D}(${name}_size());}`;
		} else if (typeof v === 'number') {
			s = `uniform float ${name};`
		} else if (typeof v === 'boolean') {
			s = `uniform bool ${name};`
		} else if (v.length in len2type) {
			s = `uniform ${len2type[v.length]} ${name};`
		}
		if (s) uni.push(s);
	}
	return uni.join('\n') + '\n';
}

const stripComments = code => code.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, '');

// TODO better parser (use '\b')
function definedUniforms(code) {
	code = stripComments(code);
	const lines = Array.from(code.matchAll(/uniform\s+\w+\s+([^;]+)\s*;/g));
	return new Set(lines.map(m => m[1].split(/[^\w]+/)).flat());
}

function expandCode(code, mainFunc, outVar) {
	const stripped = stripComments(code).trim();
	if (stripped != '' && stripped.indexOf(';') == -1) {
		code = `${outVar} = vec4(${stripped});`
	}
	if (!stripped.match(new RegExp(`\\b${mainFunc}\s*\\(`))) {
		code = `void ${mainFunc}() {
          ${code};
        }`
	}
	return code;
}
const expandVP = memoize(code => expandCode(code, 'vertex', 'VPos'));
const expandFP = memoize(code => expandCode(code, 'fragment', 'FOut'));

function extractVaryings(VP) {
	return Array.from(stripComments(VP).matchAll(/\bvarying\s+[^;]+;/g))
		.map(m => m[0]).map(s => {
			while (s != (s = s.replace(/\([^()]*\)/g, ''))); // remove nested ()
			return s.replace(/=[^,;]*/g, '') // remove assigned values 
		}).join('\n');
}

function stripVaryings(VP) {
	return VP.replace(/\bvarying\s+\w+/g, '');
}

function linkShader(gl, uniforms, Inc, VP, FP) {
	Inc = Inc.join('\n');
	const defined = definedUniforms([glsl_template, Inc, VP, FP].join('\n'));
	const undefined = Object.entries(uniforms)
		.filter(kv => kv[0].match(/^\w+$/))
		.filter(kv => !(defined.has(kv[0])));
	const guessed = guessUniforms(Object.fromEntries(undefined));
	const varyings = extractVaryings(VP);
	VP = expandVP(stripVaryings(VP));
	const prefix = `${glsl_template}\n${guessed}\n${varyings}\n${Inc}\n`;
	return compileProgram(gl, `
    #define VERT
    ${prefix}\n${VP}
    void main() {
      int rowVertN = Mesh.x*2+3;
      int rowI = VertexID/rowVertN;
      int rowVertI = min(VertexID%rowVertN, rowVertN-2);
      int odd = rowI%2;
      if (odd==0) rowVertI = rowVertN-rowVertI-2;
      VID = ivec2(rowVertI>>1, rowI + (rowVertI+odd+1)%2);
      int ii = InstanceID;
      ID.x = ii % Grid.x; ii/=Grid.x;
      ID.y = ii % Grid.y; ii/=Grid.y;
      ID.z = ii;
      UV = vec2(VID) / vec2(Mesh);
      VPos = vec4(XY,0,1);
      vertex();
      VPos.xy *= Aspect;
    }`, `
    #define FRAG
    ${prefix}\n${expandFP(FP)}
    void main() {
      I = ivec2(gl_FragCoord.xy);
      fragment();
    }`);
}

class TextureSampler {
	fork(updates) {
		const {
			gl,
			handle,
			gltarget,
			layern,
			filter,
			wrap
		} = {
			...this,
			...updates
		};
		return updateObject(new TextureSampler(), {
			gl,
			handle,
			gltarget,
			layern,
			filter,
			wrap
		});
	}
	get linear() {
		return this.fork({
			filter: 'linear'
		})
	}
	get nearest() {
		return this.fork({
			filter: 'nearest'
		})
	}
	get miplinear() {
		return this.fork({
			filter: 'miplinear'
		})
	}
	get edge() {
		return this.fork({
			wrap: 'edge'
		})
	}
	get repeat() {
		return this.fork({
			wrap: 'repeat'
		})
	}
	get mirror() {
		return this.fork({
			wrap: 'mirror'
		})
	}

	get _sampler() {
		const {
			gl,
			filter,
			wrap
		} = this;
		if (!gl._samplers) {
			gl._samplers = {};
		}
		const id = `${filter}:${wrap}`;
		if (!(id in gl._samplers)) {
			const glfilter = {
				'nearest': gl.NEAREST,
				'linear': gl.LINEAR,
				'miplinear': gl.LINEAR_MIPMAP_LINEAR
			} [filter];
			const glwrap = {
				'repeat': gl.REPEAT,
				'edge': gl.CLAMP_TO_EDGE,
				'mirror': gl.MIRRORED_REPEAT
			} [wrap];
			const sampler = gl.createSampler();
			const setf = (k, v) => gl.samplerParameteri(sampler, gl['TEXTURE_' + k], v);
			setf('MIN_FILTER', glfilter);
			setf('MAG_FILTER', filter == 'miplinear' ? gl.LINEAR : glfilter);
			setf('WRAP_S', glwrap);
			setf('WRAP_T', glwrap);
			gl._samplers[id] = sampler;
		}
		return gl._samplers[id];
	}
	bindSampler(unit) {
		// assume unit is already active
		const {
			gl,
			gltarget,
			handle
		} = this;
		gl.bindTexture(gltarget, handle);
		if (this.filter == 'miplinear' && !handle.hasMipmap) {
			gl.generateMipmap(gltarget)
			handle.hasMipmap = true;
		}
		gl.bindSampler(unit, this._sampler);
	}
}

class TextureTarget extends TextureSampler {
	constructor(gl, params) {
		super();
		let {
			size,
			tag,
			format = 'rgba8',
			filter = 'nearest',
			wrap = 'repeat',
			layern = null,
			data = null,
			depth = null
		} = params;
		if (!depth && format.includes('+')) {
			const [mainFormat, depthFormat] = format.split('+');
			format = mainFormat;
			depth = new TextureTarget(gl, {
				...params,
				tag: tag + '_depth',
				format: depthFormat,
				layern: null,
				depth: null
			});
		}
		this.handle = gl.createTexture(),
			this.filter = format == 'depth' ? 'nearest' : filter;
		this.gltarget = layern ? gl.TEXTURE_2D_ARRAY : gl.TEXTURE_2D;
		this.formatInfo = TextureFormats[format];
		updateObject(this, {
			gl,
			_tag: tag,
			format,
			layern,
			wrap,
			depth
		});
		this.update(size, data);
	}
	update(size, data) {
		const {
			gl,
			handle,
			gltarget,
			layern
		} = this;
		const {
			internalFormat,
			glformat,
			type
		} = this.formatInfo;
		const [w, h] = size;
		gl.bindTexture(gltarget, handle);
		if (!layern) {
			gl.texImage2D(gltarget, 0 /*mip level*/ ,
				internalFormat, w, h, 0 /*border*/ ,
				glformat, type, data /*data*/ );
		} else {
			gl.texImage3D(gltarget, 0 /*mip level*/ ,
				internalFormat, w, h, layern, 0 /*border*/ ,
				glformat, type, data /*data*/ );
		}
		gl.bindTexture(gltarget, null);
		this.size = size;
		if (this.depth) {
			this.depth.update(size, data);
		}
	}
	attach(gl) {
		if (!this.layern) {
			const attachment = this.format == 'depth' ? gl.DEPTH_ATTACHMENT : gl.COLOR_ATTACHMENT0;
			gl.framebufferTexture2D(
				gl.FRAMEBUFFER, attachment, gl.TEXTURE_2D, this.handle, 0 /*level*/ );
		} else {
			const drawBuffers = [];
			for (let i = 0; i < this.layern; ++i) {
				const attachment = gl.COLOR_ATTACHMENT0 + i;
				drawBuffers.push(attachment);
				gl.framebufferTextureLayer(
					gl.FRAMEBUFFER, attachment, this.handle, 0 /*level*/ , i);
			}
			gl.drawBuffers(drawBuffers);
		}
	}
	bindTarget(gl, readonly = false) {
		if (this.fbo) {
			gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo);
		} else {
			this.fbo = gl.createFramebuffer();
			gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo);
			this.attach(gl)
			if (this.depth) this.depth.attach(gl);
		}
		if (!readonly) {
			this.handle.hasMipmap = false;
		}
		return this.size;
	}
	_getBox(box) {
		box = (box && box.length) ? box : [0, 0, ...this.size];
		const [x, y, w, h] = box, n = w * h * this.formatInfo.chn;
		return {
			box,
			n
		}
	}
	_getCPUBuf(n) {
		if (!this.cpu || this.cpu.length < n) {
			this.cpu = new this.formatInfo.CpuArray(n);
		}
		return this.cpu.length == n ? this.cpu : this.cpu.subarray(0, n);
	}
	_readPixels(box, targetBuf) {
		const {
			glformat,
			type
		} = this.formatInfo;
		this.bindTarget(this.gl, /*readonly*/ true);
		this.gl.readPixels(...box, glformat, type, targetBuf);
	}
	readSync(...optBox) {
		const {
			box,
			n
		} = this._getBox(optBox);
		const buf = this._getCPUBuf(n);
		this._readPixels(box, buf);
		return buf
	}
	_bindAsyncBuffer(n) {
		const {
			gl
		} = this;
		const {
			CpuArray
		} = this.formatInfo;
		if (!this.async) {
			this.async = {
				all: new Set(),
				queue: []
			};
		}
		if (this.async.queue.length == 0) {
			const gpuBuf = gl.createBuffer();
			this.async.queue.push(gpuBuf);
			this.async.all.add(gpuBuf);
		}
		const gpuBuf = this.async.queue.shift();
		if (this.async.queue.length > 6) {
			this._deleteAsyncBuf(this.async.queue.pop());
		}
		gl.bindBuffer(gl.PIXEL_PACK_BUFFER, gpuBuf);
		if (!gpuBuf.length || gpuBuf.length < n) {
			const byteN = n * this.formatInfo.CpuArray.BYTES_PER_ELEMENT
			gl.bufferData(gl.PIXEL_PACK_BUFFER, byteN, gl.STREAM_READ);
			gpuBuf.length = n;
			console.log(`created/resized async gpu buffer "${this._tag}":`, gpuBuf);
		}
		return gpuBuf;
	}
	_deleteAsyncBuf(gpuBuf) {
		delete gpuBuf.length;
		this.gl.deleteBuffer(gpuBuf);
		this.async.all.delete(gpuBuf);
	}
	// https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#use_non-blocking_async_data_readback
	read(callback, optBox, optTarget) {
		const {
			gl
		} = this;
		const {
			box,
			n
		} = this._getBox(optBox);
		const gpuBuf = this._bindAsyncBuffer(n);
		this._readPixels(box, 0);
		gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
		const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
		gl.flush();
		this._asyncFetch(gpuBuf, sync, callback, optTarget);
	}
	_asyncFetch(gpuBuf, sync, callback, optTarget) {
		const {
			gl
		} = this;
		if (!gpuBuf.length) { // check that gpu buffer is not deleted
			gl.deleteSync(sync);
			return;
		}
		const res = gl.clientWaitSync(sync, 0, 0);
		if (res === gl.TIMEOUT_EXPIRED) {
			setTimeout(() => this._asyncFetch(gpuBuf, sync, callback, optTarget), 1 /*ms*/ );
			return;
		}
		if (res === gl.WAIT_FAILED) {
			console.log(`async read of ${this._tag} failed`);
		} else {
			gl.bindBuffer(gl.PIXEL_PACK_BUFFER, gpuBuf);
			const target = optTarget || this._getCPUBuf(gpuBuf.length);
			gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0 /*srcOffset*/ ,
				target, 0 /*dstOffset*/ , gpuBuf.length /*length*/ );
			gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
			callback(target);
		}
		gl.deleteSync(sync);
		this.async.queue.push(gpuBuf);
	}
	free() {
		const gl = this.gl;
		if (this.depth) this.depth.free();
		if (this.fbo) gl.deleteFramebuffer(this.fbo);
		if (this.async) this.async.all.forEach(buf => this._deleteAsyncBuf(buf));
		gl.deleteTexture(this.handle);
	}
}

function calcAspect(aspect, w, h) {
	if (!aspect) return [1, 1];
	let c;
	switch (aspect) {
		case 'fit':
			c = Math.min(w, h);
			break;
		case 'cover':
			c = Math.max(w, h);
			break;
		case 'x':
			c = w;
			break;
		case 'y':
			c = h;
			break;
		case 'mean':
			c = (w + h) / 2;
			break;
		default:
			throw `Unknown aspect mode "${aspect}"`;
	}
	return [c / w, c / h];
}

function ensureVertexArray(gl, neededSize) {
	// gl_VertexID / gl_InstanceID seem to be broken in some configurations
	// (e.g. https://crbug.com/1315104), so I had to fallback to using arrays
	if (gl._indexVA && neededSize <= gl._indexVA.size)
		return;
	const size = neededSize * 2;

	const va = gl._indexVA || gl.createVertexArray();
	va.size = size;
	gl._indexVA = va;
	gl.bindVertexArray(va);

	const arr = new Int32Array(size);
	arr.forEach((v, i) => {
		arr[i] = i
	});

	const buf = va.buf || gl.createBuffer();
	va.buf = buf;
	gl.bindBuffer(gl.ARRAY_BUFFER, buf);
	gl.bufferData(gl.ARRAY_BUFFER, arr, gl.STATIC_DRAW);

	for (let loc = 0; loc < 2; ++loc) {
		gl.enableVertexAttribArray(loc);
		gl.vertexAttribIPointer(loc, 1 /*size*/ , gl.INT,
			false /*normalize*/ , 0 /*stride*/ , 0 /*offset*/ );
	}
	gl.vertexAttribDivisor(1, 1);

	gl.bindBuffer(gl.ARRAY_BUFFER, null);
	gl.bindVertexArray(null);

	console.log('created:', va);
}

function getTargetSize(gl, {
	size,
	scale = 1,
	data
}) {
	if (!size && (data && data.videoWidth && data.videoHeight)) {
		size = [data.videoWidth, data.videoHeight];
	}
	size = size || [gl.canvas.width, gl.canvas.height];
	return [Math.ceil(size[0] * scale), Math.ceil(size[1] * scale)];
}

function createTarget(gl, params) {
	if (!params.story) return new TextureTarget(gl, params);
	return Array(params.story).fill(0).map(_ => new TextureTarget(gl, params));
}

function prepareOwnTarget(self, spec) {
	const buffers = self.buffers;
	spec.size = getTargetSize(self.gl, spec);
	if (!buffers[spec.tag]) {
		const target = buffers[spec.tag] = createTarget(self.gl, spec);
		console.log('created', target);
	}
	const target = buffers[spec.tag];
	const tex = Array.isArray(target) ? target[target.length - 1] : target;
	const needResize = tex.size[0] != spec.size[0] || tex.size[1] != spec.size[1];
	if (needResize || spec.data) {
		if (needResize) {
			console.log(`resizing "${spec.tag}" (${tex.size})->(${spec.size})`);
		}
		tex.update(spec.size, spec.data);
	}
	if (Array.isArray(target)) {
		target.size = spec.size;
	}
	return buffers[spec.tag];
}

function bindTarget(gl, target) {
	if (!target) {
		gl.bindFramebuffer(gl.FRAMEBUFFER, null);
		return [gl.canvas.width, gl.canvas.height];
	}
	if (Array.isArray(target)) {
		const next = target.pop();
		if (target.size[0] != next.size[0] || target.size[1] != next.size[1]) {
			next.update(target.size, null);
		}
		target.unshift(next);
		target = next;
	}
	return target.bindTarget(gl)
}

const OptNames = new Set([
	'Inc', 'VP', 'FP',
	'Clear', 'Blend', 'View', 'Grid', 'Mesh', 'Aspect', 'DepthTest', 'AlphaCoverage', 'Face'
]);

function drawQuads(self, params, target) {
	const options = {},
		uniforms = {}
	for (const p in params) {
		(OptNames.has(p) ? options : uniforms)[p] = params[p];
	}
	let Inc = options.Inc || [];
	if (!Array.isArray(Inc)) {
		Inc = [Inc];
	}
	const [VP, FP] = [options.VP || '', options.FP || ''];
	const haveShader = VP || FP;
	const haveClear = options.Clear || options.Clear == 0;

	// setup target
	if (target && target.tag) {
		target = prepareOwnTarget(self, target);
		if (!haveShader && !haveClear) return target;
	}
	if (Array.isArray(target)) {
		uniforms.Src = uniforms.Src || target[0];
	}

	// bind (and clear) target
	const gl = self.gl;
	const targetSize = bindTarget(gl, target);
	let view = options.View || [0, 0, targetSize[0], targetSize[1]];
	if (view.length == 2) {
		view = [0, 0, view[0], view[1]]
	}
	gl.depthMask(!(options.DepthTest == 'keep'));
	if (haveClear) {
		let clear = options.Clear;
		if (typeof clear === 'number') {
			clear = [clear, clear, clear, clear];
		}
		gl.clearColor(...clear);
		gl.enable(gl.SCISSOR_TEST);
		gl.scissor(...view);
		gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
		gl.disable(gl.SCISSOR_TEST);
	}

	// setup program
	if (!haveShader) {
		return target;
	}
	let prog = self.shaders;
	for (const chunk of Inc) {
		prog = prog[chunk] || (prog[chunk] = {});
	}
	prog = prog[VP] || (prog[VP] = {});
	prog = prog[FP] || (prog[FP] = linkShader(gl, uniforms, Inc, VP, FP));
	gl.useProgram(prog);

	// process options
	if (options.Blend) {
		const blend = parseBlend(options.Blend);
		const {
			s,
			d,
			f
		} = blend;
		gl.enable(gl.BLEND);
		gl.blendFunc(s, d);
		gl.blendEquation(f);
	}
	if (options.DepthTest) {
		gl.enable(gl.DEPTH_TEST);
	}
	if (options.Face) {
		gl.enable(gl.CULL_FACE);
		const mode = {
			'front': gl.BACK,
			'back': gl.FRONT
		} [options.Face];
		gl.cullFace(mode);
	}
	if (options.AlphaCoverage) {
		gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE);
	}

	// View, Aspect
	gl.viewport(...view)
	const width = view[2],
		height = view[3];
	uniforms.View = view;
	uniforms.Aspect = calcAspect(options.Aspect, width, height);

	// Grid, Mesh
	const [gx = 1, gy = 1, gz = 1] = options.Grid || [];
	uniforms.Grid = [gx, gy, gz];
	uniforms.Mesh = options.Mesh || [1, 1]; // 3d for cube?
	const vertN = (uniforms.Mesh[0] * 2 + 3) * uniforms.Mesh[1] - 1;
	const instN = gx * gy * gz;
	ensureVertexArray(gl, Math.max(vertN, instN));
	gl.bindVertexArray(gl._indexVA);

	// setup uniforms and textures
	Object.entries(prog.setters).forEach(([name, f]) => f(uniforms[name]));
	// draw
	gl.drawArraysInstanced(gl.TRIANGLE_STRIP, 0, vertN, instN);

	// revert gl state
	if (options.Blend) gl.disable(gl.BLEND);
	if (options.DepthTest) gl.disable(gl.DEPTH_TEST);
	if (options.Face) gl.disable(gl.CULL_FACE);
	if (options.AlphaCoverage) gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE);
	gl.bindVertexArray(null);
	return target;
}

function SwissGL(canvas_gl) {
	const gl = canvas_gl.getContext ?
		canvas_gl.getContext('webgl2', {
			alpha: false,
			antialias: true
		}) : canvas_gl;
	gl.getExtension("EXT_color_buffer_float");
	gl.getExtension("OES_texture_float_linear");
	gl.pixelStorei(gl.PACK_ALIGNMENT, 1);
	gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
	ensureVertexArray(gl, 1024);
	const glsl = (params, target) => drawQuads(glsl, params, target);

	glsl.gl = gl;
	glsl.shaders = {};
	glsl.buffers = {};
	glsl.reset = () => {
		const freeProg = o => (o instanceof WebGLProgram) ? gl.deleteProgram(o) : Object.values(o).forEach(
			freeProg);
		freeProg(glsl.shaders);
		Object.values(glsl.buffers).flat().forEach(target => target.free());
		glsl.shaders = {};
		glsl.buffers = {};
	};
	glsl.adjustCanvas = dpr => {
		dpr = dpr || self.devicePixelRatio;
		const canvas = gl.canvas;
		const w = canvas.clientWidth * dpr,
			h = canvas.clientHeight * dpr;
		if (canvas.width != w || canvas.height != h) {
			canvas.width = w;
			canvas.height = h;
		}
	}
	glsl.loop = callback => {
		const frameFunc = time => {
			const res = callback({
				glsl,
				time: time / 1000.0
			});
			if (res != 'stop') requestAnimationFrame(frameFunc);
		};
		requestAnimationFrame(frameFunc);
	};
	return glsl;
}

self._SwissGL = SwissGL;

七、dat.gui.min.js

/**
 * dat-gui JavaScript Controller Library
 * https://github.com/dataarts/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
 */
! function(e, t) {
	"object" == typeof exports && "undefined" != typeof module ? t(exports) : "function" == typeof define && define
		.amd ? define(["exports"], t) : t(e.dat = {})
}(this, function(e) {
	"use strict";

	function t(e, t) {
		var n = e.__state.conversionName.toString(),
			o = Math.round(e.r),
			i = Math.round(e.g),
			r = Math.round(e.b),
			s = e.a,
			a = Math.round(e.h),
			l = e.s.toFixed(1),
			d = e.v.toFixed(1);
		if (t || "THREE_CHAR_HEX" === n || "SIX_CHAR_HEX" === n) {
			for (var c = e.hex.toString(16); c.length < 6;) c = "0" + c;
			return "#" + c
		}
		return "CSS_RGB" === n ? "rgb(" + o + "," + i + "," + r + ")" : "CSS_RGBA" === n ? "rgba(" + o + "," + i +
			"," + r + "," + s + ")" : "HEX" === n ? "0x" + e.hex.toString(16) : "RGB_ARRAY" === n ? "[" + o + "," +
			i + "," + r + "]" : "RGBA_ARRAY" === n ? "[" + o + "," + i + "," + r + "," + s + "]" : "RGB_OBJ" === n ?
			"{r:" + o + ",g:" + i + ",b:" + r + "}" : "RGBA_OBJ" === n ? "{r:" + o + ",g:" + i + ",b:" + r + ",a:" +
			s + "}" : "HSV_OBJ" === n ? "{h:" + a + ",s:" + l + ",v:" + d + "}" : "HSVA_OBJ" === n ? "{h:" + a +
			",s:" + l + ",v:" + d + ",a:" + s + "}" : "unknown format"
	}

	function n(e, t, n) {
		Object.defineProperty(e, t, {
			get: function() {
				return "RGB" === this.__state.space ? this.__state[t] : (I.recalculateRGB(this, t, n),
					this.__state[t])
			},
			set: function(e) {
				"RGB" !== this.__state.space && (I.recalculateRGB(this, t, n), this.__state.space =
					"RGB"), this.__state[t] = e
			}
		})
	}

	function o(e, t) {
		Object.defineProperty(e, t, {
			get: function() {
				return "HSV" === this.__state.space ? this.__state[t] : (I.recalculateHSV(this), this
					.__state[t])
			},
			set: function(e) {
				"HSV" !== this.__state.space && (I.recalculateHSV(this), this.__state.space = "HSV"),
					this.__state[t] = e
			}
		})
	}

	function i(e) {
		if ("0" === e || S.isUndefined(e)) return 0;
		var t = e.match(U);
		return S.isNull(t) ? 0 : parseFloat(t[1])
	}

	function r(e) {
		var t = e.toString();
		return t.indexOf(".") > -1 ? t.length - t.indexOf(".") - 1 : 0
	}

	function s(e, t) {
		var n = Math.pow(10, t);
		return Math.round(e * n) / n
	}

	function a(e, t, n, o, i) {
		return o + (e - t) / (n - t) * (i - o)
	}

	function l(e, t, n, o) {
		e.style.background = "", S.each(ee, function(i) {
			e.style.cssText += "background: " + i + "linear-gradient(" + t + ", " + n + " 0%, " + o +
				" 100%); "
		})
	}

	function d(e) {
		e.style.background = "", e.style.cssText +=
			"background: -moz-linear-gradient(top,  #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);",
			e.style.cssText +=
			"background: -webkit-linear-gradient(top,  #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);",
			e.style.cssText +=
			"background: -o-linear-gradient(top,  #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);",
			e.style.cssText +=
			"background: -ms-linear-gradient(top,  #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);",
			e.style.cssText +=
			"background: linear-gradient(top,  #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"
	}

	function c(e, t, n) {
		var o = document.createElement("li");
		return t && o.appendChild(t), n ? e.__ul.insertBefore(o, n) : e.__ul.appendChild(o), e.onResize(), o
	}

	function u(e) {
		X.unbind(window, "resize", e.__resizeHandler), e.saveToLocalStorageIfPossible && X.unbind(window, "unload",
			e.saveToLocalStorageIfPossible)
	}

	function _(e, t) {
		var n = e.__preset_select[e.__preset_select.selectedIndex];
		n.innerHTML = t ? n.value + "*" : n.value
	}

	function h(e, t, n) {
		if (n.__li = t, n.__gui = e, S.extend(n, {
				options: function(t) {
					if (arguments.length > 1) {
						var o = n.__li.nextElementSibling;
						return n.remove(), f(e, n.object, n.property, {
							before: o,
							factoryArgs: [S.toArray(arguments)]
						})
					}
					if (S.isArray(t) || S.isObject(t)) {
						var i = n.__li.nextElementSibling;
						return n.remove(), f(e, n.object, n.property, {
							before: i,
							factoryArgs: [t]
						})
					}
				},
				name: function(e) {
					return n.__li.firstElementChild.firstElementChild.innerHTML = e, n
				},
				listen: function() {
					return n.__gui.listen(n), n
				},
				remove: function() {
					return n.__gui.remove(n), n
				}
			}), n instanceof q) {
			var o = new Q(n.object, n.property, {
				min: n.__min,
				max: n.__max,
				step: n.__step
			});
			S.each(["updateDisplay", "onChange", "onFinishChange", "step", "min", "max"], function(e) {
				var t = n[e],
					i = o[e];
				n[e] = o[e] = function() {
					var e = Array.prototype.slice.call(arguments);
					return i.apply(o, e), t.apply(n, e)
				}
			}), X.addClass(t, "has-slider"), n.domElement.insertBefore(o.domElement, n.domElement
				.firstElementChild)
		} else if (n instanceof Q) {
			var i = function(t) {
				if (S.isNumber(n.__min) && S.isNumber(n.__max)) {
					var o = n.__li.firstElementChild.firstElementChild.innerHTML,
						i = n.__gui.__listening.indexOf(n) > -1;
					n.remove();
					var r = f(e, n.object, n.property, {
						before: n.__li.nextElementSibling,
						factoryArgs: [n.__min, n.__max, n.__step]
					});
					return r.name(o), i && r.listen(), r
				}
				return t
			};
			n.min = S.compose(i, n.min), n.max = S.compose(i, n.max)
		} else n instanceof K ? (X.bind(t, "click", function() {
			X.fakeEvent(n.__checkbox, "click")
		}), X.bind(n.__checkbox, "click", function(e) {
			e.stopPropagation()
		})) : n instanceof Z ? (X.bind(t, "click", function() {
			X.fakeEvent(n.__button, "click")
		}), X.bind(t, "mouseover", function() {
			X.addClass(n.__button, "hover")
		}), X.bind(t, "mouseout", function() {
			X.removeClass(n.__button, "hover")
		})) : n instanceof $ && (X.addClass(t, "color"), n.updateDisplay = S.compose(function(e) {
			return t.style.borderLeftColor = n.__color.toString(), e
		}, n.updateDisplay), n.updateDisplay());
		n.setValue = S.compose(function(t) {
			return e.getRoot().__preset_select && n.isModified() && _(e.getRoot(), !0), t
		}, n.setValue)
	}

	function p(e, t) {
		var n = e.getRoot(),
			o = n.__rememberedObjects.indexOf(t.object);
		if (-1 !== o) {
			var i = n.__rememberedObjectIndecesToControllers[o];
			if (void 0 === i && (i = {}, n.__rememberedObjectIndecesToControllers[o] = i), i[t.property] = t, n
				.load && n.load.remembered) {
				var r = n.load.remembered,
					s = void 0;
				if (r[e.preset]) s = r[e.preset];
				else {
					if (!r[se]) return;
					s = r[se]
				}
				if (s[o] && void 0 !== s[o][t.property]) {
					var a = s[o][t.property];
					t.initialValue = a, t.setValue(a)
				}
			}
		}
	}

	function f(e, t, n, o) {
		if (void 0 === t[n]) throw new Error('Object "' + t + '" has no property "' + n + '"');
		var i = void 0;
		if (o.color) i = new $(t, n);
		else {
			var r = [t, n].concat(o.factoryArgs);
			i = ne.apply(e, r)
		}
		o.before instanceof z && (o.before = o.before.__li), p(e, i), X.addClass(i.domElement, "c");
		var s = document.createElement("span");
		X.addClass(s, "property-name"), s.innerHTML = i.property;
		var a = document.createElement("div");
		a.appendChild(s), a.appendChild(i.domElement);
		var l = c(e, a, o.before);
		return X.addClass(l, he.CLASS_CONTROLLER_ROW), i instanceof $ ? X.addClass(l, "color") : X.addClass(l, H(i
			.getValue())), h(e, l, i), e.__controllers.push(i), i
	}

	function m(e, t) {
		return document.location.href + "." + t
	}

	function g(e, t, n) {
		var o = document.createElement("option");
		o.innerHTML = t, o.value = t, e.__preset_select.appendChild(o), n && (e.__preset_select.selectedIndex = e
			.__preset_select.length - 1)
	}

	function b(e, t) {
		t.style.display = e.useLocalStorage ? "block" : "none"
	}

	function v(e) {
		var t = e.__save_row = document.createElement("li");
		X.addClass(e.domElement, "has-save"), e.__ul.insertBefore(t, e.__ul.firstChild), X.addClass(t, "save-row");
		var n = document.createElement("span");
		n.innerHTML = "&nbsp;", X.addClass(n, "button gears");
		var o = document.createElement("span");
		o.innerHTML = "Save", X.addClass(o, "button"), X.addClass(o, "save");
		var i = document.createElement("span");
		i.innerHTML = "New", X.addClass(i, "button"), X.addClass(i, "save-as");
		var r = document.createElement("span");
		r.innerHTML = "Revert", X.addClass(r, "button"), X.addClass(r, "revert");
		var s = e.__preset_select = document.createElement("select");
		if (e.load && e.load.remembered ? S.each(e.load.remembered, function(t, n) {
				g(e, n, n === e.preset)
			}) : g(e, se, !1), X.bind(s, "change", function() {
				for (var t = 0; t < e.__preset_select.length; t++) e.__preset_select[t].innerHTML = e
					.__preset_select[t].value;
				e.preset = this.value
			}), t.appendChild(s), t.appendChild(n), t.appendChild(o), t.appendChild(i), t.appendChild(r), ae) {
			var a = document.getElementById("dg-local-explain"),
				l = document.getElementById("dg-local-storage");
			document.getElementById("dg-save-locally").style.display = "block", "true" === localStorage.getItem(m(e,
				"isLocal")) && l.setAttribute("checked", "checked"), b(e, a), X.bind(l, "change", function() {
				e.useLocalStorage = !e.useLocalStorage, b(e, a)
			})
		}
		var d = document.getElementById("dg-new-constructor");
		X.bind(d, "keydown", function(e) {
			!e.metaKey || 67 !== e.which && 67 !== e.keyCode || le.hide()
		}), X.bind(n, "click", function() {
			d.innerHTML = JSON.stringify(e.getSaveObject(), void 0, 2), le.show(), d.focus(), d.select()
		}), X.bind(o, "click", function() {
			e.save()
		}), X.bind(i, "click", function() {
			var t = prompt("Enter a new preset name.");
			t && e.saveAs(t)
		}), X.bind(r, "click", function() {
			e.revert()
		})
	}

	function y(e) {
		function t(t) {
			return t.preventDefault(), e.width += i - t.clientX, e.onResize(), i = t.clientX, !1
		}

		function n() {
			X.removeClass(e.__closeButton, he.CLASS_DRAG), X.unbind(window, "mousemove", t), X.unbind(window,
				"mouseup", n)
		}

		function o(o) {
			return o.preventDefault(), i = o.clientX, X.addClass(e.__closeButton, he.CLASS_DRAG), X.bind(window,
				"mousemove", t), X.bind(window, "mouseup", n), !1
		}
		var i = void 0;
		e.__resize_handle = document.createElement("div"), S.extend(e.__resize_handle.style, {
				width: "6px",
				marginLeft: "-3px",
				height: "200px",
				cursor: "ew-resize",
				position: "absolute"
			}), X.bind(e.__resize_handle, "mousedown", o), X.bind(e.__closeButton, "mousedown", o), e.domElement
			.insertBefore(e.__resize_handle, e.domElement.firstElementChild)
	}

	function w(e, t) {
		e.domElement.style.width = t + "px", e.__save_row && e.autoPlace && (e.__save_row.style.width = t + "px"), e
			.__closeButton && (e.__closeButton.style.width = t + "px")
	}

	function x(e, t) {
		var n = {};
		return S.each(e.__rememberedObjects, function(o, i) {
			var r = {},
				s = e.__rememberedObjectIndecesToControllers[i];
			S.each(s, function(e, n) {
				r[n] = t ? e.initialValue : e.getValue()
			}), n[i] = r
		}), n
	}

	function E(e) {
		for (var t = 0; t < e.__preset_select.length; t++) e.__preset_select[t].value === e.preset && (e
			.__preset_select.selectedIndex = t)
	}

	function C(e) {
		0 !== e.length && oe.call(window, function() {
			C(e)
		}), S.each(e, function(e) {
			e.updateDisplay()
		})
	}
	var A = Array.prototype.forEach,
		k = Array.prototype.slice,
		S = {
			BREAK: {},
			extend: function(e) {
				return this.each(k.call(arguments, 1), function(t) {
					(this.isObject(t) ? Object.keys(t) : []).forEach(function(n) {
						this.isUndefined(t[n]) || (e[n] = t[n])
					}.bind(this))
				}, this), e
			},
			defaults: function(e) {
				return this.each(k.call(arguments, 1), function(t) {
					(this.isObject(t) ? Object.keys(t) : []).forEach(function(n) {
						this.isUndefined(e[n]) && (e[n] = t[n])
					}.bind(this))
				}, this), e
			},
			compose: function() {
				var e = k.call(arguments);
				return function() {
					for (var t = k.call(arguments), n = e.length - 1; n >= 0; n--) t = [e[n].apply(this,
						t)];
					return t[0]
				}
			},
			each: function(e, t, n) {
				if (e)
					if (A && e.forEach && e.forEach === A) e.forEach(t, n);
					else if (e.length === e.length + 0) {
					var o = void 0,
						i = void 0;
					for (o = 0, i = e.length; o < i; o++)
						if (o in e && t.call(n, e[o], o) === this.BREAK) return
				} else
					for (var r in e)
						if (t.call(n, e[r], r) === this.BREAK) return
			},
			defer: function(e) {
				setTimeout(e, 0)
			},
			debounce: function(e, t, n) {
				var o = void 0;
				return function() {
					var i = this,
						r = arguments,
						s = n || !o;
					clearTimeout(o), o = setTimeout(function() {
						o = null, n || e.apply(i, r)
					}, t), s && e.apply(i, r)
				}
			},
			toArray: function(e) {
				return e.toArray ? e.toArray() : k.call(e)
			},
			isUndefined: function(e) {
				return void 0 === e
			},
			isNull: function(e) {
				return null === e
			},
			isNaN: function(e) {
				function t(t) {
					return e.apply(this, arguments)
				}
				return t.toString = function() {
					return e.toString()
				}, t
			}(function(e) {
				return isNaN(e)
			}),
			isArray: Array.isArray || function(e) {
				return e.constructor === Array
			},
			isObject: function(e) {
				return e === Object(e)
			},
			isNumber: function(e) {
				return e === e + 0
			},
			isString: function(e) {
				return e === e + ""
			},
			isBoolean: function(e) {
				return !1 === e || !0 === e
			},
			isFunction: function(e) {
				return e instanceof Function
			}
		},
		O = [{
			litmus: S.isString,
			conversions: {
				THREE_CHAR_HEX: {
					read: function(e) {
						var t = e.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i);
						return null !== t && {
							space: "HEX",
							hex: parseInt("0x" + t[1].toString() + t[1].toString() + t[2].toString() +
								t[2].toString() + t[3].toString() + t[3].toString(), 0)
						}
					},
					write: t
				},
				SIX_CHAR_HEX: {
					read: function(e) {
						var t = e.match(/^#([A-F0-9]{6})$/i);
						return null !== t && {
							space: "HEX",
							hex: parseInt("0x" + t[1].toString(), 0)
						}
					},
					write: t
				},
				CSS_RGB: {
					read: function(e) {
						var t = e.match(/^rgb\(\s*(\S+)\s*,\s*(\S+)\s*,\s*(\S+)\s*\)/);
						return null !== t && {
							space: "RGB",
							r: parseFloat(t[1]),
							g: parseFloat(t[2]),
							b: parseFloat(t[3])
						}
					},
					write: t
				},
				CSS_RGBA: {
					read: function(e) {
						var t = e.match(/^rgba\(\s*(\S+)\s*,\s*(\S+)\s*,\s*(\S+)\s*,\s*(\S+)\s*\)/);
						return null !== t && {
							space: "RGB",
							r: parseFloat(t[1]),
							g: parseFloat(t[2]),
							b: parseFloat(t[3]),
							a: parseFloat(t[4])
						}
					},
					write: t
				}
			}
		}, {
			litmus: S.isNumber,
			conversions: {
				HEX: {
					read: function(e) {
						return {
							space: "HEX",
							hex: e,
							conversionName: "HEX"
						}
					},
					write: function(e) {
						return e.hex
					}
				}
			}
		}, {
			litmus: S.isArray,
			conversions: {
				RGB_ARRAY: {
					read: function(e) {
						return 3 === e.length && {
							space: "RGB",
							r: e[0],
							g: e[1],
							b: e[2]
						}
					},
					write: function(e) {
						return [e.r, e.g, e.b]
					}
				},
				RGBA_ARRAY: {
					read: function(e) {
						return 4 === e.length && {
							space: "RGB",
							r: e[0],
							g: e[1],
							b: e[2],
							a: e[3]
						}
					},
					write: function(e) {
						return [e.r, e.g, e.b, e.a]
					}
				}
			}
		}, {
			litmus: S.isObject,
			conversions: {
				RGBA_OBJ: {
					read: function(e) {
						return !!(S.isNumber(e.r) && S.isNumber(e.g) && S.isNumber(e.b) && S.isNumber(e
							.a)) && {
							space: "RGB",
							r: e.r,
							g: e.g,
							b: e.b,
							a: e.a
						}
					},
					write: function(e) {
						return {
							r: e.r,
							g: e.g,
							b: e.b,
							a: e.a
						}
					}
				},
				RGB_OBJ: {
					read: function(e) {
						return !!(S.isNumber(e.r) && S.isNumber(e.g) && S.isNumber(e.b)) && {
							space: "RGB",
							r: e.r,
							g: e.g,
							b: e.b
						}
					},
					write: function(e) {
						return {
							r: e.r,
							g: e.g,
							b: e.b
						}
					}
				},
				HSVA_OBJ: {
					read: function(e) {
						return !!(S.isNumber(e.h) && S.isNumber(e.s) && S.isNumber(e.v) && S.isNumber(e
							.a)) && {
							space: "HSV",
							h: e.h,
							s: e.s,
							v: e.v,
							a: e.a
						}
					},
					write: function(e) {
						return {
							h: e.h,
							s: e.s,
							v: e.v,
							a: e.a
						}
					}
				},
				HSV_OBJ: {
					read: function(e) {
						return !!(S.isNumber(e.h) && S.isNumber(e.s) && S.isNumber(e.v)) && {
							space: "HSV",
							h: e.h,
							s: e.s,
							v: e.v
						}
					},
					write: function(e) {
						return {
							h: e.h,
							s: e.s,
							v: e.v
						}
					}
				}
			}
		}],
		T = void 0,
		L = void 0,
		R = function() {
			L = !1;
			var e = arguments.length > 1 ? S.toArray(arguments) : arguments[0];
			return S.each(O, function(t) {
				if (t.litmus(e)) return S.each(t.conversions, function(t, n) {
					if (T = t.read(e), !1 === L && !1 !== T) return L = T, T.conversionName = n,
						T.conversion = t, S.BREAK
				}), S.BREAK
			}), L
		},
		B = void 0,
		N = {
			hsv_to_rgb: function(e, t, n) {
				var o = Math.floor(e / 60) % 6,
					i = e / 60 - Math.floor(e / 60),
					r = n * (1 - t),
					s = n * (1 - i * t),
					a = n * (1 - (1 - i) * t),
					l = [
						[n, a, r],
						[s, n, r],
						[r, n, a],
						[r, s, n],
						[a, r, n],
						[n, r, s]
					][o];
				return {
					r: 255 * l[0],
					g: 255 * l[1],
					b: 255 * l[2]
				}
			},
			rgb_to_hsv: function(e, t, n) {
				var o = Math.min(e, t, n),
					i = Math.max(e, t, n),
					r = i - o,
					s = void 0,
					a = void 0;
				return 0 === i ? {
					h: NaN,
					s: 0,
					v: 0
				} : (a = r / i, s = e === i ? (t - n) / r : t === i ? 2 + (n - e) / r : 4 + (e - t) / r, (
					s /= 6) < 0 && (s += 1), {
					h: 360 * s,
					s: a,
					v: i / 255
				})
			},
			rgb_to_hex: function(e, t, n) {
				var o = this.hex_with_component(0, 2, e);
				return o = this.hex_with_component(o, 1, t), o = this.hex_with_component(o, 0, n)
			},
			component_from_hex: function(e, t) {
				return e >> 8 * t & 255
			},
			hex_with_component: function(e, t, n) {
				return n << (B = 8 * t) | e & ~(255 << B)
			}
		},
		H = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(e) {
			return typeof e
		} : function(e) {
			return e && "function" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ?
				"symbol" : typeof e
		},
		F = function(e, t) {
			if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function")
		},
		P = function() {
			function e(e, t) {
				for (var n = 0; n < t.length; n++) {
					var o = t[n];
					o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0),
						Object.defineProperty(e, o.key, o)
				}
			}
			return function(t, n, o) {
				return n && e(t.prototype, n), o && e(t, o), t
			}
		}(),
		D = function e(t, n, o) {
			null === t && (t = Function.prototype);
			var i = Object.getOwnPropertyDescriptor(t, n);
			if (void 0 === i) {
				var r = Object.getPrototypeOf(t);
				return null === r ? void 0 : e(r, n, o)
			}
			if ("value" in i) return i.value;
			var s = i.get;
			if (void 0 !== s) return s.call(o)
		},
		j = function(e, t) {
			if ("function" != typeof t && null !== t) throw new TypeError(
				"Super expression must either be null or a function, not " + typeof t);
			e.prototype = Object.create(t && t.prototype, {
				constructor: {
					value: e,
					enumerable: !1,
					writable: !0,
					configurable: !0
				}
			}), t && (Object.setPrototypeOf ? Object.setPrototypeOf(e, t) : e.__proto__ = t)
		},
		V = function(e, t) {
			if (!e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
			return !t || "object" != typeof t && "function" != typeof t ? e : t
		},
		I = function() {
			function e() {
				if (F(this, e), this.__state = R.apply(this, arguments), !1 === this.__state) throw new Error(
					"Failed to interpret color arguments");
				this.__state.a = this.__state.a || 1
			}
			return P(e, [{
				key: "toString",
				value: function() {
					return t(this)
				}
			}, {
				key: "toHexString",
				value: function() {
					return t(this, !0)
				}
			}, {
				key: "toOriginal",
				value: function() {
					return this.__state.conversion.write(this)
				}
			}]), e
		}();
	I.recalculateRGB = function(e, t, n) {
			if ("HEX" === e.__state.space) e.__state[t] = N.component_from_hex(e.__state.hex, n);
			else {
				if ("HSV" !== e.__state.space) throw new Error("Corrupted color state");
				S.extend(e.__state, N.hsv_to_rgb(e.__state.h, e.__state.s, e.__state.v))
			}
		}, I.recalculateHSV = function(e) {
			var t = N.rgb_to_hsv(e.r, e.g, e.b);
			S.extend(e.__state, {
				s: t.s,
				v: t.v
			}), S.isNaN(t.h) ? S.isUndefined(e.__state.h) && (e.__state.h = 0) : e.__state.h = t.h
		}, I.COMPONENTS = ["r", "g", "b", "h", "s", "v", "hex", "a"], n(I.prototype, "r", 2), n(I.prototype, "g",
			1), n(I.prototype, "b", 0), o(I.prototype, "h"), o(I.prototype, "s"), o(I.prototype, "v"), Object
		.defineProperty(I.prototype, "a", {
			get: function() {
				return this.__state.a
			},
			set: function(e) {
				this.__state.a = e
			}
		}), Object.defineProperty(I.prototype, "hex", {
			get: function() {
				return "HEX" !== this.__state.space && (this.__state.hex = N.rgb_to_hex(this.r, this.g, this
					.b), this.__state.space = "HEX"), this.__state.hex
			},
			set: function(e) {
				this.__state.space = "HEX", this.__state.hex = e
			}
		});
	var z = function() {
			function e(t, n) {
				F(this, e), this.initialValue = t[n], this.domElement = document.createElement("div"), this.object =
					t, this.property = n, this.__onChange = void 0, this.__onFinishChange = void 0
			}
			return P(e, [{
				key: "onChange",
				value: function(e) {
					return this.__onChange = e, this
				}
			}, {
				key: "onFinishChange",
				value: function(e) {
					return this.__onFinishChange = e, this
				}
			}, {
				key: "setValue",
				value: function(e) {
					return this.object[this.property] = e, this.__onChange && this.__onChange.call(
						this, e), this.updateDisplay(), this
				}
			}, {
				key: "getValue",
				value: function() {
					return this.object[this.property]
				}
			}, {
				key: "updateDisplay",
				value: function() {
					return this
				}
			}, {
				key: "isModified",
				value: function() {
					return this.initialValue !== this.getValue()
				}
			}]), e
		}(),
		M = {
			HTMLEvents: ["change"],
			MouseEvents: ["click", "mousemove", "mousedown", "mouseup", "mouseover"],
			KeyboardEvents: ["keydown"]
		},
		G = {};
	S.each(M, function(e, t) {
		S.each(e, function(e) {
			G[e] = t
		})
	});
	var U = /(\d+(\.\d+)?)px/,
		X = {
			makeSelectable: function(e, t) {
				void 0 !== e && void 0 !== e.style && (e.onselectstart = t ? function() {
						return !1
					} : function() {}, e.style.MozUserSelect = t ? "auto" : "none", e.style
					.KhtmlUserSelect = t ? "auto" : "none", e.unselectable = t ? "on" : "off")
			},
			makeFullscreen: function(e, t, n) {
				var o = n,
					i = t;
				S.isUndefined(i) && (i = !0), S.isUndefined(o) && (o = !0), e.style.position = "absolute", i &&
					(e.style.left = 0, e.style.right = 0), o && (e.style.top = 0, e.style.bottom = 0)
			},
			fakeEvent: function(e, t, n, o) {
				var i = n || {},
					r = G[t];
				if (!r) throw new Error("Event type " + t + " not supported.");
				var s = document.createEvent(r);
				switch (r) {
					case "MouseEvents":
						var a = i.x || i.clientX || 0,
							l = i.y || i.clientY || 0;
						s.initMouseEvent(t, i.bubbles || !1, i.cancelable || !0, window, i.clickCount || 1, 0,
							0, a, l, !1, !1, !1, !1, 0, null);
						break;
					case "KeyboardEvents":
						var d = s.initKeyboardEvent || s.initKeyEvent;
						S.defaults(i, {
							cancelable: !0,
							ctrlKey: !1,
							altKey: !1,
							shiftKey: !1,
							metaKey: !1,
							keyCode: void 0,
							charCode: void 0
						}), d(t, i.bubbles || !1, i.cancelable, window, i.ctrlKey, i.altKey, i.shiftKey, i
							.metaKey, i.keyCode, i.charCode);
						break;
					default:
						s.initEvent(t, i.bubbles || !1, i.cancelable || !0)
				}
				S.defaults(s, o), e.dispatchEvent(s)
			},
			bind: function(e, t, n, o) {
				var i = o || !1;
				return e.addEventListener ? e.addEventListener(t, n, i) : e.attachEvent && e.attachEvent("on" +
					t, n), X
			},
			unbind: function(e, t, n, o) {
				var i = o || !1;
				return e.removeEventListener ? e.removeEventListener(t, n, i) : e.detachEvent && e.detachEvent(
					"on" + t, n), X
			},
			addClass: function(e, t) {
				if (void 0 === e.className) e.className = t;
				else if (e.className !== t) {
					var n = e.className.split(/ +/); - 1 === n.indexOf(t) && (n.push(t), e.className = n.join(
						" ").replace(/^\s+/, "").replace(/\s+$/, ""))
				}
				return X
			},
			removeClass: function(e, t) {
				if (t)
					if (e.className === t) e.removeAttribute("class");
					else {
						var n = e.className.split(/ +/),
							o = n.indexOf(t); - 1 !== o && (n.splice(o, 1), e.className = n.join(" "))
					}
				else e.className = void 0;
				return X
			},
			hasClass: function(e, t) {
				return new RegExp("(?:^|\\s+)" + t + "(?:\\s+|$)").test(e.className) || !1
			},
			getWidth: function(e) {
				var t = getComputedStyle(e);
				return i(t["border-left-width"]) + i(t["border-right-width"]) + i(t["padding-left"]) + i(t[
					"padding-right"]) + i(t.width)
			},
			getHeight: function(e) {
				var t = getComputedStyle(e);
				return i(t["border-top-width"]) + i(t["border-bottom-width"]) + i(t["padding-top"]) + i(t[
					"padding-bottom"]) + i(t.height)
			},
			getOffset: function(e) {
				var t = e,
					n = {
						left: 0,
						top: 0
					};
				if (t.offsetParent)
					do {
						n.left += t.offsetLeft, n.top += t.offsetTop, t = t.offsetParent
					} while (t);
				return n
			},
			isActive: function(e) {
				return e === document.activeElement && (e.type || e.href)
			}
		},
		K = function(e) {
			function t(e, n) {
				F(this, t);
				var o = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n)),
					i = o;
				return o.__prev = o.getValue(), o.__checkbox = document.createElement("input"), o.__checkbox
					.setAttribute("type", "checkbox"), X.bind(o.__checkbox, "change", function() {
						i.setValue(!i.__prev)
					}, !1), o.domElement.appendChild(o.__checkbox), o.updateDisplay(), o
			}
			return j(t, z), P(t, [{
				key: "setValue",
				value: function(e) {
					var n = D(t.prototype.__proto__ || Object.getPrototypeOf(t.prototype),
						"setValue", this).call(this, e);
					return this.__onFinishChange && this.__onFinishChange.call(this, this
						.getValue()), this.__prev = this.getValue(), n
				}
			}, {
				key: "updateDisplay",
				value: function() {
					return !0 === this.getValue() ? (this.__checkbox.setAttribute("checked",
						"checked"), this.__checkbox.checked = !0, this.__prev = !0) : (this
						.__checkbox.checked = !1, this.__prev = !1), D(t.prototype.__proto__ ||
						Object.getPrototypeOf(t.prototype), "updateDisplay", this).call(this)
				}
			}]), t
		}(),
		Y = function(e) {
			function t(e, n, o) {
				F(this, t);
				var i = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n)),
					r = o,
					s = i;
				if (i.__select = document.createElement("select"), S.isArray(r)) {
					var a = {};
					S.each(r, function(e) {
						a[e] = e
					}), r = a
				}
				return S.each(r, function(e, t) {
					var n = document.createElement("option");
					n.innerHTML = t, n.setAttribute("value", e), s.__select.appendChild(n)
				}), i.updateDisplay(), X.bind(i.__select, "change", function() {
					var e = this.options[this.selectedIndex].value;
					s.setValue(e)
				}), i.domElement.appendChild(i.__select), i
			}
			return j(t, z), P(t, [{
				key: "setValue",
				value: function(e) {
					var n = D(t.prototype.__proto__ || Object.getPrototypeOf(t.prototype),
						"setValue", this).call(this, e);
					return this.__onFinishChange && this.__onFinishChange.call(this, this
						.getValue()), n
				}
			}, {
				key: "updateDisplay",
				value: function() {
					return X.isActive(this.__select) ? this : (this.__select.value = this
						.getValue(), D(t.prototype.__proto__ || Object.getPrototypeOf(t
								.prototype),
							"updateDisplay", this).call(this))
				}
			}]), t
		}(),
		J = function(e) {
			function t(e, n) {
				function o() {
					r.setValue(r.__input.value)
				}
				F(this, t);
				var i = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n)),
					r = i;
				return i.__input = document.createElement("input"), i.__input.setAttribute("type", "text"), X.bind(i
					.__input, "keyup", o), X.bind(i.__input, "change", o), X.bind(i.__input, "blur",
					function() {
						r.__onFinishChange && r.__onFinishChange.call(r, r.getValue())
					}), X.bind(i.__input, "keydown", function(e) {
					13 === e.keyCode && this.blur()
				}), i.updateDisplay(), i.domElement.appendChild(i.__input), i
			}
			return j(t, z), P(t, [{
				key: "updateDisplay",
				value: function() {
					return X.isActive(this.__input) || (this.__input.value = this.getValue()), D(t
						.prototype.__proto__ || Object.getPrototypeOf(t.prototype),
						"updateDisplay", this).call(this)
				}
			}]), t
		}(),
		W = function(e) {
			function t(e, n, o) {
				F(this, t);
				var i = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n)),
					s = o || {};
				return i.__min = s.min, i.__max = s.max, i.__step = s.step, S.isUndefined(i.__step) ? 0 === i
					.initialValue ? i.__impliedStep = 1 : i.__impliedStep = Math.pow(10, Math.floor(Math.log(Math
						.abs(i.initialValue)) / Math.LN10)) / 10 : i.__impliedStep = i.__step, i.__precision = r(i
						.__impliedStep), i
			}
			return j(t, z), P(t, [{
				key: "setValue",
				value: function(e) {
					var n = e;
					return void 0 !== this.__min && n < this.__min ? n = this.__min : void 0 !==
						this.__max && n > this.__max && (n = this.__max), void 0 !== this.__step &&
						n % this.__step != 0 && (n = Math.round(n / this.__step) * this.__step), D(t
							.prototype.__proto__ || Object.getPrototypeOf(t.prototype), "setValue",
							this).call(this, n)
				}
			}, {
				key: "min",
				value: function(e) {
					return this.__min = e, this
				}
			}, {
				key: "max",
				value: function(e) {
					return this.__max = e, this
				}
			}, {
				key: "step",
				value: function(e) {
					return this.__step = e, this.__impliedStep = e, this.__precision = r(e), this
				}
			}]), t
		}(),
		Q = function(e) {
			function t(e, n, o) {
				function i() {
					l.__onFinishChange && l.__onFinishChange.call(l, l.getValue())
				}

				function r(e) {
					var t = d - e.clientY;
					l.setValue(l.getValue() + t * l.__impliedStep), d = e.clientY
				}

				function s() {
					X.unbind(window, "mousemove", r), X.unbind(window, "mouseup", s), i()
				}
				F(this, t);
				var a = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n, o));
				a.__truncationSuspended = !1;
				var l = a,
					d = void 0;
				return a.__input = document.createElement("input"), a.__input.setAttribute("type", "text"), X.bind(a
					.__input, "change",
					function() {
						var e = parseFloat(l.__input.value);
						S.isNaN(e) || l.setValue(e)
					}), X.bind(a.__input, "blur", function() {
					i()
				}), X.bind(a.__input, "mousedown", function(e) {
					X.bind(window, "mousemove", r), X.bind(window, "mouseup", s), d = e.clientY
				}), X.bind(a.__input, "keydown", function(e) {
					13 === e.keyCode && (l.__truncationSuspended = !0, this.blur(), l
						.__truncationSuspended = !1, i())
				}), a.updateDisplay(), a.domElement.appendChild(a.__input), a
			}
			return j(t, W), P(t, [{
				key: "updateDisplay",
				value: function() {
					return this.__input.value = this.__truncationSuspended ? this.getValue() : s(
						this.getValue(), this.__precision), D(t.prototype.__proto__ || Object
						.getPrototypeOf(t.prototype), "updateDisplay", this).call(this)
				}
			}]), t
		}(),
		q = function(e) {
			function t(e, n, o, i, r) {
				function s(e) {
					e.preventDefault();
					var t = _.__background.getBoundingClientRect();
					return _.setValue(a(e.clientX, t.left, t.right, _.__min, _.__max)), !1
				}

				function l() {
					X.unbind(window, "mousemove", s), X.unbind(window, "mouseup", l), _.__onFinishChange && _
						.__onFinishChange.call(_, _.getValue())
				}

				function d(e) {
					var t = e.touches[0].clientX,
						n = _.__background.getBoundingClientRect();
					_.setValue(a(t, n.left, n.right, _.__min, _.__max))
				}

				function c() {
					X.unbind(window, "touchmove", d), X.unbind(window, "touchend", c), _.__onFinishChange && _
						.__onFinishChange.call(_, _.getValue())
				}
				F(this, t);
				var u = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n, {
						min: o,
						max: i,
						step: r
					})),
					_ = u;
				return u.__background = document.createElement("div"), u.__foreground = document.createElement(
						"div"), X.bind(u.__background, "mousedown", function(e) {
						document.activeElement.blur(), X.bind(window, "mousemove", s), X.bind(window, "mouseup",
							l), s(e)
					}), X.bind(u.__background, "touchstart", function(e) {
						1 === e.touches.length && (X.bind(window, "touchmove", d), X.bind(window, "touchend",
							c), d(e))
					}), X.addClass(u.__background, "slider"), X.addClass(u.__foreground, "slider-fg"), u
					.updateDisplay(), u.__background.appendChild(u.__foreground), u.domElement.appendChild(u
						.__background), u
			}
			return j(t, W), P(t, [{
				key: "updateDisplay",
				value: function() {
					var e = (this.getValue() - this.__min) / (this.__max - this.__min);
					return this.__foreground.style.width = 100 * e + "%", D(t.prototype.__proto__ ||
						Object.getPrototypeOf(t.prototype), "updateDisplay", this).call(this)
				}
			}]), t
		}(),
		Z = function(e) {
			function t(e, n, o) {
				F(this, t);
				var i = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n)),
					r = i;
				return i.__button = document.createElement("div"), i.__button.innerHTML = void 0 === o ? "Fire" : o,
					X.bind(i.__button, "click", function(e) {
						return e.preventDefault(), r.fire(), !1
					}), X.addClass(i.__button, "button"), i.domElement.appendChild(i.__button), i
			}
			return j(t, z), P(t, [{
				key: "fire",
				value: function() {
					this.__onChange && this.__onChange.call(this), this.getValue().call(this
						.object), this.__onFinishChange && this.__onFinishChange.call(this, this
						.getValue())
				}
			}]), t
		}(),
		$ = function(e) {
			function t(e, n) {
				function o(e) {
					u(e), X.bind(window, "mousemove", u), X.bind(window, "touchmove", u), X.bind(window, "mouseup",
						r), X.bind(window, "touchend", r)
				}

				function i(e) {
					_(e), X.bind(window, "mousemove", _), X.bind(window, "touchmove", _), X.bind(window, "mouseup",
						s), X.bind(window, "touchend", s)
				}

				function r() {
					X.unbind(window, "mousemove", u), X.unbind(window, "touchmove", u), X.unbind(window, "mouseup",
						r), X.unbind(window, "touchend", r), c()
				}

				function s() {
					X.unbind(window, "mousemove", _), X.unbind(window, "touchmove", _), X.unbind(window, "mouseup",
						s), X.unbind(window, "touchend", s), c()
				}

				function a() {
					var e = R(this.value);
					!1 !== e ? (p.__color.__state = e, p.setValue(p.__color.toOriginal())) : this.value = p.__color
						.toString()
				}

				function c() {
					p.__onFinishChange && p.__onFinishChange.call(p, p.__color.toOriginal())
				}

				function u(e) {
					-1 === e.type.indexOf("touch") && e.preventDefault();
					var t = p.__saturation_field.getBoundingClientRect(),
						n = e.touches && e.touches[0] || e,
						o = n.clientX,
						i = n.clientY,
						r = (o - t.left) / (t.right - t.left),
						s = 1 - (i - t.top) / (t.bottom - t.top);
					return s > 1 ? s = 1 : s < 0 && (s = 0), r > 1 ? r = 1 : r < 0 && (r = 0), p.__color.v = s, p
						.__color.s = r, p.setValue(p.__color.toOriginal()), !1
				}

				function _(e) {
					-1 === e.type.indexOf("touch") && e.preventDefault();
					var t = p.__hue_field.getBoundingClientRect(),
						n = 1 - ((e.touches && e.touches[0] || e).clientY - t.top) / (t.bottom - t.top);
					return n > 1 ? n = 1 : n < 0 && (n = 0), p.__color.h = 360 * n, p.setValue(p.__color
						.toOriginal()), !1
				}
				F(this, t);
				var h = V(this, (t.__proto__ || Object.getPrototypeOf(t)).call(this, e, n));
				h.__color = new I(h.getValue()), h.__temp = new I(0);
				var p = h;
				h.domElement = document.createElement("div"), X.makeSelectable(h.domElement, !1), h.__selector =
					document.createElement("div"), h.__selector.className = "selector", h.__saturation_field =
					document.createElement("div"), h.__saturation_field.className = "saturation-field", h
					.__field_knob = document.createElement("div"), h.__field_knob.className = "field-knob", h
					.__field_knob_border = "2px solid ", h.__hue_knob = document.createElement("div"), h.__hue_knob
					.className = "hue-knob", h.__hue_field = document.createElement("div"), h.__hue_field
					.className = "hue-field", h.__input = document.createElement("input"), h.__input.type = "text",
					h.__input_textShadow = "0 1px 1px ", X.bind(h.__input, "keydown", function(e) {
						13 === e.keyCode && a.call(this)
					}), X.bind(h.__input, "blur", a), X.bind(h.__selector, "mousedown", function() {
						X.addClass(this, "drag").bind(window, "mouseup", function() {
							X.removeClass(p.__selector, "drag")
						})
					}), X.bind(h.__selector, "touchstart", function() {
						X.addClass(this, "drag").bind(window, "touchend", function() {
							X.removeClass(p.__selector, "drag")
						})
					});
				var f = document.createElement("div");
				return S.extend(h.__selector.style, {
						width: "122px",
						height: "102px",
						padding: "3px",
						backgroundColor: "#222",
						boxShadow: "0px 1px 3px rgba(0,0,0,0.3)"
					}), S.extend(h.__field_knob.style, {
						position: "absolute",
						width: "12px",
						height: "12px",
						border: h.__field_knob_border + (h.__color.v < .5 ? "#fff" : "#000"),
						boxShadow: "0px 1px 3px rgba(0,0,0,0.5)",
						borderRadius: "12px",
						zIndex: 1
					}), S.extend(h.__hue_knob.style, {
						position: "absolute",
						width: "15px",
						height: "2px",
						borderRight: "4px solid #fff",
						zIndex: 1
					}), S.extend(h.__saturation_field.style, {
						width: "100px",
						height: "100px",
						border: "1px solid #555",
						marginRight: "3px",
						display: "inline-block",
						cursor: "pointer"
					}), S.extend(f.style, {
						width: "100%",
						height: "100%",
						background: "none"
					}), l(f, "top", "rgba(0,0,0,0)", "#000"), S.extend(h.__hue_field.style, {
						width: "15px",
						height: "100px",
						border: "1px solid #555",
						cursor: "ns-resize",
						position: "absolute",
						top: "3px",
						right: "3px"
					}), d(h.__hue_field), S.extend(h.__input.style, {
						outline: "none",
						textAlign: "center",
						color: "#fff",
						border: 0,
						fontWeight: "bold",
						textShadow: h.__input_textShadow + "rgba(0,0,0,0.7)"
					}), X.bind(h.__saturation_field, "mousedown", o), X.bind(h.__saturation_field, "touchstart", o),
					X.bind(h.__field_knob, "mousedown", o), X.bind(h.__field_knob, "touchstart", o), X.bind(h
						.__hue_field, "mousedown", i), X.bind(h.__hue_field, "touchstart", i), h.__saturation_field
					.appendChild(f), h.__selector.appendChild(h.__field_knob), h.__selector.appendChild(h
						.__saturation_field), h.__selector.appendChild(h.__hue_field), h.__hue_field.appendChild(h
						.__hue_knob), h.domElement.appendChild(h.__input), h.domElement.appendChild(h.__selector), h
					.updateDisplay(), h
			}
			return j(t, z), P(t, [{
				key: "updateDisplay",
				value: function() {
					var e = R(this.getValue());
					if (!1 !== e) {
						var t = !1;
						S.each(I.COMPONENTS, function(n) {
							if (!S.isUndefined(e[n]) && !S.isUndefined(this.__color.__state[
									n]) && e[n] !== this.__color.__state[n]) return t = !
								0, {}
						}, this), t && S.extend(this.__color.__state, e)
					}
					S.extend(this.__temp.__state, this.__color.__state), this.__temp.a = 1;
					var n = this.__color.v < .5 || this.__color.s > .5 ? 255 : 0,
						o = 255 - n;
					S.extend(this.__field_knob.style, {
							marginLeft: 100 * this.__color.s - 7 + "px",
							marginTop: 100 * (1 - this.__color.v) - 7 + "px",
							backgroundColor: this.__temp.toHexString(),
							border: this.__field_knob_border + "rgb(" + n + "," + n + "," + n +
								")"
						}), this.__hue_knob.style.marginTop = 100 * (1 - this.__color.h / 360) +
						"px", this.__temp.s = 1, this.__temp.v = 1, l(this.__saturation_field,
							"left", "#fff", this.__temp.toHexString()), this.__input.value = this
						.__color.toString(), S.extend(this.__input.style, {
							backgroundColor: this.__color.toHexString(),
							color: "rgb(" + n + "," + n + "," + n + ")",
							textShadow: this.__input_textShadow + "rgba(" + o + "," + o + "," +
								o + ",.7)"
						})
				}
			}]), t
		}(),
		ee = ["-moz-", "-o-", "-webkit-", "-ms-", ""],
		te = {
			load: function(e, t) {
				var n = t || document,
					o = n.createElement("link");
				o.type = "text/css", o.rel = "stylesheet", o.href = e, n.getElementsByTagName("head")[0]
					.appendChild(o)
			},
			inject: function(e, t) {
				var n = t || document,
					o = document.createElement("style");
				o.type = "text/css", o.innerHTML = e;
				var i = n.getElementsByTagName("head")[0];
				try {
					i.appendChild(o)
				} catch (e) {}
			}
		},
		ne = function(e, t) {
			var n = e[t];
			return S.isArray(arguments[2]) || S.isObject(arguments[2]) ? new Y(e, t, arguments[2]) : S.isNumber(n) ?
				S.isNumber(arguments[2]) && S.isNumber(arguments[3]) ? S.isNumber(arguments[4]) ? new q(e, t,
					arguments[2], arguments[3], arguments[4]) : new q(e, t, arguments[2], arguments[3]) : S
				.isNumber(arguments[4]) ? new Q(e, t, {
					min: arguments[2],
					max: arguments[3],
					step: arguments[4]
				}) : new Q(e, t, {
					min: arguments[2],
					max: arguments[3]
				}) : S.isString(n) ? new J(e, t) : S.isFunction(n) ? new Z(e, t, "") : S.isBoolean(n) ? new K(e,
					t) : null
		},
		oe = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window
		.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(
			e) {
			setTimeout(e, 1e3 / 60)
		},
		ie = function() {
			function e() {
				F(this, e), this.backgroundElement = document.createElement("div"), S.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",
							transition: "opacity 0.2s linear"
						}), X.makeFullscreen(this.backgroundElement), this.backgroundElement.style.position =
					"fixed", this.domElement = document.createElement("div"), S.extend(this.domElement.style, {
						position: "fixed",
						display: "none",
						zIndex: "1001",
						opacity: 0,
						WebkitTransition: "-webkit-transform 0.2s ease-out, opacity 0.2s linear",
						transition: "transform 0.2s ease-out, opacity 0.2s linear"
					}), document.body.appendChild(this.backgroundElement), document.body.appendChild(this
						.domElement);
				var t = this;
				X.bind(this.backgroundElement, "click", function() {
					t.hide()
				})
			}
			return P(e, [{
				key: "show",
				value: function() {
					var e = 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(), S.defer(function() {
							e.backgroundElement.style.opacity = 1, e.domElement.style.opacity =
								1, e.domElement.style.webkitTransform = "scale(1)"
						})
				}
			}, {
				key: "hide",
				value: function() {
					var e = this,
						t = function t() {
							e.domElement.style.display = "none", e.backgroundElement.style.display =
								"none", X.unbind(e.domElement, "webkitTransitionEnd", t), X.unbind(e
									.domElement, "transitionend", t), X.unbind(e.domElement,
									"oTransitionEnd", t)
						};
					X.bind(this.domElement, "webkitTransitionEnd", t), X.bind(this.domElement,
							"transitionend", t), X.bind(this.domElement, "oTransitionEnd", t), this
						.backgroundElement.style.opacity = 0, this.domElement.style.opacity = 0,
						this.domElement.style.webkitTransform = "scale(1.1)"
				}
			}, {
				key: "layout",
				value: function() {
					this.domElement.style.left = window.innerWidth / 2 - X.getWidth(this
							.domElement) / 2 + "px", this.domElement.style.top = window
						.innerHeight /
						2 - X.getHeight(this.domElement) / 2 + "px"
				}
			}]), e
		}(),
		re = function(e) {
			if (e && "undefined" != typeof window) {
				var t = document.createElement("style");
				return t.setAttribute("type", "text/css"), t.innerHTML = e, document.head.appendChild(t), e
			}
		}(
			".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 .1s linear;-o-transition:opacity .1s linear;-moz-transition:opacity .1s linear;transition:opacity .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 .1s linear;-o-transition:opacity .1s linear;-moz-transition:opacity .1s linear;transition:opacity .1s linear;border:0;line-height:19px;height:20px;cursor:pointer;text-align:center;background-color:#000}.dg.main .close-button.close-top{position:relative}.dg.main .close-button.close-bottom{position:absolute}.dg.main .close-button:hover{background-color:#111}.dg.a{float:right;margin-right:15px;overflow-y:visible}.dg.a.has-save>ul.close-top{margin-top:0}.dg.a.has-save>ul.close-bottom{margin-top:27px}.dg.a.has-save>ul.closed{margin-top:0}.dg.a .save-row{top:0;z-index:1002}.dg.a .save-row.close-top{position:relative}.dg.a .save-row.close-bottom{position:fixed}.dg li{-webkit-transition:height .1s ease-out;-o-transition:height .1s ease-out;-moz-transition:height .1s ease-out;transition:height .1s ease-out;-webkit-transition:overflow .1s linear;-o-transition:overflow .1s linear;-moz-transition:overflow .1s linear;transition:overflow .1s linear}.dg li:not(.folder){cursor:auto;height:27px;line-height:27px;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;overflow:hidden}.dg .property-name{cursor:default;float:left;clear:left;width:40%;overflow:hidden;text-overflow:ellipsis}.dg .cr.function .property-name{width:100%}.dg .c{float:left;width:60%;position:relative}.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:7px}.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 .cr.color{overflow:visible}.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,R0lGODlhBQAFAJEAAPPz8yH5BAEAAAIALAAAAAAFAAUAAAIIlI+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,R0lGODlhBQAFAJEAAPPz8yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==)}.dg .cr.boolean{border-left:3px solid #806787}.dg .cr.color{border-left:3px solid}.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;max-width:100%}.dg .c .slider:hover{background:#3c3c3c}.dg .c .slider:hover .slider-fg{background:#44abda}\n"
			);
	te.inject(re);
	var se = "Default",
		ae = function() {
			try {
				return !!window.localStorage
			} catch (e) {
				return !1
			}
		}(),
		le = void 0,
		de = !0,
		ce = void 0,
		ue = !1,
		_e = [],
		he = function e(t) {
			var n = this,
				o = t || {};
			this.domElement = document.createElement("div"), this.__ul = document.createElement("ul"), this
				.domElement.appendChild(this.__ul), X.addClass(this.domElement, "dg"), this.__folders = {}, this
				.__controllers = [], this.__rememberedObjects = [], this
				.__rememberedObjectIndecesToControllers = [], this.__listening = [], o = S.defaults(o, {
					closeOnTop: !1,
					autoPlace: !0,
					width: e.DEFAULT_WIDTH
				}), o = S.defaults(o, {
					resizable: o.autoPlace,
					hideable: o.autoPlace
				}), S.isUndefined(o.load) ? o.load = {
					preset: se
				} : o.preset && (o.load.preset = o.preset), S.isUndefined(o.parent) && o.hideable && _e.push(this),
				o.resizable = S.isUndefined(o.parent) && o.resizable, o.autoPlace && S.isUndefined(o.scrollable) &&
				(o.scrollable = !0);
			var i = ae && "true" === localStorage.getItem(m(this, "isLocal")),
				r = void 0,
				s = void 0;
			if (Object.defineProperties(this, {
					parent: {
						get: function() {
							return o.parent
						}
					},
					scrollable: {
						get: function() {
							return o.scrollable
						}
					},
					autoPlace: {
						get: function() {
							return o.autoPlace
						}
					},
					closeOnTop: {
						get: function() {
							return o.closeOnTop
						}
					},
					preset: {
						get: function() {
							return n.parent ? n.getRoot().preset : o.load.preset
						},
						set: function(e) {
							n.parent ? n.getRoot().preset = e : o.load.preset = e, E(this), n.revert()
						}
					},
					width: {
						get: function() {
							return o.width
						},
						set: function(e) {
							o.width = e, w(n, e)
						}
					},
					name: {
						get: function() {
							return o.name
						},
						set: function(e) {
							o.name = e, s && (s.innerHTML = o.name)
						}
					},
					closed: {
						get: function() {
							return o.closed
						},
						set: function(t) {
							o.closed = t, o.closed ? X.addClass(n.__ul, e.CLASS_CLOSED) : X.removeClass(n
								.__ul, e.CLASS_CLOSED), this.onResize(), n.__closeButton && (n
								.__closeButton.innerHTML = t ? e.TEXT_OPEN : e.TEXT_CLOSED)
						}
					},
					load: {
						get: function() {
							return o.load
						}
					},
					useLocalStorage: {
						get: function() {
							return i
						},
						set: function(e) {
							ae && (i = e, e ? X.bind(window, "unload", r) : X.unbind(window, "unload", r),
								localStorage.setItem(m(n, "isLocal"), e))
						}
					}
				}), S.isUndefined(o.parent)) {
				if (this.closed = o.closed || !1, X.addClass(this.domElement, e.CLASS_MAIN), X.makeSelectable(this
						.domElement, !1), ae && i) {
					n.useLocalStorage = !0;
					var a = localStorage.getItem(m(this, "gui"));
					a && (o.load = JSON.parse(a))
				}
				this.__closeButton = document.createElement("div"), this.__closeButton.innerHTML = e.TEXT_CLOSED, X
					.addClass(this.__closeButton, e.CLASS_CLOSE_BUTTON), o.closeOnTop ? (X.addClass(this
						.__closeButton, e.CLASS_CLOSE_TOP), this.domElement.insertBefore(this.__closeButton,
						this.domElement.childNodes[0])) : (X.addClass(this.__closeButton, e.CLASS_CLOSE_BOTTOM),
						this.domElement.appendChild(this.__closeButton)), X.bind(this.__closeButton, "click",
						function() {
							n.closed = !n.closed
						})
			} else {
				void 0 === o.closed && (o.closed = !0);
				var l = document.createTextNode(o.name);
				X.addClass(l, "controller-name"), s = c(n, l);
				X.addClass(this.__ul, e.CLASS_CLOSED), X.addClass(s, "title"), X.bind(s, "click", function(e) {
					return e.preventDefault(), n.closed = !n.closed, !1
				}), o.closed || (this.closed = !1)
			}
			o.autoPlace && (S.isUndefined(o.parent) && (de && (ce = document.createElement("div"), X.addClass(ce,
						"dg"), X.addClass(ce, e.CLASS_AUTO_PLACE_CONTAINER), document.body.appendChild(ce),
					de = !1), ce.appendChild(this.domElement), X.addClass(this.domElement, e
					.CLASS_AUTO_PLACE)), this.parent || w(n, o.width)), this.__resizeHandler = function() {
					n.onResizeDebounced()
				}, X.bind(window, "resize", this.__resizeHandler), X.bind(this.__ul, "webkitTransitionEnd", this
					.__resizeHandler), X.bind(this.__ul, "transitionend", this.__resizeHandler), X.bind(this.__ul,
					"oTransitionEnd", this.__resizeHandler), this.onResize(), o.resizable && y(this), r =
				function() {
					ae && "true" === localStorage.getItem(m(n, "isLocal")) && localStorage.setItem(m(n, "gui"), JSON
						.stringify(n.getSaveObject()))
				}, this.saveToLocalStorageIfPossible = r, o.parent || function() {
					var e = n.getRoot();
					e.width += 1, S.defer(function() {
						e.width -= 1
					})
				}()
		};
	he.toggleHide = function() {
			ue = !ue, S.each(_e, function(e) {
				e.domElement.style.display = ue ? "none" : ""
			})
		}, he.CLASS_AUTO_PLACE = "a", he.CLASS_AUTO_PLACE_CONTAINER = "ac", he.CLASS_MAIN = "main", he
		.CLASS_CONTROLLER_ROW = "cr", he.CLASS_TOO_TALL = "taller-than-window", he.CLASS_CLOSED = "closed", he
		.CLASS_CLOSE_BUTTON = "close-button", he.CLASS_CLOSE_TOP = "close-top", he.CLASS_CLOSE_BOTTOM =
		"close-bottom", he.CLASS_DRAG = "drag", he.DEFAULT_WIDTH = 245, he.TEXT_CLOSED = "Close Controls", he
		.TEXT_OPEN = "Open Controls", he._keydownHandler = function(e) {
			"text" === document.activeElement.type || 72 !== e.which && 72 !== e.keyCode || he.toggleHide()
		}, X.bind(window, "keydown", he._keydownHandler, !1), S.extend(he.prototype, {
			add: function(e, t) {
				return f(this, e, t, {
					factoryArgs: Array.prototype.slice.call(arguments, 2)
				})
			},
			addColor: function(e, t) {
				return f(this, e, t, {
					color: !0
				})
			},
			remove: function(e) {
				this.__ul.removeChild(e.__li), this.__controllers.splice(this.__controllers.indexOf(e), 1);
				var t = this;
				S.defer(function() {
					t.onResize()
				})
			},
			destroy: function() {
				if (this.parent) throw new Error(
					"Only the root GUI should be removed with .destroy(). For subfolders, use gui.removeFolder(folder) instead."
				);
				this.autoPlace && ce.removeChild(this.domElement);
				var e = this;
				S.each(this.__folders, function(t) {
					e.removeFolder(t)
				}), X.unbind(window, "keydown", he._keydownHandler, !1), u(this)
			},
			addFolder: function(e) {
				if (void 0 !== this.__folders[e]) throw new Error(
					'You already have a folder in this GUI by the name "' + e + '"');
				var t = {
					name: e,
					parent: this
				};
				t.autoPlace = this.autoPlace, this.load && this.load.folders && this.load.folders[e] && (t
					.closed = this.load.folders[e].closed, t.load = this.load.folders[e]);
				var n = new he(t);
				this.__folders[e] = n;
				var o = c(this, n.domElement);
				return X.addClass(o, "folder"), n
			},
			removeFolder: function(e) {
				this.__ul.removeChild(e.domElement.parentElement), delete this.__folders[e.name], this
					.load && this.load.folders && this.load.folders[e.name] && delete this.load.folders[e
						.name], u(e);
				var t = this;
				S.each(e.__folders, function(t) {
					e.removeFolder(t)
				}), S.defer(function() {
					t.onResize()
				})
			},
			open: function() {
				this.closed = !1
			},
			close: function() {
				this.closed = !0
			},
			hide: function() {
				this.domElement.style.display = "none"
			},
			show: function() {
				this.domElement.style.display = ""
			},
			onResize: function() {
				var e = this.getRoot();
				if (e.scrollable) {
					var t = X.getOffset(e.__ul).top,
						n = 0;
					S.each(e.__ul.childNodes, function(t) {
						e.autoPlace && t === e.__save_row || (n += X.getHeight(t))
					}), window.innerHeight - t - 20 < n ? (X.addClass(e.domElement, he.CLASS_TOO_TALL),
						e.__ul.style.height = window.innerHeight - t - 20 + "px") : (X.removeClass(e
						.domElement, he.CLASS_TOO_TALL), e.__ul.style.height = "auto")
				}
				e.__resize_handle && S.defer(function() {
					e.__resize_handle.style.height = e.__ul.offsetHeight + "px"
				}), e.__closeButton && (e.__closeButton.style.width = e.width + "px")
			},
			onResizeDebounced: S.debounce(function() {
				this.onResize()
			}, 50),
			remember: function() {
				if (S.isUndefined(le) && ((le = new ie).domElement.innerHTML =
						'<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>'
					), this.parent) throw new Error("You can only call remember on a top level GUI.");
				var e = this;
				S.each(Array.prototype.slice.call(arguments), function(t) {
					0 === e.__rememberedObjects.length && v(e), -1 === e.__rememberedObjects
						.indexOf(t) && e.__rememberedObjects.push(t)
				}), this.autoPlace && w(this, this.width)
			},
			getRoot: function() {
				for (var e = this; e.parent;) e = e.parent;
				return e
			},
			getSaveObject: function() {
				var e = this.load;
				return e.closed = this.closed, this.__rememberedObjects.length > 0 && (e.preset = this
						.preset, e.remembered || (e.remembered = {}), e.remembered[this.preset] = x(this)),
					e.folders = {}, S.each(this.__folders, function(t, n) {
						e.folders[n] = t.getSaveObject()
					}), e
			},
			save: function() {
				this.load.remembered || (this.load.remembered = {}), this.load.remembered[this.preset] = x(
					this), _(this, !1), this.saveToLocalStorageIfPossible()
			},
			saveAs: function(e) {
				this.load.remembered || (this.load.remembered = {}, this.load.remembered[se] = x(this, !0)),
					this.load.remembered[e] = x(this), this.preset = e, g(this, e, !0), this
					.saveToLocalStorageIfPossible()
			},
			revert: function(e) {
				S.each(this.__controllers, function(t) {
					this.getRoot().load.remembered ? p(e || this.getRoot(), t) : t.setValue(t
						.initialValue), t.__onFinishChange && t.__onFinishChange.call(t, t
						.getValue())
				}, this), S.each(this.__folders, function(e) {
					e.revert(e)
				}), e || _(this.getRoot(), !1)
			},
			listen: function(e) {
				var t = 0 === this.__listening.length;
				this.__listening.push(e), t && C(this.__listening)
			},
			updateDisplay: function() {
				S.each(this.__controllers, function(e) {
					e.updateDisplay()
				}), S.each(this.__folders, function(e) {
					e.updateDisplay()
				})
			}
		});
	var pe = {
			Color: I,
			math: N,
			interpret: R
		},
		fe = {
			Controller: z,
			BooleanController: K,
			OptionController: Y,
			StringController: J,
			NumberController: W,
			NumberControllerBox: Q,
			NumberControllerSlider: q,
			FunctionController: Z,
			ColorController: $
		},
		me = {
			dom: X
		},
		ge = {
			GUI: he
		},
		be = he,
		ve = {
			color: pe,
			controllers: fe,
			dom: me,
			gui: ge,
			GUI: be
		};
	e.color = pe, e.controllers = fe, e.dom = me, e.gui = ge, e.GUI = be, e.default = ve, Object.defineProperty(e,
		"__esModule", {
			value: !0
		})
});

八、style.css

body {
    box-sizing: border-box;
    background:black; margin: 0px;
    color: white;
    overflow: hidden;
    font-family: 'Roboto Mono', monospace;
    user-select: none;
}

#demo {
  width: 100%; height:100vh;
}

#c {
    width: 100%; height:100%;
    background:black;
    touch-action: none;
}

  • 40
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值