用HTML实现拓扑面,动态4D圆环面,可手动调节,富有创新性的案例。(有源代码)
文章目录
- 前言
- 一、示例
- 二、目录结构
- 三、index.html(主页面)
- 四、main.js
- 五、Tour4D.js
- 六、swissgl.js
- 七、dat.gui.min.js
- 八、style.css
前言
如果你觉得对代码进行复制粘贴很麻烦的话,你可以直接将资源下载到本地。无需部署,直接可以运行。
一、示例

二、目录结构

三、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 runningthis.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 imagesgenPreviews() {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 compatibilityconst 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_Positionlayout(location = 0) in int VertexID;layout(location = 1) in int InstanceID;ivec2 VID;ivec3 ID;
#else#define varying inlayout(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;
#endifuniform 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_InstanceIDGLSL 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 FRAGfloat 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 activeconst {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_ELEMENTgl.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_readbackread(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 deletedgl.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 arraysif (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 targetif (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) targetconst 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 programif (!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 optionsif (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, Aspectgl.viewport(...view)const width = view[2],height = view[3];uniforms.View = view;uniforms.Aspect = calcAspect(options.Aspect, width, height);// Grid, Meshconst [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 texturesObject.entries(prog.setters).forEach(([name, f]) => f(uniforms[name]));// drawgl.drawArraysInstanced(gl.TRIANGLE_STRIP, 0, vertN, instN);// revert gl stateif (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 = " ", 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} elsefor (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;
}相关文章:
用HTML实现拓扑面,动态4D圆环面,可手动调节,富有创新性的案例。(有源代码)
文章目录 前言一、示例二、目录结构三、index.html(主页面)四、main.js五、Tour4D.js六、swissgl.js七、dat.gui.min.js八、style.css 前言 如果你觉得对代码进行复制粘贴很麻烦的话,你可以直接将资源下载到本地。无需部署,直接可…...
java调用GDAL及JTS实现生成泰森多边形(Voronoi图)的一种方法
目录 一、关于泰森多边形 1.泰森多边形的特性 2.本文的目的 二、实现思路 1.gdal和jts库的maven坐标 2.jts生成泰森多边形的关键代码 3.使用GDAL读取源文件信息的关键代码 4.使用GDAL将生成的泰森多边形写入文件 三、实现结果 1.实现的效果 2.完整代码示例 一、关于…...
Type-C音频转接器方案
在数字化时代,音频设备作为我们生活中不可或缺的一部分,其连接方式的便捷性和高效性显得尤为重要。Type-C音频转接器,作为一种新型的音频连接解决方案,正逐渐走进我们的生活,以其独特的优势改变着我们的音频体验。 一、…...
linux 服务器上离线安装 node nvm
因为是离线环境 如果你是可以访问外网的 下面内容仅供参考 也可以继续按步骤来 node 安装路径 Node.js — Download Node.js nvm 安装路径 Tags nvm-sh/nvm GitHub 后来发现 nvm安装后 nvm use 版本号 报错 让我去nvm install 版本 我是内网环境 install不了 下面 你要 把安…...
Web前端三大主流框架:React、Angular和Vue的比较与选择
Web前端三大主流框架:React、Angular和Vue的比较与选择 Web前端技术的快速发展为开发者提供了丰富的工具和框架,其中React、Angular和Vue是当前最受欢迎的三大框架。这三个框架各有特点,适用于不同的项目需求和开发团队。本文将对React、Ang…...
C# MemoryCache 缓存应用
摘要 缓存是一种非常常见的性能优化技术,在开发过程中经常会用到。.NET提供了内置的内存缓存类 MemoryCache,它可以很方便地存储数据并在后续的请求中快速读取,从而提高应用程序的响应速度。 正文 通过使用 Microsoft.Extensions.Caching.Me…...
【学习笔记】Linux前置准备
视频学习资料 基础: 黑马0基础(前面四章即可,包含软件基础安装配置) 进阶: 黑马程序员-Linux系统编程 黑马程序员-Linux网络编程 我也还没看,看了眼目录感觉把八股里面很多场景都讲到了,感觉有…...
各种空气能热泵安装图
空气能热泵安装图 循环式空气能热泵安装图 直热循环式空气能热泵安装图 泳池空气能热泵安装图 循环式水源热泵热安装系统原理图 直热循环式水源热泵安装系统图 空气水源热泵安装图...
软件杯 题目:基于深度学习的中文对话问答机器人
文章目录 0 简介1 项目架构2 项目的主要过程2.1 数据清洗、预处理2.2 分桶2.3 训练 3 项目的整体结构4 重要的API4.1 LSTM cells部分:4.2 损失函数:4.3 搭建seq2seq框架:4.4 测试部分:4.5 评价NLP测试效果:4.6 梯度截断…...
UI学习笔记(一)
UI学习 一:UIView基础frame属性隐藏视图对象:UIView的层级关系 二:UIWindow对象三:UIViewController基础UIViewController使用 四:定时器与视图移动五:UISwitch控件六:滑动条和进度条七…...
【C语言训练题库】扫雷->简单小游戏!
🔥博客主页🔥:【 坊钰_CSDN博客 】 欢迎各位点赞👍评论✍收藏⭐ 目录 1. 题目 2. 解析 3. 代码 4. 小结 1. 题目 小sun上课的时候非常喜欢玩扫雷。他现小sun有一个初始的雷矩阵,他希望你帮他生成一个扫雷矩阵。 扫雷…...
WMS仓储管理系统高效驱动制造企业物料管理
在现代制造业的快速发展中,仓储管理作为供应链的核心环节,其效率直接影响到企业的生产力和市场竞争力。随着科技的进步,实施WMS仓储管理系统逐渐成为推动仓储管理向智能化转型的关键力量。本文将深入探讨WMS仓储管理系统如何以创新的方式驱动…...
python使用appium打开程序后,为什么没有操作后程序就自动退出了
当使用Appium打开应用程序并在没有执行任何操作后它自动退出,这可能是由于几个不同的原因。以下是一些可能的原因和相应的解决方案: 应用程序的默认行为: 有些应用程序在启动后如果没有用户交互,可能会因为超时或其他逻辑而自动关…...
MacBook M系列芯片安装php8.2
适用于M1\M2\M3等系列的MacBook,记录下安装过程 安装brew 打开终端,执行如下命令: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"安装zsh(非必须) …...
OlSoul系统调校程序v2024.06.05
软件介绍 OlSoul是一款能够适配用于Win各个系统的系统调校软件,OlSoul内置有众多调校功能可以直接使用,如有启用无线网络功能、启用打印机功能、系统快速休眠与休眠开关、快捷方式小箭头去除功能等,具体的调校功能多达几十项,可自…...
图像特征提取 python
1. 边缘检测 (Edge Detection) 1.1 Sobel 算子 Sobel 算子是一种边缘检测算子,通过计算图像梯度来检测边缘。 import cv2 import numpy as np# 读取图像 image cv2.imread(image.jpg, 0)# 应用 Sobel 算子 sobel_x cv2.Sobel(image, cv2.CV_64F, 1, 0, ksize5)…...
width: 100%和 width: 100vw这两种写法有什么区别
width: 100%; 和 width: 100vw; 是两种不同的 CSS 写法,它们在实际应用中会有不同的效果。以下是这两种写法的主要区别: width: 100%; 定义:将元素的宽度设置为其包含块(通常是父元素)宽度的 100%。效果:元…...
如何在另一台电脑上使用相同的Python环境和依赖包
如果您想在另一台电脑上使用相同的Python环境和依赖包,有几种方法可以实现: 使用requirements.txt: 在您当前的虚拟环境中,您可以使用pip freeze > requirements.txt命令生成一个包含所有已安装包及其版本的文件。然后&#x…...
Vue3 响应式 API:工具函数(一)
isRef() isRef 是一个简单的工具函数,它接受一个参数并返回一个布尔值,指示该参数是否是一个由 ref 创建的响应式引用。 在某些情况下,你可能需要编写一些通用逻辑或函数,这些逻辑或函数需要处理不同类型的响应式数据(…...
开发常用软件
开发相关 代码编译 Visual Studio 2019 Visual Studio 2022 代码测试工具 LINQPad Premium 5 LINQPad 7 打包工具 Advanced Installer 反编译工具 ILSpy dnSpy spy 数据库相关 SQLite Expert Professional 5 DLL扫描工具 depends 界面设计 SvgToXaml Materi…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
SpringAI实战:ChatModel智能对话全解
一、引言:Spring AI 与 Chat Model 的核心价值 🚀 在 Java 生态中集成大模型能力,Spring AI 提供了高效的解决方案 🤖。其中 Chat Model 作为核心交互组件,通过标准化接口简化了与大语言模型(LLM࿰…...
深入理解 React 样式方案
React 的样式方案较多,在应用开发初期,开发者需要根据项目业务具体情况选择对应样式方案。React 样式方案主要有: 1. 内联样式 2. module css 3. css in js 4. tailwind css 这些方案中,均有各自的优势和缺点。 1. 方案优劣势 1. 内联样式: 简单直观,适合动态样式和…...
高保真组件库:开关
一:制作关状态 拖入一个矩形作为关闭的底色:44 x 22,填充灰色CCCCCC,圆角23,边框宽度0,文本为”关“,右对齐,边距2,2,6,2,文本颜色白色FFFFFF。 拖拽一个椭圆,尺寸18 x 18,边框为0。3. 全选转为动态面板状态1命名为”关“。 二:制作开状态 复制关状态并命名为”开…...
